From 56ac4d8ab15199d9e69256f0c95e87523481ca70 Mon Sep 17 00:00:00 2001 From: rahulramesha Date: Wed, 13 Dec 2023 18:51:09 +0530 Subject: [PATCH] code refactor kanban for better mainatinability --- .../issue-layouts/kanban/base-kanban-root.tsx | 84 +--- .../issues/issue-layouts/kanban/block.tsx | 29 +- .../issue-layouts/kanban/blocks-list.tsx | 17 +- .../issues/issue-layouts/kanban/default.tsx | 323 ++---------- .../issue-layouts/kanban/headers/assignee.tsx | 74 --- .../kanban/headers/created_by.tsx | 71 --- .../kanban/headers/group-by-root.tsx | 149 ------ .../issue-layouts/kanban/headers/label.tsx | 74 --- .../issue-layouts/kanban/headers/priority.tsx | 73 --- .../issue-layouts/kanban/headers/project.tsx | 74 --- .../kanban/headers/state-group.tsx | 77 --- .../issue-layouts/kanban/headers/state.tsx | 71 --- .../kanban/headers/sub-group-by-root.tsx | 134 ----- .../issue-layouts/kanban/properties.tsx | 48 +- .../issues/issue-layouts/kanban/swimlanes.tsx | 472 ++---------------- .../issues/issue-layouts/kanban/utils.tsx | 162 ++++++ 16 files changed, 301 insertions(+), 1631 deletions(-) delete mode 100644 web/components/issues/issue-layouts/kanban/headers/assignee.tsx delete mode 100644 web/components/issues/issue-layouts/kanban/headers/created_by.tsx delete mode 100644 web/components/issues/issue-layouts/kanban/headers/group-by-root.tsx delete mode 100644 web/components/issues/issue-layouts/kanban/headers/label.tsx delete mode 100644 web/components/issues/issue-layouts/kanban/headers/priority.tsx delete mode 100644 web/components/issues/issue-layouts/kanban/headers/project.tsx delete mode 100644 web/components/issues/issue-layouts/kanban/headers/state-group.tsx delete mode 100644 web/components/issues/issue-layouts/kanban/headers/state.tsx delete mode 100644 web/components/issues/issue-layouts/kanban/headers/sub-group-by-root.tsx create mode 100644 web/components/issues/issue-layouts/kanban/utils.tsx diff --git a/web/components/issues/issue-layouts/kanban/base-kanban-root.tsx b/web/components/issues/issue-layouts/kanban/base-kanban-root.tsx index e3f032bb1..43b815615 100644 --- a/web/components/issues/issue-layouts/kanban/base-kanban-root.tsx +++ b/web/components/issues/issue-layouts/kanban/base-kanban-root.tsx @@ -26,8 +26,6 @@ import { IQuickActionProps } from "../list/list-view-types"; import { IIssueKanBanViewStore } from "store_legacy/issue"; // hooks import useToast from "hooks/use-toast"; -// constants -import { ISSUE_STATE_GROUPS, ISSUE_PRIORITIES } from "constants/issue"; //components import { KanBan } from "./default"; import { KanBanSwimLanes } from "./swimlanes"; @@ -95,13 +93,7 @@ export const BaseKanBanRoot: React.FC = observer((props: IBas const router = useRouter(); const { workspaceSlug, peekIssueId, peekProjectId } = router.query; // mobx store - const { - project: { workspaceProjects }, - projectLabel: { projectLabels }, - projectMember: { projectMembers }, - projectState: projectStateStore, - user: userStore, - } = useMobxStore(); + const { user: userStore } = useMobxStore(); // hooks const { setToastAlert } = useToast(); @@ -185,7 +177,7 @@ export const BaseKanBanRoot: React.FC = observer((props: IBas }; const handleIssues = useCallback( - async (sub_group_by: string | null, group_by: string | null, issue: IIssue, action: EIssueActions) => { + async (issue: IIssue, action: EIssueActions) => { if (issueActions[action]) { await issueActions[action]!(issue); } @@ -193,6 +185,20 @@ export const BaseKanBanRoot: React.FC = observer((props: IBas [issueActions] ); + const renderQuickActions = (issue: IIssue, customActionButton?: React.ReactElement) => ( + handleIssues(issue, EIssueActions.DELETE)} + handleUpdate={ + issueActions[EIssueActions.UPDATE] ? async (data) => handleIssues(data, EIssueActions.UPDATE) : undefined + } + handleRemoveFromView={ + issueActions[EIssueActions.REMOVE] ? async () => handleIssues(issue, EIssueActions.REMOVE) : undefined + } + /> + ); + const handleDeleteIssue = async () => { if (!handleDragDrop) return; await handleDragDrop(dragState.source, dragState.destination, sub_group_by, group_by, issues, issueIds).finally( @@ -207,10 +213,6 @@ export const BaseKanBanRoot: React.FC = observer((props: IBas kanbanViewStore.handleKanBanToggle(toggle, value); }; - const states = projectStateStore?.projectStates || null; - const priorities = ISSUE_PRIORITIES || null; - const stateGroups = ISSUE_STATE_GROUPS || null; - return ( <> = observer((props: IBas issueIds={issueIds} sub_group_by={sub_group_by} group_by={group_by} - order_by={order_by} handleIssues={handleIssues} - quickActions={(sub_group_by, group_by, issue, customActionButton) => ( - handleIssues(sub_group_by, group_by, issue, EIssueActions.DELETE)} - handleUpdate={ - issueActions[EIssueActions.UPDATE] - ? async (data) => handleIssues(sub_group_by, group_by, data, EIssueActions.UPDATE) - : undefined - } - handleRemoveFromView={ - issueActions[EIssueActions.REMOVE] - ? async () => handleIssues(sub_group_by, group_by, issue, EIssueActions.REMOVE) - : undefined - } - /> - )} + quickActions={renderQuickActions} displayProperties={displayProperties} kanBanToggle={kanbanViewStore?.kanBanToggle} handleKanBanToggle={handleKanBanToggle} - states={states} - stateGroups={stateGroups} - priorities={priorities} - labels={projectLabels} - members={projectMembers?.map((m) => m.member) ?? null} - projects={workspaceProjects} enableQuickIssueCreate={enableQuickAdd} showEmptyGroup={userDisplayFilters?.show_empty_groups || true} - isDragStarted={isDragStarted} quickAddCallback={issueStore?.quickAddIssue} viewId={viewId} disableIssueCreation={!enableIssueCreation || !isEditingAllowed} @@ -302,32 +280,10 @@ export const BaseKanBanRoot: React.FC = observer((props: IBas group_by={group_by} order_by={order_by} handleIssues={handleIssues} - quickActions={(sub_group_by, group_by, issue, customActionButton) => ( - handleIssues(sub_group_by, group_by, issue, EIssueActions.DELETE)} - handleUpdate={ - issueActions[EIssueActions.UPDATE] - ? async (data) => handleIssues(sub_group_by, group_by, data, EIssueActions.UPDATE) - : undefined - } - handleRemoveFromView={ - issueActions[EIssueActions.REMOVE] - ? async () => handleIssues(sub_group_by, group_by, issue, EIssueActions.REMOVE) - : undefined - } - /> - )} + quickActions={renderQuickActions} displayProperties={displayProperties} kanBanToggle={kanbanViewStore?.kanBanToggle} handleKanBanToggle={handleKanBanToggle} - states={states} - stateGroups={stateGroups} - priorities={priorities} - labels={projectLabels} - members={projectMembers?.map((m) => m.member) ?? null} - projects={workspaceProjects} showEmptyGroup={userDisplayFilters?.show_empty_groups || true} isDragStarted={isDragStarted} disableIssueCreation={!enableIssueCreation || !isEditingAllowed} @@ -346,9 +302,7 @@ export const BaseKanBanRoot: React.FC = observer((props: IBas workspaceSlug={workspaceSlug.toString()} projectId={peekProjectId.toString()} issueId={peekIssueId.toString()} - handleIssue={async (issueToUpdate) => - await handleIssues(sub_group_by, group_by, issueToUpdate as IIssue, EIssueActions.UPDATE) - } + handleIssue={async (issueToUpdate) => await handleIssues(issueToUpdate as IIssue, EIssueActions.UPDATE)} /> )} diff --git a/web/components/issues/issue-layouts/kanban/block.tsx b/web/components/issues/issue-layouts/kanban/block.tsx index 9f50b4793..712327039 100644 --- a/web/components/issues/issue-layouts/kanban/block.tsx +++ b/web/components/issues/issue-layouts/kanban/block.tsx @@ -17,31 +17,28 @@ interface IssueBlockProps { issue: IIssue; isDragDisabled: boolean; showEmptyGroup: boolean; - handleIssues: (sub_group_by: string | null, group_by: string | null, issue: IIssue, action: EIssueActions) => void; - quickActions: (sub_group_by: string | null, group_by: string | null, issue: IIssue) => React.ReactNode; + handleIssues: (issue: IIssue, action: EIssueActions) => void; + quickActions: (issue: IIssue) => React.ReactNode; displayProperties: IIssueDisplayProperties | null; canEditProperties: (projectId: string | undefined) => boolean; } interface IssueDetailsBlockProps { - sub_group_id: string; - columnId: string; issue: IIssue; showEmptyGroup: boolean; - handleIssues: (sub_group_by: string | null, group_by: string | null, issue: IIssue, action: EIssueActions) => void; - quickActions: (sub_group_by: string | null, group_by: string | null, issue: IIssue) => React.ReactNode; + handleIssues: (issue: IIssue, action: EIssueActions) => void; + quickActions: (issue: IIssue) => React.ReactNode; displayProperties: IIssueDisplayProperties | null; isReadOnly: boolean; } const KanbanIssueDetailsBlock: React.FC = (props) => { - const { sub_group_id, columnId, issue, showEmptyGroup, handleIssues, quickActions, displayProperties, isReadOnly } = - props; + const { issue, showEmptyGroup, handleIssues, quickActions, displayProperties, isReadOnly } = props; const router = useRouter(); - const updateIssue = (sub_group_by: string | null, group_by: string | null, issueToUpdate: IIssue) => { - if (issueToUpdate) handleIssues(sub_group_by, group_by, issueToUpdate, EIssueActions.UPDATE); + const updateIssue = (issueToUpdate: IIssue) => { + if (issueToUpdate) handleIssues(issueToUpdate, EIssueActions.UPDATE); }; const handleIssuePeekOverview = () => { @@ -60,13 +57,7 @@ const KanbanIssueDetailsBlock: React.FC = (props) => {
{issue.project_detail.identifier}-{issue.sequence_id}
-
- {quickActions( - !sub_group_id && sub_group_id === "null" ? null : sub_group_id, - !columnId && columnId === "null" ? null : columnId, - issue - )} -
+
{quickActions(issue)}
)} @@ -76,8 +67,6 @@ const KanbanIssueDetailsBlock: React.FC = (props) => {
= (props) => { } ${snapshot.isDragging ? `border-custom-primary-100` : `border-transparent`}`} > void; - quickActions: ( - sub_group_by: string | null, - group_by: string | null, - issue: IIssue, - customActionButton?: React.ReactElement - ) => React.ReactNode; + handleIssues: (issue: IIssue, action: EIssueActions) => void; + quickActions: (issue: IIssue, customActionButton?: React.ReactElement) => React.ReactNode; displayProperties: IIssueDisplayProperties | null; canEditProperties: (projectId: string | undefined) => boolean; } @@ -62,13 +57,7 @@ export const KanbanIssueBlocksList: React.FC = (props) => ); })} - ) : ( - !isDragDisabled && ( -
- {/*
Drop here
*/} -
- ) - )} + ) : null} ); }; diff --git a/web/components/issues/issue-layouts/kanban/default.tsx b/web/components/issues/issue-layouts/kanban/default.tsx index 30b57b84f..7bb7b426b 100644 --- a/web/components/issues/issue-layouts/kanban/default.tsx +++ b/web/components/issues/issue-layouts/kanban/default.tsx @@ -4,12 +4,12 @@ import { Droppable } from "@hello-pangea/dnd"; // mobx store import { useMobxStore } from "lib/mobx/store-provider"; // components -import { KanBanGroupByHeaderRoot } from "./headers/group-by-root"; import { KanbanIssueBlocksList, KanBanQuickAddIssueForm } from "components/issues"; +import { HeaderGroupByCard } from "./headers/group-by-card"; // types -import { IIssueDisplayProperties, IIssue, IState } from "types"; +import { IIssueDisplayProperties, IIssue } from "types"; // constants -import { getValueFromObject } from "constants/issue"; +import { columnTypes, getKanbanColumns, IKanbanColumn } from "./utils"; import { EIssueActions } from "../types"; import { IIssueResponse, IGroupedIssues, ISubGroupedIssues, TUnGroupedIssues } from "store_legacy/issues/types"; import { EProjectStore } from "store_legacy/command-palette.store"; @@ -19,25 +19,15 @@ export interface IGroupByKanBan { issueIds: any; sub_group_by: string | null; group_by: string | null; - order_by: string | null; sub_group_id: string; - list: any; - listKey: string; - states: IState[] | null; isDragDisabled: boolean; - handleIssues: (sub_group_by: string | null, group_by: string | null, issue: IIssue, action: EIssueActions) => void; + handleIssues: (issue: IIssue, action: EIssueActions) => void; showEmptyGroup: boolean; - quickActions: ( - sub_group_by: string | null, - group_by: string | null, - issue: IIssue, - customActionButton?: React.ReactElement - ) => React.ReactNode; + quickActions: (issue: IIssue, customActionButton?: React.ReactElement) => React.ReactNode; displayProperties: IIssueDisplayProperties | null; kanBanToggle: any; handleKanBanToggle: any; enableQuickIssueCreate?: boolean; - isDragStarted?: boolean; quickAddCallback?: ( workspaceSlug: string, projectId: string, @@ -57,11 +47,7 @@ const GroupByKanBan: React.FC = observer((props) => { issueIds, sub_group_by, group_by, - // eslint-disable-next-line @typescript-eslint/no-unused-vars - order_by, sub_group_id = "null", - list, - listKey, isDragDisabled, handleIssues, showEmptyGroup, @@ -70,8 +56,6 @@ const GroupByKanBan: React.FC = observer((props) => { kanBanToggle, handleKanBanToggle, enableQuickIssueCreate, - // eslint-disable-next-line @typescript-eslint/no-unused-vars - isDragStarted, quickAddCallback, viewId, disableIssueCreation, @@ -80,27 +64,34 @@ const GroupByKanBan: React.FC = observer((props) => { canEditProperties, } = props; - const verticalAlignPosition = (_list: any) => - kanBanToggle?.groupByHeaderMinMax.includes(getValueFromObject(_list, listKey) as string); + const { project, projectLabel, projectMember, projectState } = useMobxStore(); + + const list = getKanbanColumns(group_by as columnTypes, project, projectLabel, projectMember, projectState); + + if (!list) return null; + + const verticalAlignPosition = (_list: IKanbanColumn) => kanBanToggle?.groupByHeaderMinMax.includes(_list.id); return (
{list && list.length > 0 && - list.map((_list: any) => ( + list.map((_list: IKanbanColumn) => (
{sub_group_by === null && (
- = observer((props) => { verticalAlignPosition(_list) ? `min-h-[150px] w-[0px] overflow-hidden` : `w-full transition-all` }`} > - + {(provided: any, snapshot: any) => (
= observer((props) => { {issues && !verticalAlignPosition(_list) ? ( = observer((props) => { displayProperties={displayProperties} canEditProperties={canEditProperties} /> - ) : ( - isDragDisabled && ( -
- {/*
Drop here
*/} -
- ) - )} + ) : null} {provided.placeholder}
@@ -152,10 +137,10 @@ const GroupByKanBan: React.FC = observer((props) => { {enableQuickIssueCreate && !disableIssueCreation && ( = observer((props) => { )}
- - {/* {isDragStarted && isDragDisabled && ( -
-
- {`This board is ordered by "${replaceUnderscoreIfSnakeCase( - order_by ? (order_by[0] === "-" ? order_by.slice(1) : order_by) : "created_at" - )}"`} -
-
- )} */}
))}
@@ -185,27 +160,14 @@ export interface IKanBan { issueIds: IGroupedIssues | ISubGroupedIssues | TUnGroupedIssues; sub_group_by: string | null; group_by: string | null; - order_by: string | null; sub_group_id?: string; - handleIssues: (sub_group_by: string | null, group_by: string | null, issue: IIssue, action: EIssueActions) => void; - quickActions: ( - sub_group_by: string | null, - group_by: string | null, - issue: IIssue, - customActionButton?: React.ReactElement - ) => React.ReactNode; + handleIssues: (issue: IIssue, action: EIssueActions) => void; + quickActions: (issue: IIssue, customActionButton?: React.ReactElement) => React.ReactNode; displayProperties: IIssueDisplayProperties | null; kanBanToggle: any; handleKanBanToggle: any; showEmptyGroup: boolean; - states: any; - stateGroups: any; - priorities: any; - labels: any; - members: any; - projects: any; enableQuickIssueCreate?: boolean; - isDragStarted?: boolean; quickAddCallback?: ( workspaceSlug: string, projectId: string, @@ -225,7 +187,6 @@ export const KanBan: React.FC = observer((props) => { issueIds, sub_group_by, group_by, - order_by, sub_group_id = "null", handleIssues, quickActions, @@ -233,14 +194,7 @@ export const KanBan: React.FC = observer((props) => { kanBanToggle, handleKanBanToggle, showEmptyGroup, - states, - stateGroups, - priorities, - labels, - members, - projects, enableQuickIssueCreate, - isDragStarted, quickAddCallback, viewId, disableIssueCreation, @@ -253,208 +207,27 @@ export const KanBan: React.FC = observer((props) => { return (
- {group_by && group_by === "project" && ( - - )} - - {group_by && group_by === "state" && ( - - )} - - {group_by && group_by === "state_detail.group" && ( - - )} - - {group_by && group_by === "priority" && ( - - )} - - {group_by && group_by === "labels" && ( - - )} - - {group_by && group_by === "assignees" && ( - - )} - - {group_by && group_by === "created_by" && ( - - )} +
); }); diff --git a/web/components/issues/issue-layouts/kanban/headers/assignee.tsx b/web/components/issues/issue-layouts/kanban/headers/assignee.tsx deleted file mode 100644 index 8842ab7df..000000000 --- a/web/components/issues/issue-layouts/kanban/headers/assignee.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import { FC } from "react"; -import { observer } from "mobx-react-lite"; -// components -import { HeaderGroupByCard } from "./group-by-card"; -import { HeaderSubGroupByCard } from "./sub-group-by-card"; -// ui -import { Avatar } from "@plane/ui"; -import { EProjectStore } from "store_legacy/command-palette.store"; -import { IIssue } from "types"; - -export interface IAssigneesHeader { - column_id: string; - column_value: any; - sub_group_by: string | null; - group_by: string | null; - header_type: "group_by" | "sub_group_by"; - issues_count: number; - kanBanToggle: any; - handleKanBanToggle: any; - disableIssueCreation?: boolean; - currentStore?: EProjectStore; - addIssuesToView?: (issueIds: string[]) => Promise; -} - -export const Icon = ({ user }: any) => ; - -export const AssigneesHeader: FC = observer((props) => { - const { - column_id, - column_value, - sub_group_by, - group_by, - header_type, - issues_count, - kanBanToggle, - handleKanBanToggle, - disableIssueCreation, - currentStore, - addIssuesToView, - } = props; - - const assignee = column_value ?? null; - - return ( - <> - {assignee && - (sub_group_by && header_type === "sub_group_by" ? ( - } - title={assignee?.display_name || ""} - count={issues_count} - kanBanToggle={kanBanToggle} - handleKanBanToggle={handleKanBanToggle} - /> - ) : ( - } - title={assignee?.display_name || ""} - count={issues_count} - kanBanToggle={kanBanToggle} - handleKanBanToggle={handleKanBanToggle} - issuePayload={{ assignees: [assignee?.id] }} - disableIssueCreation={disableIssueCreation} - currentStore={currentStore} - addIssuesToView={addIssuesToView} - /> - ))} - - ); -}); diff --git a/web/components/issues/issue-layouts/kanban/headers/created_by.tsx b/web/components/issues/issue-layouts/kanban/headers/created_by.tsx deleted file mode 100644 index c5d944917..000000000 --- a/web/components/issues/issue-layouts/kanban/headers/created_by.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import { FC } from "react"; -import { observer } from "mobx-react-lite"; -// components -import { HeaderGroupByCard } from "./group-by-card"; -import { HeaderSubGroupByCard } from "./sub-group-by-card"; -import { Icon } from "./assignee"; -import { EProjectStore } from "store_legacy/command-palette.store"; -import { IIssue } from "types"; - -export interface ICreatedByHeader { - column_id: string; - column_value: any; - sub_group_by: string | null; - group_by: string | null; - header_type: "group_by" | "sub_group_by"; - issues_count: number; - kanBanToggle: any; - handleKanBanToggle: any; - disableIssueCreation?: boolean; - currentStore?: EProjectStore; - addIssuesToView?: (issueIds: string[]) => Promise; -} - -export const CreatedByHeader: FC = observer((props) => { - const { - column_id, - column_value, - sub_group_by, - group_by, - header_type, - issues_count, - kanBanToggle, - handleKanBanToggle, - disableIssueCreation, - currentStore, - addIssuesToView, - } = props; - - const createdBy = column_value ?? null; - - return ( - <> - {createdBy && - (sub_group_by && header_type === "sub_group_by" ? ( - } - title={createdBy?.display_name || ""} - count={issues_count} - kanBanToggle={kanBanToggle} - handleKanBanToggle={handleKanBanToggle} - /> - ) : ( - } - title={createdBy?.display_name || ""} - count={issues_count} - kanBanToggle={kanBanToggle} - handleKanBanToggle={handleKanBanToggle} - issuePayload={{ created_by: createdBy?.id }} - disableIssueCreation={disableIssueCreation} - currentStore={currentStore} - addIssuesToView={addIssuesToView} - /> - ))} - - ); -}); diff --git a/web/components/issues/issue-layouts/kanban/headers/group-by-root.tsx b/web/components/issues/issue-layouts/kanban/headers/group-by-root.tsx deleted file mode 100644 index 5b20ed2e6..000000000 --- a/web/components/issues/issue-layouts/kanban/headers/group-by-root.tsx +++ /dev/null @@ -1,149 +0,0 @@ -// components -import { ProjectHeader } from "./project"; -import { StateHeader } from "./state"; -import { StateGroupHeader } from "./state-group"; -import { AssigneesHeader } from "./assignee"; -import { PriorityHeader } from "./priority"; -import { LabelHeader } from "./label"; -import { CreatedByHeader } from "./created_by"; -// mobx -import { observer } from "mobx-react-lite"; -import { EProjectStore } from "store_legacy/command-palette.store"; -import { IIssue } from "types"; - -export interface IKanBanGroupByHeaderRoot { - column_id: string; - column_value: any; - sub_group_by: string | null; - group_by: string | null; - issues_count: number; - kanBanToggle: any; - handleKanBanToggle: any; - disableIssueCreation?: boolean; - currentStore?: EProjectStore; - addIssuesToView?: (issueIds: string[]) => Promise; -} - -export const KanBanGroupByHeaderRoot: React.FC = observer( - ({ - column_id, - column_value, - sub_group_by, - group_by, - issues_count, - kanBanToggle, - disableIssueCreation, - handleKanBanToggle, - currentStore, - addIssuesToView, - }) => ( - <> - {group_by && group_by === "project" && ( - - )} - - {group_by && group_by === "state" && ( - - )} - {group_by && group_by === "state_detail.group" && ( - - )} - {group_by && group_by === "priority" && ( - - )} - {group_by && group_by === "labels" && ( - - )} - {group_by && group_by === "assignees" && ( - - )} - {group_by && group_by === "created_by" && ( - - )} - - ) -); diff --git a/web/components/issues/issue-layouts/kanban/headers/label.tsx b/web/components/issues/issue-layouts/kanban/headers/label.tsx deleted file mode 100644 index 95a0ecbff..000000000 --- a/web/components/issues/issue-layouts/kanban/headers/label.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import { FC } from "react"; -import { observer } from "mobx-react-lite"; -// components -import { HeaderGroupByCard } from "./group-by-card"; -import { HeaderSubGroupByCard } from "./sub-group-by-card"; -import { EProjectStore } from "store_legacy/command-palette.store"; -import { IIssue } from "types"; - -export interface ILabelHeader { - column_id: string; - column_value: any; - sub_group_by: string | null; - group_by: string | null; - header_type: "group_by" | "sub_group_by"; - issues_count: number; - kanBanToggle: any; - handleKanBanToggle: any; - disableIssueCreation?: boolean; - currentStore?: EProjectStore; - addIssuesToView?: (issueIds: string[]) => Promise; -} - -const Icon = ({ color }: any) => ( -
-); - -export const LabelHeader: FC = observer((props) => { - const { - column_id, - column_value, - sub_group_by, - group_by, - header_type, - issues_count, - kanBanToggle, - handleKanBanToggle, - disableIssueCreation, - currentStore, - addIssuesToView, - } = props; - - const label = column_value ?? null; - - return ( - <> - {label && - (sub_group_by && header_type === "sub_group_by" ? ( - } - title={label?.name || ""} - count={issues_count} - kanBanToggle={kanBanToggle} - handleKanBanToggle={handleKanBanToggle} - /> - ) : ( - } - title={label?.name || ""} - count={issues_count} - kanBanToggle={kanBanToggle} - handleKanBanToggle={handleKanBanToggle} - issuePayload={{ labels: [label?.id] }} - disableIssueCreation={disableIssueCreation} - currentStore={currentStore} - addIssuesToView={addIssuesToView} - /> - ))} - - ); -}); diff --git a/web/components/issues/issue-layouts/kanban/headers/priority.tsx b/web/components/issues/issue-layouts/kanban/headers/priority.tsx deleted file mode 100644 index 1a279761d..000000000 --- a/web/components/issues/issue-layouts/kanban/headers/priority.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import { FC } from "react"; -import { observer } from "mobx-react-lite"; -// components -import { HeaderGroupByCard } from "./group-by-card"; -import { HeaderSubGroupByCard } from "./sub-group-by-card"; - -// Icons -import { PriorityIcon } from "@plane/ui"; -import { EProjectStore } from "store_legacy/command-palette.store"; -import { IIssue } from "types"; - -export interface IPriorityHeader { - column_id: string; - column_value: any; - sub_group_by: string | null; - group_by: string | null; - header_type: "group_by" | "sub_group_by"; - issues_count: number; - kanBanToggle: any; - handleKanBanToggle: any; - disableIssueCreation?: boolean; - currentStore?: EProjectStore; - addIssuesToView?: (issueIds: string[]) => Promise; -} - -export const PriorityHeader: FC = observer((props) => { - const { - column_id, - column_value, - sub_group_by, - group_by, - header_type, - issues_count, - kanBanToggle, - handleKanBanToggle, - disableIssueCreation, - currentStore, - addIssuesToView, - } = props; - - const priority = column_value || null; - - return ( - <> - {priority && - (sub_group_by && header_type === "sub_group_by" ? ( - } - title={priority?.title || ""} - count={issues_count} - kanBanToggle={kanBanToggle} - handleKanBanToggle={handleKanBanToggle} - /> - ) : ( - } - title={priority?.title || ""} - count={issues_count} - kanBanToggle={kanBanToggle} - handleKanBanToggle={handleKanBanToggle} - issuePayload={{ priority: priority?.key }} - disableIssueCreation={disableIssueCreation} - currentStore={currentStore} - addIssuesToView={addIssuesToView} - /> - ))} - - ); -}); diff --git a/web/components/issues/issue-layouts/kanban/headers/project.tsx b/web/components/issues/issue-layouts/kanban/headers/project.tsx deleted file mode 100644 index 6498e8649..000000000 --- a/web/components/issues/issue-layouts/kanban/headers/project.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import { FC } from "react"; -import { observer } from "mobx-react-lite"; -// components -import { HeaderGroupByCard } from "./group-by-card"; -import { HeaderSubGroupByCard } from "./sub-group-by-card"; -// emoji helper -import { renderEmoji } from "helpers/emoji.helper"; -import { EProjectStore } from "store_legacy/command-palette.store"; -import { IIssue } from "types"; - -export interface IProjectHeader { - column_id: string; - column_value: any; - sub_group_by: string | null; - group_by: string | null; - header_type: "group_by" | "sub_group_by"; - issues_count: number; - kanBanToggle: any; - handleKanBanToggle: any; - disableIssueCreation?: boolean; - currentStore?: EProjectStore; - addIssuesToView?: (issueIds: string[]) => Promise; -} - -const Icon = ({ emoji }: any) =>
{renderEmoji(emoji)}
; - -export const ProjectHeader: FC = observer((props) => { - const { - column_id, - column_value, - sub_group_by, - group_by, - header_type, - issues_count, - kanBanToggle, - handleKanBanToggle, - disableIssueCreation, - currentStore, - addIssuesToView, - } = props; - - const project = column_value ?? null; - - return ( - <> - {project && - (sub_group_by && header_type === "sub_group_by" ? ( - } - title={project?.name || ""} - count={issues_count} - kanBanToggle={kanBanToggle} - handleKanBanToggle={handleKanBanToggle} - /> - ) : ( - } - title={project?.name || ""} - count={issues_count} - kanBanToggle={kanBanToggle} - handleKanBanToggle={handleKanBanToggle} - issuePayload={{ project: project?.id }} - disableIssueCreation={disableIssueCreation} - currentStore={currentStore} - addIssuesToView={addIssuesToView} - /> - ))} - - ); -}); diff --git a/web/components/issues/issue-layouts/kanban/headers/state-group.tsx b/web/components/issues/issue-layouts/kanban/headers/state-group.tsx deleted file mode 100644 index 7ebf4f8cd..000000000 --- a/web/components/issues/issue-layouts/kanban/headers/state-group.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import { FC } from "react"; -import { observer } from "mobx-react-lite"; -// components -import { HeaderGroupByCard } from "./group-by-card"; -import { HeaderSubGroupByCard } from "./sub-group-by-card"; -import { StateGroupIcon } from "@plane/ui"; -import { EProjectStore } from "store_legacy/command-palette.store"; -import { IIssue } from "types"; - -export interface IStateGroupHeader { - column_id: string; - column_value: any; - sub_group_by: string | null; - group_by: string | null; - header_type: "group_by" | "sub_group_by"; - issues_count: number; - kanBanToggle: any; - handleKanBanToggle: any; - disableIssueCreation?: boolean; - currentStore?: EProjectStore; - addIssuesToView?: (issueIds: string[]) => Promise; -} - -export const Icon = ({ stateGroup, color }: { stateGroup: any; color?: any }) => ( -
- -
-); - -export const StateGroupHeader: FC = observer((props) => { - const { - column_id, - column_value, - sub_group_by, - group_by, - header_type, - issues_count, - kanBanToggle, - handleKanBanToggle, - disableIssueCreation, - currentStore, - addIssuesToView, - } = props; - - const stateGroup = column_value || null; - - return ( - <> - {stateGroup && - (sub_group_by && header_type === "sub_group_by" ? ( - } - title={stateGroup?.key || ""} - count={issues_count} - kanBanToggle={kanBanToggle} - handleKanBanToggle={handleKanBanToggle} - /> - ) : ( - } - title={stateGroup?.key || ""} - count={issues_count} - kanBanToggle={kanBanToggle} - handleKanBanToggle={handleKanBanToggle} - issuePayload={{}} - disableIssueCreation={disableIssueCreation} - currentStore={currentStore} - addIssuesToView={addIssuesToView} - /> - ))} - - ); -}); diff --git a/web/components/issues/issue-layouts/kanban/headers/state.tsx b/web/components/issues/issue-layouts/kanban/headers/state.tsx deleted file mode 100644 index ed90ffa71..000000000 --- a/web/components/issues/issue-layouts/kanban/headers/state.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import { FC } from "react"; -import { observer } from "mobx-react-lite"; -// components -import { HeaderGroupByCard } from "./group-by-card"; -import { HeaderSubGroupByCard } from "./sub-group-by-card"; -import { Icon } from "./state-group"; -import { EProjectStore } from "store_legacy/command-palette.store"; -import { IIssue } from "types"; - -export interface IStateHeader { - column_id: string; - column_value: any; - sub_group_by: string | null; - group_by: string | null; - header_type: "group_by" | "sub_group_by"; - issues_count: number; - kanBanToggle: any; - handleKanBanToggle: any; - disableIssueCreation?: boolean; - currentStore?: EProjectStore; - addIssuesToView?: (issueIds: string[]) => Promise; -} - -export const StateHeader: FC = observer((props) => { - const { - column_id, - column_value, - sub_group_by, - group_by, - header_type, - issues_count, - kanBanToggle, - handleKanBanToggle, - disableIssueCreation, - currentStore, - addIssuesToView, - } = props; - - const state = column_value ?? null; - - return ( - <> - {state && - (sub_group_by && header_type === "sub_group_by" ? ( - } - title={state?.name || ""} - count={issues_count} - kanBanToggle={kanBanToggle} - handleKanBanToggle={handleKanBanToggle} - /> - ) : ( - } - title={state?.name || ""} - count={issues_count} - kanBanToggle={kanBanToggle} - handleKanBanToggle={handleKanBanToggle} - issuePayload={{ state: state?.id }} - disableIssueCreation={disableIssueCreation} - currentStore={currentStore} - addIssuesToView={addIssuesToView} - /> - ))} - - ); -}); diff --git a/web/components/issues/issue-layouts/kanban/headers/sub-group-by-root.tsx b/web/components/issues/issue-layouts/kanban/headers/sub-group-by-root.tsx deleted file mode 100644 index 97f638f1f..000000000 --- a/web/components/issues/issue-layouts/kanban/headers/sub-group-by-root.tsx +++ /dev/null @@ -1,134 +0,0 @@ -// mobx -import { observer } from "mobx-react-lite"; -// components -import { StateHeader } from "./state"; -import { StateGroupHeader } from "./state-group"; -import { AssigneesHeader } from "./assignee"; -import { PriorityHeader } from "./priority"; -import { LabelHeader } from "./label"; -import { CreatedByHeader } from "./created_by"; -import { EProjectStore } from "store_legacy/command-palette.store"; -import { IIssue } from "types"; - -export interface IKanBanSubGroupByHeaderRoot { - column_id: string; - column_value: any; - sub_group_by: string | null; - group_by: string | null; - issues_count: number; - kanBanToggle: any; - handleKanBanToggle: any; - disableIssueCreation?: boolean; - currentStore?: EProjectStore; - addIssuesToView?: (issueIds: string[]) => Promise; -} - -export const KanBanSubGroupByHeaderRoot: React.FC = observer((props) => { - const { - column_id, - column_value, - sub_group_by, - group_by, - issues_count, - kanBanToggle, - handleKanBanToggle, - disableIssueCreation, - currentStore, - addIssuesToView, - } = props; - - return ( - <> - {sub_group_by && sub_group_by === "state" && ( - - )} - {sub_group_by && sub_group_by === "state_detail.group" && ( - - )} - {sub_group_by && sub_group_by === "priority" && ( - - )} - {sub_group_by && sub_group_by === "labels" && ( - - )} - {sub_group_by && sub_group_by === "assignees" && ( - - )} - {sub_group_by && sub_group_by === "created_by" && ( - - )} - - ); -}); diff --git a/web/components/issues/issue-layouts/kanban/properties.tsx b/web/components/issues/issue-layouts/kanban/properties.tsx index 788607690..e5f4f863a 100644 --- a/web/components/issues/issue-layouts/kanban/properties.tsx +++ b/web/components/issues/issue-layouts/kanban/properties.tsx @@ -13,72 +13,42 @@ import { Tooltip } from "@plane/ui"; import { IIssue, IIssueDisplayProperties, IState, TIssuePriorities } from "types"; export interface IKanBanProperties { - sub_group_id: string; - columnId: string; issue: IIssue; - handleIssues: (sub_group_by: string | null, group_by: string | null, issue: IIssue) => void; + handleIssues: (issue: IIssue) => void; displayProperties: IIssueDisplayProperties | null; showEmptyGroup: boolean; isReadOnly: boolean; } export const KanBanProperties: React.FC = observer((props) => { - const { sub_group_id, columnId: group_id, issue, handleIssues, displayProperties, isReadOnly } = props; + const { issue, handleIssues, displayProperties, isReadOnly } = props; const handleState = (state: IState) => { - handleIssues( - !sub_group_id && sub_group_id === "null" ? null : sub_group_id, - !group_id && group_id === "null" ? null : group_id, - { ...issue, state: state.id } - ); + handleIssues({ ...issue, state: state.id }); }; const handlePriority = (value: TIssuePriorities) => { - handleIssues( - !sub_group_id && sub_group_id === "null" ? null : sub_group_id, - !group_id && group_id === "null" ? null : group_id, - { ...issue, priority: value } - ); + handleIssues({ ...issue, priority: value }); }; const handleLabel = (ids: string[]) => { - handleIssues( - !sub_group_id && sub_group_id === "null" ? null : sub_group_id, - !group_id && group_id === "null" ? null : group_id, - { ...issue, labels: ids } - ); + handleIssues({ ...issue, labels: ids }); }; const handleAssignee = (ids: string[]) => { - handleIssues( - !sub_group_id && sub_group_id === "null" ? null : sub_group_id, - !group_id && group_id === "null" ? null : group_id, - { ...issue, assignees: ids } - ); + handleIssues({ ...issue, assignees: ids }); }; const handleStartDate = (date: string) => { - handleIssues( - !sub_group_id && sub_group_id === "null" ? null : sub_group_id, - !group_id && group_id === "null" ? null : group_id, - { ...issue, start_date: date } - ); + handleIssues({ ...issue, start_date: date }); }; const handleTargetDate = (date: string) => { - handleIssues( - !sub_group_id && sub_group_id === "null" ? null : sub_group_id, - !group_id && group_id === "null" ? null : group_id, - { ...issue, target_date: date } - ); + handleIssues({ ...issue, target_date: date }); }; const handleEstimate = (value: number | null) => { - handleIssues( - !sub_group_id && sub_group_id === "null" ? null : sub_group_id, - !group_id && group_id === "null" ? null : group_id, - { ...issue, estimate_point: value } - ); + handleIssues({ ...issue, estimate_point: value }); }; return ( diff --git a/web/components/issues/issue-layouts/kanban/swimlanes.tsx b/web/components/issues/issue-layouts/kanban/swimlanes.tsx index b76d82b91..d856ce0ef 100644 --- a/web/components/issues/issue-layouts/kanban/swimlanes.tsx +++ b/web/components/issues/issue-layouts/kanban/swimlanes.tsx @@ -1,71 +1,50 @@ import React from "react"; import { observer } from "mobx-react-lite"; // components -import { KanBanGroupByHeaderRoot } from "./headers/group-by-root"; -import { KanBanSubGroupByHeaderRoot } from "./headers/sub-group-by-root"; import { KanBan } from "./default"; +import { HeaderSubGroupByCard } from "./headers/sub-group-by-card"; +import { HeaderGroupByCard } from "./headers/group-by-card"; // types -import { IIssue, IIssueDisplayProperties, IIssueLabel, IProject, IState, IUserLite } from "types"; +import { IIssue, IIssueDisplayProperties } from "types"; import { IIssueResponse, IGroupedIssues, ISubGroupedIssues, TUnGroupedIssues } from "store_legacy/issues/types"; // constants -import { getValueFromObject } from "constants/issue"; import { EIssueActions } from "../types"; import { EProjectStore } from "store_legacy/command-palette.store"; +import { IKanbanColumn, columnTypes, getKanbanColumns } from "./utils"; +import { useMobxStore } from "lib/mobx/store-provider"; interface ISubGroupSwimlaneHeader { - issues: IIssueResponse; issueIds: any; sub_group_by: string | null; group_by: string | null; - list: any; - listKey: string; + list: IKanbanColumn[]; kanBanToggle: any; handleKanBanToggle: any; - disableIssueCreation?: boolean; - currentStore?: EProjectStore; - addIssuesToView?: (issueIds: string[]) => Promise; } const SubGroupSwimlaneHeader: React.FC = ({ issueIds, sub_group_by, group_by, list, - listKey, kanBanToggle, handleKanBanToggle, - disableIssueCreation, - currentStore, - addIssuesToView, }) => { - const calculateIssueCount = (column_id: string) => { - let issueCount = 0; - issueIds && - Object.keys(issueIds)?.forEach((_issueKey: any) => { - issueCount += issueIds?.[_issueKey]?.[column_id]?.length || 0; - }); - return issueCount; - }; - return (
{list && list.length > 0 && - list.map((_list: any) => ( -
- ( +
+
))} @@ -78,19 +57,8 @@ interface ISubGroupSwimlane extends ISubGroupSwimlaneHeader { issueIds: any; order_by: string | null; showEmptyGroup: boolean; - states: IState[] | null; - stateGroups: any; - priorities: any; - labels: IIssueLabel[] | null; - members: IUserLite[] | null; - projects: IProject[] | null; - handleIssues: (sub_group_by: string | null, group_by: string | null, issue: IIssue, action: EIssueActions) => void; - quickActions: ( - sub_group_by: string | null, - group_by: string | null, - issue: IIssue, - customActionButton?: React.ReactElement - ) => React.ReactNode; + handleIssues: (issue: IIssue, action: EIssueActions) => void; + quickActions: (issue: IIssue, customActionButton?: React.ReactElement) => React.ReactNode; displayProperties: IIssueDisplayProperties | null; kanBanToggle: any; handleKanBanToggle: any; @@ -99,6 +67,7 @@ interface ISubGroupSwimlane extends ISubGroupSwimlaneHeader { currentStore?: EProjectStore; enableQuickIssueCreate: boolean; canEditProperties: (projectId: string | undefined) => boolean; + addIssuesToView?: (issueIds: string[]) => Promise; quickAddCallback?: ( workspaceSlug: string, projectId: string, @@ -112,23 +81,13 @@ const SubGroupSwimlane: React.FC = observer((props) => { issueIds, sub_group_by, group_by, - order_by, list, - listKey, handleIssues, quickActions, displayProperties, kanBanToggle, handleKanBanToggle, showEmptyGroup, - states, - stateGroups, - priorities, - labels, - members, - projects, - isDragStarted, - disableIssueCreation, enableQuickIssueCreate, canEditProperties, addIssuesToView, @@ -152,43 +111,32 @@ const SubGroupSwimlane: React.FC = observer((props) => {
-
- {!kanBanToggle?.subgroupByIssuesVisibility.includes(getValueFromObject(_list, listKey) as string) && ( + {!kanBanToggle?.subgroupByIssuesVisibility.includes(_list.id) && (
void; - quickActions: ( - sub_group_by: string | null, - group_by: string | null, - issue: IIssue, - customActionButton?: React.ReactElement - ) => React.ReactNode; + handleIssues: (issue: IIssue, action: EIssueActions) => void; + quickActions: (issue: IIssue, customActionButton?: React.ReactElement) => React.ReactNode; displayProperties: IIssueDisplayProperties | null; kanBanToggle: any; handleKanBanToggle: any; showEmptyGroup: boolean; - states: IState[] | null; - stateGroups: any; - priorities: any; - labels: IIssueLabel[] | null; - members: IUserLite[] | null; - projects: IProject[] | null; isDragStarted?: boolean; disableIssueCreation?: boolean; currentStore?: EProjectStore; @@ -251,363 +188,58 @@ export const KanBanSwimLanes: React.FC = observer((props) => { kanBanToggle, handleKanBanToggle, showEmptyGroup, - states, - stateGroups, - priorities, - labels, - members, - projects, isDragStarted, disableIssueCreation, enableQuickIssueCreate, canEditProperties, - currentStore, addIssuesToView, quickAddCallback, } = props; + const { project, projectLabel, projectMember, projectState } = useMobxStore(); + + const groupByList = getKanbanColumns(group_by as columnTypes, project, projectLabel, projectMember, projectState); + const subGroupByList = getKanbanColumns( + sub_group_by as columnTypes, + project, + projectLabel, + projectMember, + projectState + ); + + if (!groupByList || !subGroupByList) return null; + return (
- {group_by && group_by === "project" && ( - - )} - - {group_by && group_by === "state" && ( - - )} - - {group_by && group_by === "state_detail.group" && ( - - )} - - {group_by && group_by === "priority" && ( - - )} - - {group_by && group_by === "labels" && ( - - )} - - {group_by && group_by === "assignees" && ( - - )} - - {group_by && group_by === "created_by" && ( - - )} +
- {sub_group_by && sub_group_by === "project" && ( + {sub_group_by && ( - )} - - {sub_group_by && sub_group_by === "state" && ( - - )} - - {sub_group_by && sub_group_by === "state" && ( - - )} - - {sub_group_by && sub_group_by === "state_detail.group" && ( - - )} - - {sub_group_by && sub_group_by === "priority" && ( - - )} - - {sub_group_by && sub_group_by === "labels" && ( - - )} - - {sub_group_by && sub_group_by === "assignees" && ( - - )} - - {sub_group_by && sub_group_by === "created_by" && ( - diff --git a/web/components/issues/issue-layouts/kanban/utils.tsx b/web/components/issues/issue-layouts/kanban/utils.tsx new file mode 100644 index 000000000..97a6b8b55 --- /dev/null +++ b/web/components/issues/issue-layouts/kanban/utils.tsx @@ -0,0 +1,162 @@ +import { Avatar, PriorityIcon, StateGroupIcon } from "@plane/ui"; +import { ISSUE_PRIORITIES, ISSUE_STATE_GROUPS } from "constants/issue"; +import { renderEmoji } from "helpers/emoji.helper"; +import { ReactElement } from "react"; +import { IProjectLabelStore, IProjectMemberStore, IProjectStateStore, IProjectStore } from "store_legacy/project"; +import { IIssue } from "types"; + +export type columnTypes = + | "project" + | "state" + | "state_detail.group" + | "priority" + | "labels" + | "assignees" + | "created_by"; + +export interface IKanbanColumn { + id: string; + name: string; + Icon: ReactElement; + payload: Partial; +} + +export const getKanbanColumns = ( + groupBy: columnTypes | null, + project: IProjectStore, + projectLabel: IProjectLabelStore, + projectMember: IProjectMemberStore, + projectState: IProjectStateStore +): IKanbanColumn[] | undefined => { + switch (groupBy) { + case "project": + return getProjectColumns(project); + case "state": + return getStateColumns(projectState); + case "state_detail.group": + return getStateGroupColumns(); + case "priority": + return getPriorityColumns(); + case "labels": + return getLabelsColumns(projectLabel); + case "assignees": + return getAssigneeColumns(projectMember); + case "created_by": + return getCreatedByColumns(projectMember); + } +}; + +const getProjectColumns = (project: IProjectStore): IKanbanColumn[] | undefined => { + const { workspaceProjects: projects } = project; + + if (!projects) return; + + return projects.map((project) => { + return { + id: project.id, + name: project.name, + Icon:
{renderEmoji(project.emoji || "")}
, + payload: { project: project.id }, + }; + }); +}; + +const getStateColumns = (projectState: IProjectStateStore): IKanbanColumn[] | undefined => { + const { projectStates } = projectState; + if (!projectStates) return; + + return projectStates.map((state) => { + return { + id: state.id, + name: state.name, + Icon: ( +
+ +
+ ), + payload: { state: state.id }, + }; + }); +}; + +const getStateGroupColumns = () => { + const stateGroups = ISSUE_STATE_GROUPS; + + return stateGroups.map((stateGroup) => { + return { + id: stateGroup.key, + name: stateGroup.title, + Icon: ( +
+ +
+ ), + payload: {}, + }; + }); +}; + +const getPriorityColumns = () => { + const priorities = ISSUE_PRIORITIES; + + return priorities.map((priority) => { + return { + id: priority.key, + name: priority.title, + Icon: , + payload: { priority: priority.key }, + }; + }); +}; + +const getLabelsColumns = (projectLabel: IProjectLabelStore) => { + const { projectLabels } = projectLabel; + + if (!projectLabels) return; + + const labels = [...projectLabels, { id: "None", name: "None", color: "#666" }]; + + return labels.map((label) => { + return { + id: label.id, + name: label.name, + Icon: ( +
+ ), + payload: { labels: [label.id] }, + }; + }); +}; + +const getAssigneeColumns = (projectMember: IProjectMemberStore) => { + const { projectMembers: users } = projectMember; + if (!users) return; + + return users.map((user) => { + const member = user.member; + return { + id: member?.id, + name: member?.display_name || "", + Icon: , + payload: { assignees: [member?.id] }, + }; + }); +}; + +const getCreatedByColumns = (projectMember: IProjectMemberStore) => { + const { projectMembers: users } = projectMember; + if (!users) return; + + return users.map((user) => { + const member = user.member; + return { + id: member?.id, + name: member?.display_name || "", + Icon: , + payload: { created_by: member?.id }, + }; + }); +};