fix: issue peek-overview delete functionality (#3134)

* fix: peek-overview delete issue

* refactor: removed unused variables
This commit is contained in:
Lakhan Baheti 2023-12-18 12:14:57 +05:30 committed by GitHub
parent 969a51f425
commit 05e7afab8d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 172 additions and 35 deletions

View File

@ -120,8 +120,8 @@ export const BaseCalendarRoot = observer((props: IBaseCalendarRoot) => {
workspaceSlug={workspaceSlug.toString()} workspaceSlug={workspaceSlug.toString()}
projectId={peekProjectId.toString()} projectId={peekProjectId.toString()}
issueId={peekIssueId.toString()} issueId={peekIssueId.toString()}
handleIssue={async (issueToUpdate) => handleIssue={async (issueToUpdate, action: EIssueActions) =>
await handleIssues(issueToUpdate.target_date ?? "", issueToUpdate as IIssue, EIssueActions.UPDATE) await handleIssues(issueToUpdate.target_date ?? "", issueToUpdate as IIssue, action)
} }
/> />
)} )}

View File

@ -1,4 +1,4 @@
import React from "react"; import React, { useCallback } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
// mobx store // mobx store
@ -25,6 +25,8 @@ import {
IViewIssuesStore, IViewIssuesStore,
} from "store/issues"; } from "store/issues";
import { TUnGroupedIssues } from "store/issues/types"; import { TUnGroupedIssues } from "store/issues/types";
import { EIssueActions } from "../types";
// constants
import { EUserWorkspaceRoles } from "constants/workspace"; import { EUserWorkspaceRoles } from "constants/workspace";
interface IBaseGanttRoot { interface IBaseGanttRoot {
@ -35,10 +37,15 @@ interface IBaseGanttRoot {
| IViewIssuesFilterStore; | IViewIssuesFilterStore;
issueStore: IProjectIssuesStore | IModuleIssuesStore | ICycleIssuesStore | IViewIssuesStore; issueStore: IProjectIssuesStore | IModuleIssuesStore | ICycleIssuesStore | IViewIssuesStore;
viewId?: string; viewId?: string;
issueActions: {
[EIssueActions.DELETE]: (issue: IIssue) => Promise<void>;
[EIssueActions.UPDATE]?: (issue: IIssue) => Promise<void>;
[EIssueActions.REMOVE]?: (issue: IIssue) => Promise<void>;
};
} }
export const BaseGanttRoot: React.FC<IBaseGanttRoot> = observer((props: IBaseGanttRoot) => { export const BaseGanttRoot: React.FC<IBaseGanttRoot> = observer((props: IBaseGanttRoot) => {
const { issueFiltersStore, issueStore, viewId } = props; const { issueFiltersStore, issueStore, viewId, issueActions } = props;
const router = useRouter(); const router = useRouter();
const { workspaceSlug, peekIssueId, peekProjectId } = router.query; const { workspaceSlug, peekIssueId, peekProjectId } = router.query;
@ -64,11 +71,14 @@ export const BaseGanttRoot: React.FC<IBaseGanttRoot> = observer((props: IBaseGan
await issueStore.updateIssue(workspaceSlug.toString(), issue.project, issue.id, payload, viewId); await issueStore.updateIssue(workspaceSlug.toString(), issue.project, issue.id, payload, viewId);
}; };
const updateIssue = async (projectId: string, issueId: string, payload: Partial<IIssue>) => { const handleIssues = useCallback(
if (!workspaceSlug) return; async (issue: IIssue, action: EIssueActions) => {
if (issueActions[action]) {
await issueStore.updateIssue(workspaceSlug.toString(), projectId, issueId, payload, viewId); await issueActions[action]!(issue);
}; }
},
[issueActions]
);
const isAllowed = !!currentProjectRole && currentProjectRole >= EUserWorkspaceRoles.MEMBER; const isAllowed = !!currentProjectRole && currentProjectRole >= EUserWorkspaceRoles.MEMBER;
@ -102,8 +112,8 @@ export const BaseGanttRoot: React.FC<IBaseGanttRoot> = observer((props: IBaseGan
workspaceSlug={workspaceSlug.toString()} workspaceSlug={workspaceSlug.toString()}
projectId={peekProjectId.toString()} projectId={peekProjectId.toString()}
issueId={peekIssueId.toString()} issueId={peekIssueId.toString()}
handleIssue={async (issueToUpdate) => { handleIssue={async (issueToUpdate, action) => {
await updateIssue(peekProjectId.toString(), peekIssueId.toString(), issueToUpdate); await handleIssues(issueToUpdate as IIssue, action);
}} }}
/> />
)} )}

View File

@ -4,15 +4,43 @@ import { useMobxStore } from "lib/mobx/store-provider";
// components // components
import { BaseGanttRoot } from "./base-gantt-root"; import { BaseGanttRoot } from "./base-gantt-root";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
// types
import { EIssueActions } from "../types";
import { IIssue } from "types";
export const CycleGanttLayout: React.FC = observer(() => { export const CycleGanttLayout: React.FC = observer(() => {
const router = useRouter(); const router = useRouter();
const { cycleId } = router.query; const { cycleId, workspaceSlug } = router.query;
const { cycleIssues: cycleIssueStore, cycleIssuesFilter: cycleIssueFilterStore } = useMobxStore(); const { cycleIssues: cycleIssueStore, cycleIssuesFilter: cycleIssueFilterStore } = useMobxStore();
const issueActions = {
[EIssueActions.UPDATE]: async (issue: IIssue) => {
if (!workspaceSlug || !cycleId) return;
await cycleIssueStore.updateIssue(workspaceSlug.toString(), issue.project, issue.id, issue, cycleId.toString());
},
[EIssueActions.DELETE]: async (issue: IIssue) => {
if (!workspaceSlug || !cycleId) return;
await cycleIssueStore.removeIssue(workspaceSlug.toString(), issue.project, issue.id, cycleId.toString());
},
[EIssueActions.REMOVE]: async (issue: IIssue) => {
if (!workspaceSlug || !cycleId || !issue.bridge_id) return;
await cycleIssueStore.removeIssueFromCycle(
workspaceSlug.toString(),
issue.project,
cycleId.toString(),
issue.id,
issue.bridge_id
);
},
};
return ( return (
<BaseGanttRoot <BaseGanttRoot
issueActions={issueActions}
issueFiltersStore={cycleIssueFilterStore} issueFiltersStore={cycleIssueFilterStore}
issueStore={cycleIssueStore} issueStore={cycleIssueStore}
viewId={cycleId?.toString()} viewId={cycleId?.toString()}

View File

@ -4,15 +4,43 @@ import { useMobxStore } from "lib/mobx/store-provider";
// components // components
import { BaseGanttRoot } from "./base-gantt-root"; import { BaseGanttRoot } from "./base-gantt-root";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
// types
import { EIssueActions } from "../types";
import { IIssue } from "types";
export const ModuleGanttLayout: React.FC = observer(() => { export const ModuleGanttLayout: React.FC = observer(() => {
const router = useRouter(); const router = useRouter();
const { moduleId } = router.query; const { moduleId, workspaceSlug } = router.query;
const { moduleIssues: moduleIssueStore, moduleIssuesFilter: moduleIssueFilterStore } = useMobxStore(); const { moduleIssues: moduleIssueStore, moduleIssuesFilter: moduleIssueFilterStore } = useMobxStore();
const issueActions = {
[EIssueActions.UPDATE]: async (issue: IIssue) => {
if (!workspaceSlug || !moduleId) return;
await moduleIssueStore.updateIssue(workspaceSlug.toString(), issue.project, issue.id, issue, moduleId.toString());
},
[EIssueActions.DELETE]: async (issue: IIssue) => {
if (!workspaceSlug || !moduleId) return;
await moduleIssueStore.removeIssue(workspaceSlug.toString(), issue.project, issue.id, moduleId.toString());
},
[EIssueActions.REMOVE]: async (issue: IIssue) => {
if (!workspaceSlug || !moduleId || !issue.bridge_id) return;
await moduleIssueStore.removeIssueFromModule(
workspaceSlug.toString(),
issue.project,
moduleId.toString(),
issue.id,
issue.bridge_id
);
},
};
return ( return (
<BaseGanttRoot <BaseGanttRoot
issueActions={issueActions}
issueFiltersStore={moduleIssueFilterStore} issueFiltersStore={moduleIssueFilterStore}
issueStore={moduleIssueStore} issueStore={moduleIssueStore}
viewId={moduleId?.toString()} viewId={moduleId?.toString()}

View File

@ -1,12 +1,36 @@
import React from "react"; import React from "react";
import { useRouter } from "next/router";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
// hooks // hooks
import { useMobxStore } from "lib/mobx/store-provider"; import { useMobxStore } from "lib/mobx/store-provider";
// components // components
import { BaseGanttRoot } from "./base-gantt-root"; import { BaseGanttRoot } from "./base-gantt-root";
// types
import { EIssueActions } from "../types";
import { IIssue } from "types";
export const GanttLayout: React.FC = observer(() => { export const GanttLayout: React.FC = observer(() => {
const { projectIssues: projectIssuesStore, projectIssuesFilter: projectIssueFiltersStore } = useMobxStore(); const { projectIssues: projectIssuesStore, projectIssuesFilter: projectIssueFiltersStore } = useMobxStore();
const router = useRouter();
const { workspaceSlug } = router.query;
return <BaseGanttRoot issueFiltersStore={projectIssueFiltersStore} issueStore={projectIssuesStore} />; const issueActions = {
[EIssueActions.UPDATE]: async (issue: IIssue) => {
if (!workspaceSlug) return;
await projectIssuesStore.updateIssue(workspaceSlug.toString(), issue.project, issue.id, issue);
},
[EIssueActions.DELETE]: async (issue: IIssue) => {
if (!workspaceSlug) return;
await projectIssuesStore.removeIssue(workspaceSlug.toString(), issue.project, issue.id);
},
};
return (
<BaseGanttRoot
issueActions={issueActions}
issueFiltersStore={projectIssueFiltersStore}
issueStore={projectIssuesStore}
/>
);
}); });

View File

@ -1,11 +1,35 @@
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import { useRouter } from "next/router";
// mobx store // mobx store
import { useMobxStore } from "lib/mobx/store-provider"; import { useMobxStore } from "lib/mobx/store-provider";
// components // components
import { BaseGanttRoot } from "./base-gantt-root"; import { BaseGanttRoot } from "./base-gantt-root";
// types
import { EIssueActions } from "../types";
import { IIssue } from "types";
export const ProjectViewGanttLayout: React.FC = observer(() => { export const ProjectViewGanttLayout: React.FC = observer(() => {
const { viewIssues: projectIssueViewStore, viewIssuesFilter: projectIssueViewFiltersStore } = useMobxStore(); const { viewIssues: projectIssueViewStore, viewIssuesFilter: projectIssueViewFiltersStore } = useMobxStore();
const router = useRouter();
const { workspaceSlug } = router.query;
return <BaseGanttRoot issueFiltersStore={projectIssueViewFiltersStore} issueStore={projectIssueViewStore} />; const issueActions = {
[EIssueActions.UPDATE]: async (issue: IIssue) => {
if (!workspaceSlug) return;
await projectIssueViewStore.updateIssue(workspaceSlug.toString(), issue.project, issue.id, issue);
},
[EIssueActions.DELETE]: async (issue: IIssue) => {
if (!workspaceSlug) return;
await projectIssueViewStore.removeIssue(workspaceSlug.toString(), issue.project, issue.id);
},
};
return (
<BaseGanttRoot
issueActions={issueActions}
issueFiltersStore={projectIssueViewFiltersStore}
issueStore={projectIssueViewStore}
/>
);
}); });

View File

@ -346,8 +346,8 @@ export const BaseKanBanRoot: React.FC<IBaseKanBanLayout> = observer((props: IBas
workspaceSlug={workspaceSlug.toString()} workspaceSlug={workspaceSlug.toString()}
projectId={peekProjectId.toString()} projectId={peekProjectId.toString()}
issueId={peekIssueId.toString()} issueId={peekIssueId.toString()}
handleIssue={async (issueToUpdate) => handleIssue={async (issueToUpdate, action: EIssueActions) =>
await handleIssues(sub_group_by, group_by, issueToUpdate as IIssue, EIssueActions.UPDATE) await handleIssues(sub_group_by, group_by, issueToUpdate as IIssue, action)
} }
/> />
)} )}

View File

@ -168,7 +168,9 @@ export const BaseListRoot = observer((props: IBaseListRoot) => {
workspaceSlug={workspaceSlug.toString()} workspaceSlug={workspaceSlug.toString()}
projectId={peekProjectId.toString()} projectId={peekProjectId.toString()}
issueId={peekIssueId.toString()} issueId={peekIssueId.toString()}
handleIssue={async (issueToUpdate) => await handleIssues(issueToUpdate as IIssue, EIssueActions.UPDATE)} handleIssue={async (issueToUpdate, action: EIssueActions) =>
await handleIssues(issueToUpdate as IIssue, action)
}
/> />
)} )}
</> </>

View File

@ -194,7 +194,7 @@ export const SpreadsheetView: React.FC<Props> = observer((props) => {
workspaceSlug={workspaceSlug.toString()} workspaceSlug={workspaceSlug.toString()}
projectId={peekProjectId.toString()} projectId={peekProjectId.toString()}
issueId={peekIssueId.toString()} issueId={peekIssueId.toString()}
handleIssue={async (issueToUpdate: any) => await handleIssues(issueToUpdate, EIssueActions.UPDATE)} handleIssue={async (issueToUpdate: any, action: EIssueActions) => await handleIssues(issueToUpdate, action)}
/> />
)} )}
</div> </div>

View File

@ -11,6 +11,7 @@ import { IssueView } from "components/issues";
import { copyUrlToClipboard } from "helpers/string.helper"; import { copyUrlToClipboard } from "helpers/string.helper";
// types // types
import { IIssue, IIssueLink } from "types"; import { IIssue, IIssueLink } from "types";
import { EIssueActions } from "../issue-layouts/types";
// constants // constants
import { EUserWorkspaceRoles } from "constants/workspace"; import { EUserWorkspaceRoles } from "constants/workspace";
@ -18,7 +19,7 @@ interface IIssuePeekOverview {
workspaceSlug: string; workspaceSlug: string;
projectId: string; projectId: string;
issueId: string; issueId: string;
handleIssue: (issue: Partial<IIssue>) => void; handleIssue: (issue: Partial<IIssue>, action: EIssueActions) => Promise<void>;
isArchived?: boolean; isArchived?: boolean;
children?: ReactNode; children?: ReactNode;
} }
@ -31,7 +32,6 @@ export const IssuePeekOverview: FC<IIssuePeekOverview> = observer((props) => {
const { const {
user: { currentProjectRole }, user: { currentProjectRole },
issue: { removeIssueFromStructure },
issueDetail: { issueDetail: {
createIssueComment, createIssueComment,
updateIssueComment, updateIssueComment,
@ -98,7 +98,7 @@ export const IssuePeekOverview: FC<IIssuePeekOverview> = observer((props) => {
const issueUpdate = async (_data: Partial<IIssue>) => { const issueUpdate = async (_data: Partial<IIssue>) => {
if (handleIssue) { if (handleIssue) {
await handleIssue(_data); await handleIssue(_data, EIssueActions.UPDATE);
fetchIssueActivity(workspaceSlug, projectId, issueId); fetchIssueActivity(workspaceSlug, projectId, issueId);
} }
}; };
@ -133,7 +133,7 @@ export const IssuePeekOverview: FC<IIssuePeekOverview> = observer((props) => {
const handleDeleteIssue = async () => { const handleDeleteIssue = async () => {
if (isArchived) await deleteArchivedIssue(workspaceSlug, projectId, issue!); if (isArchived) await deleteArchivedIssue(workspaceSlug, projectId, issue!);
else removeIssueFromStructure(workspaceSlug, projectId, issue!); else await handleIssue(issue!, EIssueActions.DELETE);
const { query } = router; const { query } = router;
if (query.peekIssueId) { if (query.peekIssueId) {
setPeekId(null); setPeekId(null);

View File

@ -10,6 +10,7 @@ import { CustomMenu, Tooltip } from "@plane/ui";
// types // types
import { IUser, IIssue } from "types"; import { IUser, IIssue } from "types";
import { ISubIssuesRootLoaders, ISubIssuesRootLoadersHandler } from "./root"; import { ISubIssuesRootLoaders, ISubIssuesRootLoadersHandler } from "./root";
import { EIssueActions } from "../issue-layouts/types";
export interface ISubIssues { export interface ISubIssues {
workspaceSlug: string; workspaceSlug: string;
@ -29,6 +30,7 @@ export interface ISubIssues {
issue?: IIssue | null issue?: IIssue | null
) => void; ) => void;
handleUpdateIssue: (issue: IIssue, data: Partial<IIssue>) => void; handleUpdateIssue: (issue: IIssue, data: Partial<IIssue>) => void;
handleDeleteIssue: (issue: IIssue) => Promise<void>;
} }
export const SubIssues: React.FC<ISubIssues> = ({ export const SubIssues: React.FC<ISubIssues> = ({
@ -45,6 +47,7 @@ export const SubIssues: React.FC<ISubIssues> = ({
copyText, copyText,
handleIssueCrudOperation, handleIssueCrudOperation,
handleUpdateIssue, handleUpdateIssue,
handleDeleteIssue,
}) => { }) => {
const router = useRouter(); const router = useRouter();
const { peekProjectId, peekIssueId } = router.query; const { peekProjectId, peekIssueId } = router.query;
@ -69,7 +72,13 @@ export const SubIssues: React.FC<ISubIssues> = ({
workspaceSlug={workspaceSlug} workspaceSlug={workspaceSlug}
projectId={peekProjectId.toString()} projectId={peekProjectId.toString()}
issueId={peekIssueId.toString()} issueId={peekIssueId.toString()}
handleIssue={async (issueToUpdate) => await handleUpdateIssue(issue, { ...issue, ...issueToUpdate })} handleIssue={async (issueToUpdate, action) => {
if (action === EIssueActions.UPDATE) {
await handleUpdateIssue(issue, { ...issue, ...issueToUpdate });
} else if (action === EIssueActions.DELETE) {
await handleDeleteIssue(issue);
}
}}
/> />
)} )}
<div> <div>
@ -180,6 +189,7 @@ export const SubIssues: React.FC<ISubIssues> = ({
{issuesLoader.visibility.includes(issue?.id) && issue?.sub_issues_count > 0 && ( {issuesLoader.visibility.includes(issue?.id) && issue?.sub_issues_count > 0 && (
<SubIssuesRootList <SubIssuesRootList
handleDeleteIssue={handleDeleteIssue}
workspaceSlug={workspaceSlug} workspaceSlug={workspaceSlug}
projectId={projectId} projectId={projectId}
parentIssue={issue} parentIssue={issue}

View File

@ -27,6 +27,7 @@ export interface ISubIssuesRootList {
issue?: IIssue | null issue?: IIssue | null
) => void; ) => void;
handleUpdateIssue: (issue: IIssue, data: Partial<IIssue>) => void; handleUpdateIssue: (issue: IIssue, data: Partial<IIssue>) => void;
handleDeleteIssue: (issue: IIssue) => Promise<void>
} }
const issueService = new IssueService(); const issueService = new IssueService();
@ -44,6 +45,7 @@ export const SubIssuesRootList: React.FC<ISubIssuesRootList> = ({
copyText, copyText,
handleIssueCrudOperation, handleIssueCrudOperation,
handleUpdateIssue, handleUpdateIssue,
handleDeleteIssue
}) => { }) => {
const { data: issues, isLoading } = useSWR( const { data: issues, isLoading } = useSWR(
workspaceSlug && projectId && parentIssue && parentIssue?.id ? SUB_ISSUES(parentIssue?.id) : null, workspaceSlug && projectId && parentIssue && parentIssue?.id ? SUB_ISSUES(parentIssue?.id) : null,
@ -70,6 +72,7 @@ export const SubIssuesRootList: React.FC<ISubIssuesRootList> = ({
issues.sub_issues.length > 0 && issues.sub_issues.length > 0 &&
issues.sub_issues.map((issue: IIssue) => ( issues.sub_issues.map((issue: IIssue) => (
<SubIssues <SubIssues
handleDeleteIssue={handleDeleteIssue}
key={`${issue?.id}`} key={`${issue?.id}`}
workspaceSlug={workspaceSlug} workspaceSlug={workspaceSlug}
projectId={projectId} projectId={projectId}

View File

@ -176,6 +176,16 @@ export const SubIssuesRoot: React.FC<ISubIssuesRoot> = observer((props) => {
[updateIssueStructure, projectId, updateIssue, user, workspaceSlug] [updateIssueStructure, projectId, updateIssue, user, workspaceSlug]
); );
const handleDeleteIssue = useCallback(
async (issue: IIssue) => {
if (!workspaceSlug || !projectId || !user) return;
await removeIssue(workspaceSlug.toString(), projectId.toString(), issue.id);
await mutate(SUB_ISSUES(parentIssue?.id));
},
[removeIssue, projectId, user, workspaceSlug, parentIssue?.id]
);
const isEditable = !!currentProjectRole && currentProjectRole >= EUserWorkspaceRoles.MEMBER; const isEditable = !!currentProjectRole && currentProjectRole >= EUserWorkspaceRoles.MEMBER;
const mutateSubIssues = (parentIssueId: string | null) => { const mutateSubIssues = (parentIssueId: string | null) => {
@ -236,6 +246,7 @@ export const SubIssuesRoot: React.FC<ISubIssuesRoot> = observer((props) => {
{issuesLoader.visibility.includes(parentIssue?.id) && workspaceSlug && projectId && ( {issuesLoader.visibility.includes(parentIssue?.id) && workspaceSlug && projectId && (
<div className="border border-b-0 border-custom-border-100"> <div className="border border-b-0 border-custom-border-100">
<SubIssuesRootList <SubIssuesRootList
handleDeleteIssue={handleDeleteIssue}
workspaceSlug={workspaceSlug.toString()} workspaceSlug={workspaceSlug.toString()}
projectId={projectId.toString()} projectId={projectId.toString()}
parentIssue={parentIssue} parentIssue={parentIssue}

View File

@ -82,7 +82,7 @@ const PageDetailsPage: NextPageWithLayout = observer(() => {
description_html: newDescription, description_html: newDescription,
}) })
.then(() => { .then(() => {
mutatePageDetails((prevData) => ({ ...prevData, description_html: newDescription }) as IPage, false); mutatePageDetails((prevData) => ({ ...prevData, description_html: newDescription } as IPage), false);
}); });
}; };
@ -162,15 +162,12 @@ const PageDetailsPage: NextPageWithLayout = observer(() => {
}, [pageDetails?.description_html]); // TODO: Verify the exhaustive-deps warning }, [pageDetails?.description_html]); // TODO: Verify the exhaustive-deps warning
function createObjectFromArray(keys: string[], options: any): any { function createObjectFromArray(keys: string[], options: any): any {
return keys.reduce( return keys.reduce((obj, key) => {
(obj, key) => { if (options[key] !== undefined) {
if (options[key] !== undefined) { obj[key] = options[key];
obj[key] = options[key]; }
} return obj;
return obj; }, {} as { [key: string]: any });
},
{} as { [key: string]: any }
);
} }
const mutatePageDetailsHelper = ( const mutatePageDetailsHelper = (
@ -499,7 +496,7 @@ const PageDetailsPage: NextPageWithLayout = observer(() => {
projectId={projectId as string} projectId={projectId as string}
issueId={peekIssueId ? (peekIssueId as string) : ""} issueId={peekIssueId ? (peekIssueId as string) : ""}
isArchived={false} isArchived={false}
handleIssue={(issueToUpdate) => { handleIssue={async (issueToUpdate, action) => {
if (peekIssueId && typeof peekIssueId === "string") { if (peekIssueId && typeof peekIssueId === "string") {
handleUpdateIssue(peekIssueId, issueToUpdate); handleUpdateIssue(peekIssueId, issueToUpdate);
} }