fix: delete mutations for issues, cycles and modules (#634)

This commit is contained in:
Aaryan Khandelwal 2023-03-31 02:17:35 +05:30 committed by GitHub
parent 29ea592c4a
commit 66d07e340b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 131 additions and 75 deletions

View File

@ -26,6 +26,7 @@ import {
LinkIcon, LinkIcon,
PencilIcon, PencilIcon,
TrashIcon, TrashIcon,
XMarkIcon,
} from "@heroicons/react/24/outline"; } from "@heroicons/react/24/outline";
// helpers // helpers
import { copyTextToClipboard, truncateText } from "helpers/string.helper"; import { copyTextToClipboard, truncateText } from "helpers/string.helper";
@ -38,6 +39,7 @@ import {
MODULE_ISSUES_WITH_PARAMS, MODULE_ISSUES_WITH_PARAMS,
PROJECT_ISSUES_LIST_WITH_PARAMS, PROJECT_ISSUES_LIST_WITH_PARAMS,
} from "constants/fetch-keys"; } from "constants/fetch-keys";
import { DIVIDER } from "@blueprintjs/core/lib/esm/common/classes";
type Props = { type Props = {
type?: string; type?: string;
@ -258,27 +260,30 @@ export const SingleListIssue: React.FC<Props> = ({
{type && !isNotAllowed && ( {type && !isNotAllowed && (
<CustomMenu width="auto" ellipsis> <CustomMenu width="auto" ellipsis>
<CustomMenu.MenuItem onClick={editIssue}> <CustomMenu.MenuItem onClick={editIssue}>
<span className="flex items-center justify-start gap-2"> <div className="flex items-center justify-start gap-2">
<PencilIcon className="h-4 w-4" /> <PencilIcon className="h-4 w-4" />
<span>Edit issue</span> <span>Edit issue</span>
</span> </div>
</CustomMenu.MenuItem> </CustomMenu.MenuItem>
{type !== "issue" && removeIssue && ( {type !== "issue" && removeIssue && (
<CustomMenu.MenuItem onClick={removeIssue}> <CustomMenu.MenuItem onClick={removeIssue}>
<>Remove from {type}</> <div className="flex items-center justify-start gap-2">
<XMarkIcon className="h-4 w-4" />
<span>Remove from {type}</span>
</div>
</CustomMenu.MenuItem> </CustomMenu.MenuItem>
)} )}
<CustomMenu.MenuItem onClick={() => handleDeleteIssue(issue)}> <CustomMenu.MenuItem onClick={() => handleDeleteIssue(issue)}>
<span className="flex items-center justify-start gap-2"> <div className="flex items-center justify-start gap-2">
<TrashIcon className="h-4 w-4" /> <TrashIcon className="h-4 w-4" />
<span>Delete issue</span> <span>Delete issue</span>
</span> </div>
</CustomMenu.MenuItem> </CustomMenu.MenuItem>
<CustomMenu.MenuItem onClick={handleCopyText}> <CustomMenu.MenuItem onClick={handleCopyText}>
<span className="flex items-center justify-start gap-2"> <div className="flex items-center justify-start gap-2">
<LinkIcon className="h-4 w-4" /> <LinkIcon className="h-4 w-4" />
<span>Copy issue link</span> <span>Copy issue link</span>
</span> </div>
</CustomMenu.MenuItem> </CustomMenu.MenuItem>
</CustomMenu> </CustomMenu>
)} )}

View File

@ -14,14 +14,25 @@ import { DangerButton, SecondaryButton } from "components/ui";
// icons // icons
import { ExclamationTriangleIcon } from "@heroicons/react/24/outline"; import { ExclamationTriangleIcon } from "@heroicons/react/24/outline";
// types // types
import type { ICycle } from "types"; import type {
CompletedCyclesResponse,
CurrentAndUpcomingCyclesResponse,
DraftCyclesResponse,
ICycle,
} from "types";
type TConfirmCycleDeletionProps = { type TConfirmCycleDeletionProps = {
isOpen: boolean; isOpen: boolean;
setIsOpen: React.Dispatch<React.SetStateAction<boolean>>; setIsOpen: React.Dispatch<React.SetStateAction<boolean>>;
data?: ICycle; data?: ICycle;
}; };
// fetch-keys // fetch-keys
import { CYCLE_LIST } from "constants/fetch-keys"; import {
CYCLE_COMPLETE_LIST,
CYCLE_CURRENT_AND_UPCOMING_LIST,
CYCLE_DRAFT_LIST,
CYCLE_LIST,
} from "constants/fetch-keys";
import { getDateRangeStatus } from "helpers/date-time.helper";
export const DeleteCycleModal: React.FC<TConfirmCycleDeletionProps> = ({ export const DeleteCycleModal: React.FC<TConfirmCycleDeletionProps> = ({
isOpen, isOpen,
@ -31,7 +42,7 @@ export const DeleteCycleModal: React.FC<TConfirmCycleDeletionProps> = ({
const [isDeleteLoading, setIsDeleteLoading] = useState(false); const [isDeleteLoading, setIsDeleteLoading] = useState(false);
const router = useRouter(); const router = useRouter();
const { workspaceSlug } = router.query; const { workspaceSlug, projectId } = router.query;
const { setToastAlert } = useToast(); const { setToastAlert } = useToast();
@ -41,16 +52,68 @@ export const DeleteCycleModal: React.FC<TConfirmCycleDeletionProps> = ({
}; };
const handleDeletion = async () => { const handleDeletion = async () => {
setIsDeleteLoading(true);
if (!data || !workspaceSlug) return; if (!data || !workspaceSlug) return;
setIsDeleteLoading(true);
await cycleService await cycleService
.deleteCycle(workspaceSlug as string, data.project, data.id) .deleteCycle(workspaceSlug as string, data.project, data.id)
.then(() => { .then(() => {
mutate<ICycle[]>( switch (getDateRangeStatus(data.start_date, data.end_date)) {
CYCLE_LIST(data.project), case "completed":
(prevData) => prevData?.filter((cycle) => cycle.id !== data?.id), mutate<CompletedCyclesResponse>(
CYCLE_COMPLETE_LIST(projectId as string),
(prevData) => {
if (!prevData) return;
return {
completed_cycles: prevData.completed_cycles?.filter(
(cycle) => cycle.id !== data?.id
),
};
},
false false
); );
break;
case "current":
mutate<CurrentAndUpcomingCyclesResponse>(
CYCLE_CURRENT_AND_UPCOMING_LIST(projectId as string),
(prevData) => {
if (!prevData) return;
return {
current_cycle: prevData.current_cycle?.filter((c) => c.id !== data?.id),
upcoming_cycle: prevData.upcoming_cycle,
};
},
false
);
break;
case "upcoming":
mutate<CurrentAndUpcomingCyclesResponse>(
CYCLE_CURRENT_AND_UPCOMING_LIST(projectId as string),
(prevData) => {
if (!prevData) return;
return {
current_cycle: prevData.current_cycle,
upcoming_cycle: prevData.upcoming_cycle?.filter((c) => c.id !== data?.id),
};
},
false
);
break;
default:
mutate<DraftCyclesResponse>(
CYCLE_DRAFT_LIST(projectId as string),
(prevData) => {
if (!prevData) return;
return {
draft_cycles: prevData.draft_cycles?.filter((cycle) => cycle.id !== data?.id),
};
},
false
);
}
handleClose(); handleClose();
setToastAlert({ setToastAlert({
@ -59,8 +122,7 @@ export const DeleteCycleModal: React.FC<TConfirmCycleDeletionProps> = ({
message: "Cycle deleted successfully", message: "Cycle deleted successfully",
}); });
}) })
.catch((error) => { .catch(() => {
console.log(error);
setIsDeleteLoading(false); setIsDeleteLoading(false);
}); });
}; };
@ -107,9 +169,8 @@ export const DeleteCycleModal: React.FC<TConfirmCycleDeletionProps> = ({
<div className="mt-2"> <div className="mt-2">
<p className="text-sm text-gray-500"> <p className="text-sm text-gray-500">
Are you sure you want to delete cycle-{" "} Are you sure you want to delete cycle-{" "}
<span className="font-bold">{data?.name}</span> <span className="font-bold">{data?.name}</span>? All of the data related
? All of the data related to the cycle will be permanently removed. to the cycle will be permanently removed. This action cannot be undone.
This action cannot be undone.
</p> </p>
</div> </div>
</div> </div>

View File

@ -1,4 +1,4 @@
import React, { useEffect, useRef, useState } from "react"; import React, { useEffect, useState } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
@ -17,7 +17,15 @@ import { SecondaryButton, DangerButton } from "components/ui";
// types // types
import type { CycleIssueResponse, IIssue, ModuleIssueResponse } from "types"; import type { CycleIssueResponse, IIssue, ModuleIssueResponse } from "types";
// fetch-keys // fetch-keys
import { CYCLE_ISSUES, PROJECT_ISSUES_LIST, MODULE_ISSUES, USER_ISSUE } from "constants/fetch-keys"; import {
CYCLE_ISSUES,
CYCLE_ISSUES_WITH_PARAMS,
MODULE_ISSUES,
MODULE_ISSUES_WITH_PARAMS,
PROJECT_ISSUES_LIST_WITH_PARAMS,
USER_ISSUE,
} from "constants/fetch-keys";
import useIssuesView from "hooks/use-issues-view";
type Props = { type Props = {
isOpen: boolean; isOpen: boolean;
@ -29,7 +37,9 @@ export const DeleteIssueModal: React.FC<Props> = ({ isOpen, handleClose, data })
const [isDeleteLoading, setIsDeleteLoading] = useState(false); const [isDeleteLoading, setIsDeleteLoading] = useState(false);
const router = useRouter(); const router = useRouter();
const { workspaceSlug, projectId: queryProjectId } = router.query; const { workspaceSlug, projectId, cycleId, moduleId } = router.query;
const { params } = useIssuesView();
const { setToastAlert } = useToast(); const { setToastAlert } = useToast();
@ -44,37 +54,14 @@ export const DeleteIssueModal: React.FC<Props> = ({ isOpen, handleClose, data })
const handleDeletion = async () => { const handleDeletion = async () => {
setIsDeleteLoading(true); setIsDeleteLoading(true);
if (!data || !workspaceSlug) return; if (!workspaceSlug || !projectId || !data) return;
const projectId = data.project;
await issueServices await issueServices
.deleteIssue(workspaceSlug as string, projectId, data.id) .deleteIssue(workspaceSlug as string, projectId as string, data.id)
.then(() => { .then(() => {
const cycleId = data?.cycle; if (cycleId) mutate(CYCLE_ISSUES_WITH_PARAMS(cycleId as string, params));
const moduleId = data?.module; else if (moduleId) mutate(MODULE_ISSUES_WITH_PARAMS(moduleId as string, params));
else mutate(PROJECT_ISSUES_LIST_WITH_PARAMS(projectId as string, params));
if (cycleId) {
mutate<CycleIssueResponse[]>(
CYCLE_ISSUES(cycleId),
(prevData) => prevData?.filter((i) => i.issue !== data.id),
false
);
}
if (moduleId) {
mutate<ModuleIssueResponse[]>(
MODULE_ISSUES(moduleId),
(prevData) => prevData?.filter((i) => i.issue !== data.id),
false
);
}
if (!queryProjectId)
mutate<IIssue[]>(
USER_ISSUE(workspaceSlug as string),
(prevData) => prevData?.filter((i) => i.id !== data.id),
false
);
handleClose(); handleClose();
setToastAlert({ setToastAlert({
@ -133,8 +120,8 @@ export const DeleteIssueModal: React.FC<Props> = ({ isOpen, handleClose, data })
Are you sure you want to delete issue{" "} Are you sure you want to delete issue{" "}
<span className="break-all font-semibold"> <span className="break-all font-semibold">
{data?.project_detail.identifier}-{data?.sequence_id} {data?.project_detail.identifier}-{data?.sequence_id}
</span>{""} </span>
? All of the data related to the issue will be permanently removed. This {""}? All of the data related to the issue will be permanently removed. This
action cannot be undone. action cannot be undone.
</p> </p>
</span> </span>

View File

@ -1,4 +1,4 @@
import React, { useEffect, useRef, useState } from "react"; import React, { useState } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
@ -29,7 +29,7 @@ export const DeleteModuleModal: React.FC<Props> = ({ isOpen, setIsOpen, data })
const [isDeleteLoading, setIsDeleteLoading] = useState(false); const [isDeleteLoading, setIsDeleteLoading] = useState(false);
const router = useRouter(); const router = useRouter();
const { workspaceSlug } = router.query; const { workspaceSlug, projectId, moduleId } = router.query;
const { setToastAlert } = useToast(); const { setToastAlert } = useToast();
@ -41,22 +41,26 @@ export const DeleteModuleModal: React.FC<Props> = ({ isOpen, setIsOpen, data })
const handleDeletion = async () => { const handleDeletion = async () => {
setIsDeleteLoading(true); setIsDeleteLoading(true);
if (!workspaceSlug || !data) return; if (!workspaceSlug || !projectId || !data) return;
await modulesService
.deleteModule(workspaceSlug as string, data.project, data.id)
.then(() => {
mutate(MODULE_LIST(data.project));
router.push(`/${workspaceSlug}/projects/${data.project}/modules`);
handleClose();
setToastAlert({ mutate<IModule[]>(
title: "Success", MODULE_LIST(projectId as string),
type: "success", (prevData) => prevData?.filter((m) => m.id !== data.id),
message: "Module deleted successfully", false
}); );
await modulesService
.deleteModule(workspaceSlug as string, projectId as string, data.id)
.then(() => {
if (moduleId) router.push(`/${workspaceSlug}/projects/${data.project}/modules`);
handleClose();
}) })
.catch((error) => { .catch(() => {
console.log(error); setToastAlert({
type: "error",
title: "Error!",
message: "Module could not be deleted. Please try again.",
});
setIsDeleteLoading(false); setIsDeleteLoading(false);
}); });
}; };
@ -103,9 +107,8 @@ export const DeleteModuleModal: React.FC<Props> = ({ isOpen, setIsOpen, data })
<div className="mt-2"> <div className="mt-2">
<p className="text-sm text-gray-500"> <p className="text-sm text-gray-500">
Are you sure you want to delete module-{" "} Are you sure you want to delete module-{" "}
<span className="font-bold">{data?.name}</span> <span className="font-bold">{data?.name}</span>? All of the data related
? All of the data related to the module will be permanently removed. to the module will be permanently removed. This action cannot be undone.
This action cannot be undone.
</p> </p>
</div> </div>
</div> </div>

View File

@ -36,7 +36,7 @@ const useIssuesView = () => {
setFilters, setFilters,
resetFilterToDefault, resetFilterToDefault,
setNewFilterDefaultView, setNewFilterDefaultView,
setIssueView setIssueView,
} = useContext(issueViewContext); } = useContext(issueViewContext);
const router = useRouter(); const router = useRouter();