From f347c1cd69e34131b118f9e6abc0d0b9d101652c Mon Sep 17 00:00:00 2001 From: Prateek Shourya Date: Thu, 18 Jan 2024 14:42:10 +0530 Subject: [PATCH] refactor: update `create/update issue` modal to use currently active store's create/update method. (#3395) * refactor: update `create/update issue` modal to use currently active store's create/update method. * chore: add condition to avoid multiple API calls if the current store is MODULE or CYCLE. * remove: console log * chore: update `currentStore` to `storeType`. --- .../command-palette/command-palette.tsx | 2 + .../issue-layouts/empty-states/module.tsx | 2 +- .../issue-layouts/kanban/base-kanban-root.tsx | 6 +- .../issues/issue-layouts/kanban/default.tsx | 12 +- .../kanban/headers/group-by-card.tsx | 10 +- .../issue-layouts/kanban/roots/cycle-root.tsx | 2 +- .../kanban/roots/module-root.tsx | 2 +- .../kanban/roots/profile-issues-root.tsx | 2 +- .../kanban/roots/project-root.tsx | 2 +- .../kanban/roots/project-view-root.tsx | 2 +- .../issues/issue-layouts/kanban/swimlanes.tsx | 4 +- .../issue-layouts/list/base-list-root.tsx | 6 +- .../issues/issue-layouts/list/default.tsx | 12 +- .../list/headers/group-by-card.tsx | 11 +- .../list/roots/archived-issue-root.tsx | 2 +- .../issue-layouts/list/roots/cycle-root.tsx | 2 +- .../list/roots/draft-issue-root.tsx | 2 +- .../issue-layouts/list/roots/module-root.tsx | 2 +- .../list/roots/profile-issues-root.tsx | 2 +- .../issue-layouts/list/roots/project-root.tsx | 2 +- .../list/roots/project-view-root.tsx | 2 +- .../quick-action-dropdowns/all-issue.tsx | 3 + .../quick-action-dropdowns/cycle-issue.tsx | 3 + .../quick-action-dropdowns/module-issue.tsx | 3 + .../quick-action-dropdowns/project-issue.tsx | 2 + web/components/issues/issue-modal/form.tsx | 1 - web/components/issues/issue-modal/modal.tsx | 213 ++++++++++++++---- web/store/issue/cycle/issue.store.ts | 20 +- web/store/issue/module/issue.store.ts | 19 +- 29 files changed, 251 insertions(+), 102 deletions(-) diff --git a/web/components/command-palette/command-palette.tsx b/web/components/command-palette/command-palette.tsx index 04b2fb714..6a550e0ad 100644 --- a/web/components/command-palette/command-palette.tsx +++ b/web/components/command-palette/command-palette.tsx @@ -60,6 +60,7 @@ export const CommandPalette: FC = observer(() => { isDeleteIssueModalOpen, toggleDeleteIssueModal, isAnyModalOpen, + createIssueStoreType, } = commandPalette; const { setToastAlert } = useToast(); @@ -216,6 +217,7 @@ export const CommandPalette: FC = observer(() => { isOpen={isCreateIssueModalOpen} onClose={() => toggleCreateIssueModal(false)} data={cycleId ? { cycle_id: cycleId.toString() } : moduleId ? { module_id: moduleId.toString() } : undefined} + storeType={createIssueStoreType} /> {workspaceSlug && projectId && issueId && issueDetails && ( diff --git a/web/components/issues/issue-layouts/empty-states/module.tsx b/web/components/issues/issue-layouts/empty-states/module.tsx index cb70a5a22..a91bcde1e 100644 --- a/web/components/issues/issue-layouts/empty-states/module.tsx +++ b/web/components/issues/issue-layouts/empty-states/module.tsx @@ -75,7 +75,7 @@ export const ModuleEmptyState: React.FC = observer((props) => { icon: , onClick: () => { setTrackElement("MODULE_EMPTY_STATE"); - toggleCreateIssueModal(true); + toggleCreateIssueModal(true, EIssuesStoreType.MODULE); }, }} secondaryButton={ 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 7349f6803..4c2e21649 100644 --- a/web/components/issues/issue-layouts/kanban/base-kanban-root.tsx +++ b/web/components/issues/issue-layouts/kanban/base-kanban-root.tsx @@ -43,7 +43,7 @@ export interface IBaseKanBanLayout { }; showLoader?: boolean; viewId?: string; - currentStore?: TCreateModalStoreTypes; + storeType?: TCreateModalStoreTypes; addIssuesToView?: (issueIds: string[]) => Promise; canEditPropertiesBasedOnProject?: (projectId: string) => boolean; } @@ -62,7 +62,7 @@ export const BaseKanBanRoot: React.FC = observer((props: IBas issueActions, showLoader, viewId, - currentStore, + storeType, addIssuesToView, canEditPropertiesBasedOnProject, } = props; @@ -277,7 +277,7 @@ export const BaseKanBanRoot: React.FC = observer((props: IBas viewId={viewId} disableIssueCreation={!enableIssueCreation || !isEditingAllowed} canEditProperties={canEditProperties} - currentStore={currentStore} + storeType={storeType} addIssuesToView={addIssuesToView} /> diff --git a/web/components/issues/issue-layouts/kanban/default.tsx b/web/components/issues/issue-layouts/kanban/default.tsx index 49f022299..5b5dc3ead 100644 --- a/web/components/issues/issue-layouts/kanban/default.tsx +++ b/web/components/issues/issue-layouts/kanban/default.tsx @@ -42,7 +42,7 @@ export interface IGroupByKanBan { ) => Promise; viewId?: string; disableIssueCreation?: boolean; - currentStore?: TCreateModalStoreTypes; + storeType?: TCreateModalStoreTypes; addIssuesToView?: (issueIds: string[]) => Promise; canEditProperties: (projectId: string | undefined) => boolean; } @@ -64,7 +64,7 @@ const GroupByKanBan: React.FC = observer((props) => { quickAddCallback, viewId, disableIssueCreation, - currentStore, + storeType, addIssuesToView, canEditProperties, } = props; @@ -107,7 +107,7 @@ const GroupByKanBan: React.FC = observer((props) => { count={(issueIds as TGroupedIssues)?.[_list.id]?.length || 0} issuePayload={_list.payload} disableIssueCreation={disableIssueCreation || isGroupByCreatedBy} - currentStore={currentStore} + storeType={storeType} addIssuesToView={addIssuesToView} kanbanFilters={kanbanFilters} handleKanbanFilters={handleKanbanFilters} @@ -163,7 +163,7 @@ export interface IKanBan { ) => Promise; viewId?: string; disableIssueCreation?: boolean; - currentStore?: TCreateModalStoreTypes; + storeType?: TCreateModalStoreTypes; addIssuesToView?: (issueIds: string[]) => Promise; canEditProperties: (projectId: string | undefined) => boolean; } @@ -184,7 +184,7 @@ export const KanBan: React.FC = observer((props) => { quickAddCallback, viewId, disableIssueCreation, - currentStore, + storeType, addIssuesToView, canEditProperties, } = props; @@ -208,7 +208,7 @@ export const KanBan: React.FC = observer((props) => { quickAddCallback={quickAddCallback} viewId={viewId} disableIssueCreation={disableIssueCreation} - currentStore={currentStore} + storeType={storeType} addIssuesToView={addIssuesToView} canEditProperties={canEditProperties} /> diff --git a/web/components/issues/issue-layouts/kanban/headers/group-by-card.tsx b/web/components/issues/issue-layouts/kanban/headers/group-by-card.tsx index 5095b9bec..3bb49106b 100644 --- a/web/components/issues/issue-layouts/kanban/headers/group-by-card.tsx +++ b/web/components/issues/issue-layouts/kanban/headers/group-by-card.tsx @@ -25,7 +25,7 @@ interface IHeaderGroupByCard { handleKanbanFilters: any; issuePayload: Partial; disableIssueCreation?: boolean; - currentStore?: TCreateModalStoreTypes; + storeType?: TCreateModalStoreTypes; addIssuesToView?: (issueIds: string[]) => Promise; } @@ -40,6 +40,7 @@ export const HeaderGroupByCard: FC = observer((props) => { handleKanbanFilters, issuePayload, disableIssueCreation, + storeType, addIssuesToView, } = props; const verticalAlignPosition = sub_group_by ? false : kanbanFilters?.group_by.includes(column_id); @@ -83,7 +84,12 @@ export const HeaderGroupByCard: FC = observer((props) => { fieldsToShow={["all"]} /> ) : ( - setIsOpen(false)} data={issuePayload} /> + setIsOpen(false)} + data={issuePayload} + storeType={storeType} + /> )} {renderExistingIssueModal && ( { showLoader={true} QuickActions={CycleIssueQuickActions} viewId={cycleId?.toString() ?? ""} - currentStore={EIssuesStoreType.CYCLE} + storeType={EIssuesStoreType.CYCLE} addIssuesToView={(issueIds: string[]) => { if (!workspaceSlug || !projectId || !cycleId) throw new Error(); return issues.addIssueToCycle(workspaceSlug.toString(), projectId.toString(), cycleId.toString(), issueIds); diff --git a/web/components/issues/issue-layouts/kanban/roots/module-root.tsx b/web/components/issues/issue-layouts/kanban/roots/module-root.tsx index 4577d56b7..89f4683af 100644 --- a/web/components/issues/issue-layouts/kanban/roots/module-root.tsx +++ b/web/components/issues/issue-layouts/kanban/roots/module-root.tsx @@ -50,7 +50,7 @@ export const ModuleKanBanLayout: React.FC = observer(() => { showLoader={true} QuickActions={ModuleIssueQuickActions} viewId={moduleId?.toString()} - currentStore={EIssuesStoreType.MODULE} + storeType={EIssuesStoreType.MODULE} addIssuesToView={(issueIds: string[]) => { if (!workspaceSlug || !projectId || !moduleId) throw new Error(); return issues.addIssueToModule(workspaceSlug.toString(), projectId.toString(), moduleId.toString(), issueIds); diff --git a/web/components/issues/issue-layouts/kanban/roots/profile-issues-root.tsx b/web/components/issues/issue-layouts/kanban/roots/profile-issues-root.tsx index c0bf0a728..4b3f3fe83 100644 --- a/web/components/issues/issue-layouts/kanban/roots/profile-issues-root.tsx +++ b/web/components/issues/issue-layouts/kanban/roots/profile-issues-root.tsx @@ -52,7 +52,7 @@ export const ProfileIssuesKanBanLayout: React.FC = observer(() => { issues={issues} showLoader={true} QuickActions={ProjectIssueQuickActions} - currentStore={EIssuesStoreType.PROFILE} + storeType={EIssuesStoreType.PROFILE} canEditPropertiesBasedOnProject={canEditPropertiesBasedOnProject} /> ); diff --git a/web/components/issues/issue-layouts/kanban/roots/project-root.tsx b/web/components/issues/issue-layouts/kanban/roots/project-root.tsx index b67ceb460..89e2ee187 100644 --- a/web/components/issues/issue-layouts/kanban/roots/project-root.tsx +++ b/web/components/issues/issue-layouts/kanban/roots/project-root.tsx @@ -43,7 +43,7 @@ export const KanBanLayout: React.FC = observer(() => { issuesFilter={issuesFilter} showLoader={true} QuickActions={ProjectIssueQuickActions} - currentStore={EIssuesStoreType.PROJECT} + storeType={EIssuesStoreType.PROJECT} /> ); }); 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 0458b6f98..1cdf71d45 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 @@ -35,7 +35,7 @@ export const ProjectViewKanBanLayout: React.FC = observer((pr issues={issues} showLoader={true} QuickActions={ProjectIssueQuickActions} - currentStore={EIssuesStoreType.PROJECT_VIEW} + storeType={EIssuesStoreType.PROJECT_VIEW} viewId={viewId?.toString()} /> ); diff --git a/web/components/issues/issue-layouts/kanban/swimlanes.tsx b/web/components/issues/issue-layouts/kanban/swimlanes.tsx index 7beba8a92..33126d17b 100644 --- a/web/components/issues/issue-layouts/kanban/swimlanes.tsx +++ b/web/components/issues/issue-layouts/kanban/swimlanes.tsx @@ -69,7 +69,7 @@ interface ISubGroupSwimlane extends ISubGroupSwimlaneHeader { handleKanbanFilters: (toggle: "group_by" | "sub_group_by", value: string) => void; isDragStarted?: boolean; disableIssueCreation?: boolean; - currentStore?: TCreateModalStoreTypes; + storeType?: TCreateModalStoreTypes; enableQuickIssueCreate: boolean; canEditProperties: (projectId: string | undefined) => boolean; addIssuesToView?: (issueIds: string[]) => Promise; @@ -172,7 +172,7 @@ export interface IKanBanSwimLanes { showEmptyGroup: boolean; isDragStarted?: boolean; disableIssueCreation?: boolean; - currentStore?: TCreateModalStoreTypes; + storeType?: TCreateModalStoreTypes; addIssuesToView?: (issueIds: string[]) => Promise; enableQuickIssueCreate: boolean; quickAddCallback?: ( diff --git a/web/components/issues/issue-layouts/list/base-list-root.tsx b/web/components/issues/issue-layouts/list/base-list-root.tsx index 87cafa3ad..b718269b6 100644 --- a/web/components/issues/issue-layouts/list/base-list-root.tsx +++ b/web/components/issues/issue-layouts/list/base-list-root.tsx @@ -48,7 +48,7 @@ interface IBaseListRoot { [EIssueActions.REMOVE]?: (issue: TIssue) => Promise; }; viewId?: string; - currentStore: TCreateModalStoreTypes; + storeType: TCreateModalStoreTypes; addIssuesToView?: (issueIds: string[]) => Promise; canEditPropertiesBasedOnProject?: (projectId: string) => boolean; } @@ -60,7 +60,7 @@ export const BaseListRoot = observer((props: IBaseListRoot) => { QuickActions, issueActions, viewId, - currentStore, + storeType, addIssuesToView, canEditPropertiesBasedOnProject, } = props; @@ -134,7 +134,7 @@ export const BaseListRoot = observer((props: IBaseListRoot) => { enableIssueQuickAdd={!!enableQuickAdd} canEditProperties={canEditProperties} disableIssueCreation={!enableIssueCreation || !isEditingAllowed} - currentStore={currentStore} + storeType={storeType} addIssuesToView={addIssuesToView} /> diff --git a/web/components/issues/issue-layouts/list/default.tsx b/web/components/issues/issue-layouts/list/default.tsx index 38994215c..df9103817 100644 --- a/web/components/issues/issue-layouts/list/default.tsx +++ b/web/components/issues/issue-layouts/list/default.tsx @@ -34,7 +34,7 @@ export interface IGroupByList { viewId?: string ) => Promise; disableIssueCreation?: boolean; - currentStore: TCreateModalStoreTypes; + storeType: TCreateModalStoreTypes; addIssuesToView?: (issueIds: string[]) => Promise; viewId?: string; } @@ -53,7 +53,7 @@ const GroupByList: React.FC = (props) => { quickAddCallback, viewId, disableIssueCreation, - currentStore, + storeType, addIssuesToView, } = props; // store hooks @@ -116,7 +116,7 @@ const GroupByList: React.FC = (props) => { count={is_list ? issueIds?.length || 0 : issueIds?.[_list.id]?.length || 0} issuePayload={_list.payload} disableIssueCreation={disableIssueCreation || isGroupByCreatedBy} - currentStore={currentStore} + storeType={storeType} addIssuesToView={addIssuesToView} /> @@ -166,7 +166,7 @@ export interface IList { ) => Promise; viewId?: string; disableIssueCreation?: boolean; - currentStore: TCreateModalStoreTypes; + storeType: TCreateModalStoreTypes; addIssuesToView?: (issueIds: string[]) => Promise; } @@ -184,7 +184,7 @@ export const List: React.FC = (props) => { enableIssueQuickAdd, canEditProperties, disableIssueCreation, - currentStore, + storeType, addIssuesToView, } = props; @@ -203,7 +203,7 @@ export const List: React.FC = (props) => { quickAddCallback={quickAddCallback} viewId={viewId} disableIssueCreation={disableIssueCreation} - currentStore={currentStore} + storeType={storeType} addIssuesToView={addIssuesToView} /> diff --git a/web/components/issues/issue-layouts/list/headers/group-by-card.tsx b/web/components/issues/issue-layouts/list/headers/group-by-card.tsx index 4989cb250..8e7b0ace3 100644 --- a/web/components/issues/issue-layouts/list/headers/group-by-card.tsx +++ b/web/components/issues/issue-layouts/list/headers/group-by-card.tsx @@ -19,12 +19,12 @@ interface IHeaderGroupByCard { count: number; issuePayload: Partial; disableIssueCreation?: boolean; - currentStore: TCreateModalStoreTypes; + storeType: TCreateModalStoreTypes; addIssuesToView?: (issueIds: string[]) => Promise; } export const HeaderGroupByCard = observer( - ({ icon, title, count, issuePayload, disableIssueCreation, currentStore, addIssuesToView }: IHeaderGroupByCard) => { + ({ icon, title, count, issuePayload, disableIssueCreation, storeType, addIssuesToView }: IHeaderGroupByCard) => { const router = useRouter(); const { workspaceSlug, projectId, moduleId, cycleId } = router.query; @@ -101,7 +101,12 @@ export const HeaderGroupByCard = observer( fieldsToShow={["all"]} /> ) : ( - setIsOpen(false)} data={issuePayload} /> + setIsOpen(false)} + data={issuePayload} + storeType={storeType} + /> )} {renderExistingIssueModal && ( diff --git a/web/components/issues/issue-layouts/list/roots/archived-issue-root.tsx b/web/components/issues/issue-layouts/list/roots/archived-issue-root.tsx index 388699dc7..e29c7dbb0 100644 --- a/web/components/issues/issue-layouts/list/roots/archived-issue-root.tsx +++ b/web/components/issues/issue-layouts/list/roots/archived-issue-root.tsx @@ -34,7 +34,7 @@ export const ArchivedIssueListLayout: FC = observer(() => { issues={issues} QuickActions={ArchivedIssueQuickActions} issueActions={issueActions} - currentStore={EIssuesStoreType.PROJECT} + storeType={EIssuesStoreType.PROJECT} /> ); }); diff --git a/web/components/issues/issue-layouts/list/roots/cycle-root.tsx b/web/components/issues/issue-layouts/list/roots/cycle-root.tsx index c1db51411..89da8dd54 100644 --- a/web/components/issues/issue-layouts/list/roots/cycle-root.tsx +++ b/web/components/issues/issue-layouts/list/roots/cycle-root.tsx @@ -48,7 +48,7 @@ export const CycleListLayout: React.FC = observer(() => { QuickActions={CycleIssueQuickActions} issueActions={issueActions} viewId={cycleId?.toString()} - currentStore={EIssuesStoreType.CYCLE} + storeType={EIssuesStoreType.CYCLE} addIssuesToView={(issueIds: string[]) => { if (!workspaceSlug || !projectId || !cycleId) throw new Error(); return issues.addIssueToCycle(workspaceSlug.toString(), projectId.toString(), cycleId.toString(), issueIds); diff --git a/web/components/issues/issue-layouts/list/roots/draft-issue-root.tsx b/web/components/issues/issue-layouts/list/roots/draft-issue-root.tsx index ef1edc831..e11971874 100644 --- a/web/components/issues/issue-layouts/list/roots/draft-issue-root.tsx +++ b/web/components/issues/issue-layouts/list/roots/draft-issue-root.tsx @@ -43,7 +43,7 @@ export const DraftIssueListLayout: FC = observer(() => { issues={issues} QuickActions={ProjectIssueQuickActions} issueActions={issueActions} - currentStore={EIssuesStoreType.PROJECT} + storeType={EIssuesStoreType.PROJECT} /> ); }); diff --git a/web/components/issues/issue-layouts/list/roots/module-root.tsx b/web/components/issues/issue-layouts/list/roots/module-root.tsx index 7dbe38090..fb874b8f6 100644 --- a/web/components/issues/issue-layouts/list/roots/module-root.tsx +++ b/web/components/issues/issue-layouts/list/roots/module-root.tsx @@ -48,7 +48,7 @@ export const ModuleListLayout: React.FC = observer(() => { QuickActions={ModuleIssueQuickActions} issueActions={issueActions} viewId={moduleId?.toString()} - currentStore={EIssuesStoreType.MODULE} + storeType={EIssuesStoreType.MODULE} addIssuesToView={(issueIds: string[]) => { if (!workspaceSlug || !projectId || !moduleId) throw new Error(); return issues.addIssueToModule(workspaceSlug.toString(), projectId.toString(), moduleId.toString(), issueIds); diff --git a/web/components/issues/issue-layouts/list/roots/profile-issues-root.tsx b/web/components/issues/issue-layouts/list/roots/profile-issues-root.tsx index 55db4cd71..1e7e364d5 100644 --- a/web/components/issues/issue-layouts/list/roots/profile-issues-root.tsx +++ b/web/components/issues/issue-layouts/list/roots/profile-issues-root.tsx @@ -52,7 +52,7 @@ export const ProfileIssuesListLayout: FC = observer(() => { issues={issues} QuickActions={ProjectIssueQuickActions} issueActions={issueActions} - currentStore={EIssuesStoreType.PROFILE} + storeType={EIssuesStoreType.PROFILE} canEditPropertiesBasedOnProject={canEditPropertiesBasedOnProject} /> ); diff --git a/web/components/issues/issue-layouts/list/roots/project-root.tsx b/web/components/issues/issue-layouts/list/roots/project-root.tsx index b99b431c8..f0479b71f 100644 --- a/web/components/issues/issue-layouts/list/roots/project-root.tsx +++ b/web/components/issues/issue-layouts/list/roots/project-root.tsx @@ -44,7 +44,7 @@ export const ListLayout: FC = observer(() => { issues={issues} QuickActions={ProjectIssueQuickActions} issueActions={issueActions} - currentStore={EIssuesStoreType.PROJECT} + storeType={EIssuesStoreType.PROJECT} /> ); }); 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 92b8a9f2c..dd384ba93 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 @@ -36,7 +36,7 @@ export const ProjectViewListLayout: React.FC = observer((props) issues={issues} QuickActions={ProjectIssueQuickActions} issueActions={issueActions} - currentStore={EIssuesStoreType.PROJECT_VIEW} + storeType={EIssuesStoreType.PROJECT_VIEW} viewId={viewId?.toString()} /> ); diff --git a/web/components/issues/issue-layouts/quick-action-dropdowns/all-issue.tsx b/web/components/issues/issue-layouts/quick-action-dropdowns/all-issue.tsx index 9d2d83071..92964ec97 100644 --- a/web/components/issues/issue-layouts/quick-action-dropdowns/all-issue.tsx +++ b/web/components/issues/issue-layouts/quick-action-dropdowns/all-issue.tsx @@ -11,6 +11,8 @@ import { copyUrlToClipboard } from "helpers/string.helper"; // types import { TIssue } from "@plane/types"; import { IQuickActionProps } from "../list/list-view-types"; +// constants +import { EIssuesStoreType } from "constants/issue"; export const AllIssueQuickActions: React.FC = (props) => { const { issue, handleDelete, handleUpdate, customActionButton, portalElement } = props; @@ -58,6 +60,7 @@ export const AllIssueQuickActions: React.FC = (props) => { onSubmit={async (data) => { if (issueToEdit && handleUpdate) await handleUpdate({ ...issueToEdit, ...data }); }} + storeType={EIssuesStoreType.PROJECT} /> = (props) => { const { issue, handleDelete, handleUpdate, handleRemoveFromView, customActionButton, portalElement } = props; @@ -58,6 +60,7 @@ export const CycleIssueQuickActions: React.FC = (props) => { onSubmit={async (data) => { if (issueToEdit && handleUpdate) await handleUpdate({ ...issueToEdit, ...data }); }} + storeType={EIssuesStoreType.CYCLE} /> = (props) => { const { issue, handleDelete, handleUpdate, handleRemoveFromView, customActionButton, portalElement } = props; @@ -58,6 +60,7 @@ export const ModuleIssueQuickActions: React.FC = (props) => { onSubmit={async (data) => { if (issueToEdit && handleUpdate) await handleUpdate({ ...issueToEdit, ...data }); }} + storeType={EIssuesStoreType.MODULE} /> = (props) => { const { issue, handleDelete, handleUpdate, customActionButton, portalElement } = props; @@ -67,6 +68,7 @@ export const ProjectIssueQuickActions: React.FC = (props) => onSubmit={async (data) => { if (issueToEdit && handleUpdate) await handleUpdate({ ...issueToEdit, ...data }); }} + storeType={EIssuesStoreType.PROJECT} /> = observer((props) => { const { data, onChange, onClose, onSubmit, projectId, isCreateMoreToggleEnabled, onCreateMoreToggleChange } = props; - console.log("onCreateMoreToggleChange", typeof onCreateMoreToggleChange); // states const [labelModal, setLabelModal] = useState(false); const [parentIssueListModalOpen, setParentIssueListModalOpen] = useState(false); diff --git a/web/components/issues/issue-modal/modal.tsx b/web/components/issues/issue-modal/modal.tsx index 81a07d761..99e60eab2 100644 --- a/web/components/issues/issue-modal/modal.tsx +++ b/web/components/issues/issue-modal/modal.tsx @@ -1,9 +1,8 @@ -import React, { useState } from "react"; -import { useRouter } from "next/router"; +import React, { useEffect, useState } from "react"; import { observer } from "mobx-react-lite"; import { Dialog, Transition } from "@headlessui/react"; // hooks -import { useIssues, useProject } from "hooks/store"; +import { useApplication, useCycle, useIssues, useModule, useProject, useUser, useWorkspace } from "hooks/store"; import useToast from "hooks/use-toast"; import useLocalStorage from "hooks/use-local-storage"; // components @@ -12,7 +11,7 @@ import { IssueFormRoot } from "./form"; // types import type { TIssue } from "@plane/types"; // constants -import { EIssuesStoreType } from "constants/issue"; +import { EIssuesStoreType, TCreateModalStoreTypes } from "constants/issue"; export interface IssuesModalProps { data?: Partial; @@ -20,31 +19,108 @@ export interface IssuesModalProps { onClose: () => void; onSubmit?: (res: TIssue) => Promise; withDraftIssueWrapper?: boolean; + storeType?: TCreateModalStoreTypes; } export const CreateUpdateIssueModal: React.FC = observer((props) => { - const { data, isOpen, onClose, onSubmit, withDraftIssueWrapper = true } = props; + const { + data, + isOpen, + onClose, + onSubmit, + withDraftIssueWrapper = true, + storeType = EIssuesStoreType.PROJECT, + } = props; // states const [changesMade, setChangesMade] = useState | null>(null); const [createMore, setCreateMore] = useState(false); - // router - const router = useRouter(); - const { workspaceSlug, projectId } = router.query; + const [activeProjectId, setActiveProjectId] = useState(null); // store hooks + const { + eventTracker: { postHogEventTracker }, + } = useApplication(); + const { currentUser } = useUser(); + const { + router: { workspaceSlug, projectId, cycleId, moduleId, viewId: projectViewId }, + } = useApplication(); + const { currentWorkspace } = useWorkspace(); const { workspaceProjectIds } = useProject(); - const { - issues: { createIssue, updateIssue }, - } = useIssues(EIssuesStoreType.PROJECT); - const { - issues: { addIssueToCycle }, - } = useIssues(EIssuesStoreType.CYCLE); - const { - issues: { addIssueToModule }, - } = useIssues(EIssuesStoreType.MODULE); + const { fetchCycleDetails } = useCycle(); + const { fetchModuleDetails } = useModule(); + const { issues: projectIssues } = useIssues(EIssuesStoreType.PROJECT); + const { issues: moduleIssues } = useIssues(EIssuesStoreType.MODULE); + const { issues: cycleIssues } = useIssues(EIssuesStoreType.CYCLE); + const { issues: viewIssues } = useIssues(EIssuesStoreType.PROJECT_VIEW); + const { issues: profileIssues } = useIssues(EIssuesStoreType.PROFILE); + // store mapping based on current store + const issueStores = { + [EIssuesStoreType.PROJECT]: { + store: projectIssues, + dataIdToUpdate: activeProjectId, + viewId: undefined, + }, + [EIssuesStoreType.PROJECT_VIEW]: { + store: viewIssues, + dataIdToUpdate: activeProjectId, + viewId: projectViewId, + }, + [EIssuesStoreType.PROFILE]: { + store: profileIssues, + dataIdToUpdate: currentUser?.id || undefined, + viewId: undefined, + }, + [EIssuesStoreType.CYCLE]: { + store: cycleIssues, + dataIdToUpdate: activeProjectId, + viewId: cycleId, + }, + [EIssuesStoreType.MODULE]: { + store: moduleIssues, + dataIdToUpdate: activeProjectId, + viewId: moduleId, + }, + }; // toast alert const { setToastAlert } = useToast(); // local storage const { setValue: setLocalStorageDraftIssue } = useLocalStorage("draftedIssue", {}); + // current store details + const { store: currentIssueStore, viewId, dataIdToUpdate } = issueStores[storeType]; + + useEffect(() => { + // if modal is closed, reset active project to null + // and return to avoid activeProjectId being set to some other project + if (!isOpen) { + setActiveProjectId(null); + return; + } + + // if data is present, set active project to the project of the + // issue. This has more priority than the project in the url. + if (data && data.project_id) { + setActiveProjectId(data.project_id); + return; + } + + // if data is not present, set active project to the project + // in the url. This has the least priority. + if (workspaceProjectIds && workspaceProjectIds.length > 0 && !activeProjectId) + setActiveProjectId(projectId ?? workspaceProjectIds?.[0]); + }, [data, projectId, workspaceProjectIds, isOpen, activeProjectId]); + + const addIssueToCycle = async (issue: TIssue, cycleId: string) => { + if (!workspaceSlug || !activeProjectId) return; + + await cycleIssues.addIssueToCycle(workspaceSlug, issue.project_id, cycleId, [issue.id]); + fetchCycleDetails(workspaceSlug, activeProjectId, cycleId); + }; + + const addIssueToModule = async (issue: TIssue, moduleId: string) => { + if (!workspaceSlug || !activeProjectId) return; + + await moduleIssues.addIssueToModule(workspaceSlug, activeProjectId, moduleId, [issue.id]); + fetchModuleDetails(workspaceSlug, activeProjectId, moduleId); + }; const handleCreateMoreToggleChange = (value: boolean) => { setCreateMore(value); @@ -55,19 +131,41 @@ export const CreateUpdateIssueModal: React.FC = observer((prop const draftIssue = JSON.stringify(changesMade); setLocalStorageDraftIssue(draftIssue); } + setActiveProjectId(null); onClose(); }; const handleCreateIssue = async (payload: Partial): Promise => { - if (!workspaceSlug || !payload.project_id) return undefined; + if (!workspaceSlug || !dataIdToUpdate) return; try { - const response = await createIssue(workspaceSlug.toString(), payload.project_id, payload); + const response = await currentIssueStore.createIssue(workspaceSlug, dataIdToUpdate, payload, viewId); + if (!response) throw new Error(); + + currentIssueStore.fetchIssues(workspaceSlug, dataIdToUpdate, "mutation", viewId); + + if (payload.cycle_id && payload.cycle_id !== "" && storeType !== EIssuesStoreType.CYCLE) + await addIssueToCycle(response, payload.cycle_id); + if (payload.module_id && payload.module_id !== "" && storeType !== EIssuesStoreType.MODULE) + await addIssueToModule(response, payload.module_id); + setToastAlert({ type: "success", title: "Success!", message: "Issue created successfully.", }); + postHogEventTracker( + "ISSUE_CREATED", + { + ...response, + state: "SUCCESS", + }, + { + isGrouping: true, + groupType: "Workspace_metrics", + groupId: currentWorkspace?.id!, + } + ); !createMore && handleClose(); return response; } catch (error) { @@ -76,19 +174,42 @@ export const CreateUpdateIssueModal: React.FC = observer((prop title: "Error!", message: "Issue could not be created. Please try again.", }); + postHogEventTracker( + "ISSUE_CREATED", + { + state: "FAILED", + }, + { + isGrouping: true, + groupType: "Workspace_metrics", + groupId: currentWorkspace?.id!, + } + ); } }; const handleUpdateIssue = async (payload: Partial): Promise => { - if (!workspaceSlug || !payload.project_id || !data?.id) return undefined; + if (!workspaceSlug || !dataIdToUpdate || !data?.id) return; try { - const response = await updateIssue(workspaceSlug.toString(), payload.project_id, data.id, payload); + const response = await currentIssueStore.updateIssue(workspaceSlug, dataIdToUpdate, data.id, payload, viewId); setToastAlert({ type: "success", title: "Success!", message: "Issue updated successfully.", }); + postHogEventTracker( + "ISSUE_UPDATED", + { + ...response, + state: "SUCCESS", + }, + { + isGrouping: true, + groupType: "Workspace_metrics", + groupId: currentWorkspace?.id!, + } + ); handleClose(); return response; } catch (error) { @@ -97,39 +218,39 @@ export const CreateUpdateIssueModal: React.FC = observer((prop title: "Error!", message: "Issue could not be created. Please try again.", }); + postHogEventTracker( + "ISSUE_UPDATED", + { + state: "FAILED", + }, + { + isGrouping: true, + groupType: "Workspace_metrics", + groupId: currentWorkspace?.id!, + } + ); } }; const handleFormSubmit = async (formData: Partial) => { - if (!workspaceSlug || !formData.project_id) return; + if (!workspaceSlug || !dataIdToUpdate || !storeType) return; const payload: Partial = { ...formData, description_html: formData.description_html ?? "

", }; - let res: TIssue | undefined = undefined; - if (!data?.id) res = await handleCreateIssue(payload); - else res = await handleUpdateIssue(payload); + let response: TIssue | undefined = undefined; + if (!data?.id) response = await handleCreateIssue(payload); + else response = await handleUpdateIssue(payload); - // add issue to cycle if cycle is selected, and cycle is different from current cycle - if (formData.cycle_id && res && (!data?.id || formData.cycle_id !== data?.cycle_id)) - await addIssueToCycle(workspaceSlug.toString(), formData.project_id, formData.cycle_id, [res.id]); - - // add issue to module if module is selected, and module is different from current module - if (formData.module_id && res && (!data?.id || formData.module_id !== data?.module_id)) - await addIssueToModule(workspaceSlug.toString(), formData.project_id, formData.module_id, [res.id]); - - if (res != undefined && onSubmit) await onSubmit(res); + if (response != undefined && onSubmit) await onSubmit(response); }; const handleFormChange = (formData: Partial | null) => setChangesMade(formData); // don't open the modal if there are no projects - if (!workspaceProjectIds || workspaceProjectIds.length === 0) return null; - - // if project id is present in the router query, use that as the selected project id, otherwise use the first project id - const selectedProjectId = projectId ? projectId.toString() : workspaceProjectIds[0]; + if (!workspaceProjectIds || workspaceProjectIds.length === 0 || !activeProjectId) return null; return ( @@ -161,22 +282,30 @@ export const CreateUpdateIssueModal: React.FC = observer((prop {withDraftIssueWrapper ? ( ) : ( handleClose(false)} isCreateMoreToggleEnabled={createMore} onCreateMoreToggleChange={handleCreateMoreToggleChange} onSubmit={handleFormSubmit} - projectId={selectedProjectId} + projectId={activeProjectId} /> )} diff --git a/web/store/issue/cycle/issue.store.ts b/web/store/issue/cycle/issue.store.ts index 21fce7a76..87cb50092 100644 --- a/web/store/issue/cycle/issue.store.ts +++ b/web/store/issue/cycle/issue.store.ts @@ -180,9 +180,16 @@ export class CycleIssues extends IssueHelperStore implements ICycleIssues { if (!cycleId) throw new Error("Cycle Id is required"); const response = await this.rootIssueStore.projectIssues.createIssue(workspaceSlug, projectId, data); - const issueToCycle = await this.addIssueToCycle(workspaceSlug, projectId, cycleId, [response.id]); + await this.addIssueToCycle(workspaceSlug, projectId, cycleId, [response.id]); - return issueToCycle; + runInAction(() => { + update(this.issues, cycleId, (cycleIssueIds) => { + if (!cycleIssueIds) return [response.id]; + else return concat(cycleIssueIds, [response.id]); + }); + }); + + return response; } catch (error) { throw error; } @@ -261,15 +268,6 @@ export class CycleIssues extends IssueHelperStore implements ICycleIssues { addIssueToCycle = async (workspaceSlug: string, projectId: string, cycleId: string, issueIds: string[]) => { try { - runInAction(() => { - update(this.issues, cycleId, (cycleIssueIds) => { - if (!cycleIssueIds) return issueIds; - else return concat(cycleIssueIds, issueIds); - }); - }); - - issueIds.map((issueId) => this.rootStore.issues.updateIssue(issueId, { cycle_id: cycleId })); - const issueToCycle = await this.issueService.addIssueToCycle(workspaceSlug, projectId, cycleId, { issues: issueIds, }); diff --git a/web/store/issue/module/issue.store.ts b/web/store/issue/module/issue.store.ts index 80874abe3..d9612107a 100644 --- a/web/store/issue/module/issue.store.ts +++ b/web/store/issue/module/issue.store.ts @@ -173,7 +173,15 @@ export class ModuleIssues extends IssueHelperStore implements IModuleIssues { if (!moduleId) throw new Error("Module Id is required"); const response = await this.rootIssueStore.projectIssues.createIssue(workspaceSlug, projectId, data); - const issueToModule = await this.addIssueToModule(workspaceSlug, projectId, moduleId, [response.id]); + await this.addIssueToModule(workspaceSlug, projectId, moduleId, [response.id]); + + runInAction(() => { + update(this.issues, moduleId, (moduleIssueIds) => { + if (!moduleIssueIds) return [response.id]; + else return concat(moduleIssueIds, [response.id]); + }); + }); + return response; } catch (error) { throw error; @@ -252,15 +260,6 @@ export class ModuleIssues extends IssueHelperStore implements IModuleIssues { addIssueToModule = async (workspaceSlug: string, projectId: string, moduleId: string, issueIds: string[]) => { try { - runInAction(() => { - update(this.issues, moduleId, (moduleIssueIds) => { - if (!moduleIssueIds) return issueIds; - else return concat(moduleIssueIds, issueIds); - }); - }); - - issueIds.map((issueId) => this.rootStore.issues.updateIssue(issueId, { module_id: moduleId })); - const issueToModule = await this.moduleService.addIssuesToModule(workspaceSlug, projectId, moduleId, { issues: issueIds, });