From 02e6439bd5b5b1a50bfd308d778d6b4501c182e2 Mon Sep 17 00:00:00 2001 From: Anmol Singh Bhatia <121005188+anmolsinghbhatia@users.noreply.github.com> Date: Thu, 30 Mar 2023 18:59:53 +0530 Subject: [PATCH] feat: completed cycle transfer issue (#624) * feat: bulk transfer issue for completed cycle added * feat: toast alert added for issue transfer --- apps/app/components/core/issues-view.tsx | 24 ++- apps/app/components/cycles/index.ts | 3 +- .../cycles/transfer-issues-modal.tsx | 153 ++++++++++++++++++ apps/app/components/icons/index.ts | 1 + apps/app/components/icons/transfer-icon.tsx | 16 ++ apps/app/services/cycles.service.ts | 18 +++ 6 files changed, 210 insertions(+), 5 deletions(-) create mode 100644 apps/app/components/cycles/transfer-issues-modal.tsx create mode 100644 apps/app/components/icons/transfer-icon.tsx diff --git a/apps/app/components/core/issues-view.tsx b/apps/app/components/core/issues-view.tsx index 4f20c8f3c..e71eab695 100644 --- a/apps/app/components/core/issues-view.tsx +++ b/apps/app/components/core/issues-view.tsx @@ -18,6 +18,7 @@ import { AllLists, AllBoards, FilterList } from "components/core"; import { CreateUpdateIssueModal, DeleteIssueModal } from "components/issues"; import StrictModeDroppable from "components/dnd/StrictModeDroppable"; import { CreateUpdateViewModal } from "components/views"; +import { TransferIssuesModal } from "components/cycles"; // ui import { EmptySpace, EmptySpaceItem, PrimaryButton, Spinner } from "components/ui"; import { CalendarView } from "./calendar-view"; @@ -28,7 +29,7 @@ import { RectangleStackIcon, TrashIcon, } from "@heroicons/react/24/outline"; -import { ExclamationIcon } from "components/icons"; +import { ExclamationIcon, getStateGroupIcon, TransferIcon } from "components/icons"; // helpers import { getStatesList } from "helpers/state.helper"; // types @@ -82,6 +83,9 @@ export const IssuesView: React.FC = ({ // trash box const [trashBox, setTrashBox] = useState(false); + // transfer issue + const [transferIssuesModal, setTransferIssuesModal] = useState(false); + const router = useRouter(); const { workspaceSlug, projectId, cycleId, moduleId, viewId } = router.query; @@ -406,6 +410,10 @@ export const IssuesView: React.FC = ({ isOpen={deleteIssueModal} data={issueToDelete} /> + setTransferIssuesModal(false)} + isOpen={transferIssuesModal} + />
@@ -460,9 +468,17 @@ export const IssuesView: React.FC = ({ isNotEmpty ? ( <> {isCompleted && ( -
- - Completed cycles are not editable. +
+
+ + Completed cycles are not editable. +
+
+ setTransferIssuesModal(true)} className="flex items-center gap-3 rounded-lg"> + + Transfer Issues + +
)} {issueView === "list" ? ( diff --git a/apps/app/components/cycles/index.ts b/apps/app/components/cycles/index.ts index f137e81eb..33a43c18c 100644 --- a/apps/app/components/cycles/index.ts +++ b/apps/app/components/cycles/index.ts @@ -7,4 +7,5 @@ export * from "./select"; export * from "./sidebar"; export * from "./single-cycle-card"; export * from "./empty-cycle"; -export * from "./date"; +export * from "./transfer-issues-modal"; +export * from "./date"; \ No newline at end of file diff --git a/apps/app/components/cycles/transfer-issues-modal.tsx b/apps/app/components/cycles/transfer-issues-modal.tsx new file mode 100644 index 000000000..5227000d4 --- /dev/null +++ b/apps/app/components/cycles/transfer-issues-modal.tsx @@ -0,0 +1,153 @@ +import React, { useState, useEffect } from "react"; + +import { useRouter } from "next/router"; + +import useSWR from "swr"; + +// component +import { Dialog, Transition } from "@headlessui/react"; +// services +import cyclesService from "services/cycles.service"; +// hooks +import useToast from "hooks/use-toast"; +//icons +import { MagnifyingGlassIcon, XMarkIcon } from "@heroicons/react/24/outline"; +import { ContrastIcon, CyclesIcon } from "components/icons"; +// fetch-key +import { CYCLE_INCOMPLETE_LIST } from "constants/fetch-keys"; +// types +import { ICycle } from "types"; + +type Props = { + isOpen: boolean; + handleClose: () => void; +}; + +export const TransferIssuesModal: React.FC = ({ isOpen, handleClose }) => { + const [query, setQuery] = useState(""); + + const router = useRouter(); + const { workspaceSlug, projectId, cycleId } = router.query; + + const { setToastAlert } = useToast(); + + + const transferIssue = async (payload: any) => { + await cyclesService + .transferIssues(workspaceSlug as string, projectId as string, cycleId as string, payload) + .then((res) => { + setToastAlert({ + type: "success", + title: "Issues transfered successfully", + message: + "Issues have been transferred successfully", + }); + }) + .catch((err) => { + setToastAlert({ + type: "error", + title: "Error!", + message: + "Issues cannot be transfer. Please try again.", + }); + }); + }; + + const { data: incompleteCycles } = useSWR( + workspaceSlug && projectId ? CYCLE_INCOMPLETE_LIST(projectId as string) : null, + workspaceSlug && projectId + ? () => cyclesService.getIncompleteCycles(workspaceSlug as string, projectId as string) + : null + ); + + const filteredOptions = + query === "" + ? incompleteCycles + : incompleteCycles?.filter((option) => + option.name.toLowerCase().includes(query.toLowerCase()) + ); + + useEffect(() => { + const handleKeyDown = (e: KeyboardEvent) => { + if (e.key === "Escape") { + handleClose(); + } + }; + }, [handleClose]); + return ( + + + +
+ + +
+
+ + +
+
+

Transfer Issues

+ +
+
+ + setQuery(e.target.value)} + value={query} + /> +
+
+ {filteredOptions ? ( + filteredOptions.length > 0 ? ( + filteredOptions.map((option: ICycle) => ( + + )) + ) : ( +

No matching results

+ ) + ) : ( +

Loading...

+ )} +
+
+
+
+
+
+
+
+ ); +}; diff --git a/apps/app/components/icons/index.ts b/apps/app/components/icons/index.ts index f55cf763b..a4f95eb4e 100644 --- a/apps/app/components/icons/index.ts +++ b/apps/app/components/icons/index.ts @@ -56,3 +56,4 @@ export * from "./users"; export * from "./import-layers"; export * from "./check"; export * from "./water-drop-icon"; +export * from "./transfer-icon"; \ No newline at end of file diff --git a/apps/app/components/icons/transfer-icon.tsx b/apps/app/components/icons/transfer-icon.tsx new file mode 100644 index 000000000..e6b2e0840 --- /dev/null +++ b/apps/app/components/icons/transfer-icon.tsx @@ -0,0 +1,16 @@ +import React from "react"; + +import type { Props } from "./types"; + +export const TransferIcon: React.FC = ({ width, height, className }) => ( + + + + ); diff --git a/apps/app/services/cycles.service.ts b/apps/app/services/cycles.service.ts index 4c78d90cb..48fc4b335 100644 --- a/apps/app/services/cycles.service.ts +++ b/apps/app/services/cycles.service.ts @@ -210,6 +210,24 @@ class ProjectCycleServices extends APIService { }); } + async transferIssues( + workspaceSlug: string, + projectId: string, + cycleId: string, + data: { + new_cycle_id: string; + } + ): Promise { + return this.post( + `/api/workspaces/${workspaceSlug}/projects/${projectId}/cycles/${cycleId}/transfer-issues/`, + data + ) + .then((response) => response?.data) + .catch((error) => { + throw error?.response?.data; + }); + } + async removeCycleFromFavorites( workspaceSlug: string, projectId: string,