diff --git a/apps/app/components/core/existing-issues-list-modal.tsx b/apps/app/components/core/existing-issues-list-modal.tsx index 15a313cb0d5..77116d87482 100644 --- a/apps/app/components/core/existing-issues-list-modal.tsx +++ b/apps/app/components/core/existing-issues-list-modal.tsx @@ -20,7 +20,6 @@ type FormInput = { type Props = { isOpen: boolean; handleClose: () => void; - type: string; issues: IIssue[]; handleOnSubmit: any; }; @@ -30,7 +29,6 @@ export const ExistingIssuesListModal: React.FC = ({ handleClose: onClose, issues, handleOnSubmit, - type, }) => { const [query, setQuery] = useState(""); @@ -132,7 +130,7 @@ export const ExistingIssuesListModal: React.FC = ({
  • {query === "" && (

    - Select issues to add to {type} + Select issues to add

    )}
      @@ -203,7 +201,7 @@ export const ExistingIssuesListModal: React.FC = ({ onClick={handleSubmit(onSubmit)} disabled={isSubmitting} > - {isSubmitting ? "Adding..." : `Add to ${type}`} + {isSubmitting ? "Adding..." : "Add selected issues"} )} diff --git a/apps/app/components/issues/index.ts b/apps/app/components/issues/index.ts index ab62034b5bd..18253c6d3fd 100644 --- a/apps/app/components/issues/index.ts +++ b/apps/app/components/issues/index.ts @@ -9,4 +9,3 @@ export * from "./my-issues-list-item"; export * from "./parent-issues-list-modal"; export * from "./sidebar"; export * from "./sub-issues-list"; -export * from "./sub-issues-list-modal"; diff --git a/apps/app/components/issues/sub-issues-list-modal.tsx b/apps/app/components/issues/sub-issues-list-modal.tsx deleted file mode 100644 index ac3bc759776..00000000000 --- a/apps/app/components/issues/sub-issues-list-modal.tsx +++ /dev/null @@ -1,205 +0,0 @@ -import React, { useState } from "react"; - -import { useRouter } from "next/router"; - -import useSWR, { mutate } from "swr"; - -// headless ui -import { Combobox, Dialog, Transition } from "@headlessui/react"; -// icons -import { RectangleStackIcon, MagnifyingGlassIcon } from "@heroicons/react/24/outline"; -// services -import issuesServices from "services/issues.service"; -// helpers -import { orderArrayBy } from "helpers/array.helper"; -// types -import { IIssue, IssueResponse } from "types"; -// constants -import { PROJECT_ISSUES_LIST, SUB_ISSUES } from "constants/fetch-keys"; - -type Props = { - isOpen: boolean; - handleClose: () => void; - parent: IIssue | undefined; -}; - -export const SubIssuesListModal: React.FC = ({ isOpen, handleClose, parent }) => { - const [query, setQuery] = useState(""); - - const router = useRouter(); - const { workspaceSlug, projectId } = router.query; - - const { data: issues } = useSWR( - workspaceSlug && projectId - ? PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string) - : null, - workspaceSlug && projectId - ? () => issuesServices.getIssues(workspaceSlug as string, projectId as string) - : null - ); - - const filteredIssues: IIssue[] = - query === "" - ? issues?.results ?? [] - : issues?.results.filter((issue) => issue.name.toLowerCase().includes(query.toLowerCase())) ?? - []; - - const handleModalClose = () => { - handleClose(); - setQuery(""); - }; - - const addAsSubIssue = (issue: IIssue) => { - if (!workspaceSlug || !projectId) return; - - mutate( - SUB_ISSUES(parent?.id ?? ""), - (prevData) => { - let newSubIssues = [...(prevData as IIssue[])]; - newSubIssues.push(issue); - - newSubIssues = orderArrayBy(newSubIssues, "created_at", "descending"); - - return newSubIssues; - }, - false - ); - - issuesServices - .patchIssue(workspaceSlug as string, projectId as string, issue.id, { parent: parent?.id }) - .then((res) => { - mutate(SUB_ISSUES(parent?.id ?? "")); - mutate( - PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string), - (prevData) => ({ - ...(prevData as IssueResponse), - results: (prevData?.results ?? []).map((p) => { - if (p.id === res.id) - return { - ...p, - ...res, - }; - - return p; - }), - }), - false - ); - }) - .catch((e) => { - console.log(e); - }); - }; - - return ( - setQuery("")} appear> - - -
      - - -
      - - - -
      -
      - - - {filteredIssues.length > 0 && ( - <> -
    • - {query === "" && ( -

      - Issues -

      - )} -
        - {filteredIssues.map((issue) => { - if ( - (issue.parent === "" || issue.parent === null) && // issue does not have any other parent - issue.id !== parent?.id && // issue is not itself - issue.id !== parent?.parent // issue is not it's parent - ) - return ( - - `flex cursor-pointer select-none items-center gap-2 rounded-md px-3 py-2 ${ - active ? "bg-gray-900 bg-opacity-5 text-gray-900" : "" - }` - } - onClick={() => { - addAsSubIssue(issue); - handleClose(); - }} - > - - - {issue.project_detail.identifier}-{issue.sequence_id} - - {issue.name} - - ); - })} -
      -
    • - - )} -
      - - {query !== "" && filteredIssues.length === 0 && ( -
      -
      - )} -
      -
      -
      -
      -
      -
      - ); -}; diff --git a/apps/app/components/issues/sub-issues-list.tsx b/apps/app/components/issues/sub-issues-list.tsx index 94a45a5712a..a274e51eb88 100644 --- a/apps/app/components/issues/sub-issues-list.tsx +++ b/apps/app/components/issues/sub-issues-list.tsx @@ -10,11 +10,14 @@ import { Disclosure, Transition } from "@headlessui/react"; // services import issuesService from "services/issues.service"; // components -import { CreateUpdateIssueModal, SubIssuesListModal } from "components/issues"; +import { ExistingIssuesListModal } from "components/core"; +import { CreateUpdateIssueModal } from "components/issues"; // ui import { CustomMenu } from "components/ui"; // icons import { ChevronRightIcon, PlusIcon } from "@heroicons/react/24/outline"; +// helpers +import { orderArrayBy } from "helpers/array.helper"; // types import { IIssue, IssueResponse, UserAuth } from "types"; // fetch-keys @@ -42,6 +45,65 @@ export const SubIssuesList: FC = ({ parentIssue, userAuth }) : null ); + const { data: issues } = useSWR( + workspaceSlug && projectId + ? PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string) + : null, + workspaceSlug && projectId + ? () => issuesService.getIssues(workspaceSlug as string, projectId as string) + : null + ); + + const addAsSubIssue = async (data: { issues: string[] }) => { + if (!workspaceSlug || !projectId) return; + + await issuesService + .addSubIssues(workspaceSlug as string, projectId as string, parentIssue?.id ?? "", { + sub_issue_ids: data.issues, + }) + .then((res) => { + mutate( + SUB_ISSUES(parentIssue?.id ?? ""), + (prevData) => { + let newSubIssues = [...(prevData as IIssue[])]; + + data.issues.forEach((issueId: string) => { + const issue = issues?.results.find((i) => i.id === issueId); + + if (issue) newSubIssues.push(issue); + }); + + newSubIssues = orderArrayBy(newSubIssues, "created_at", "descending"); + + return newSubIssues; + }, + false + ); + + mutate( + PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string), + (prevData) => ({ + ...(prevData as IssueResponse), + results: (prevData?.results ?? []).map((p) => { + if (data.issues.includes(p.id)) + return { + ...p, + parent: parentIssue.id, + }; + + return p; + }), + }), + false + ); + + mutate(PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string)); + }) + .catch((err) => { + console.log(err); + }); + }; + const handleSubIssueRemove = (issueId: string) => { if (!workspaceSlug || !projectId) return; @@ -94,10 +156,18 @@ export const SubIssuesList: FC = ({ parentIssue, userAuth }) prePopulateData={{ ...preloadedData }} handleClose={() => setCreateIssueModal(false)} /> - setSubIssuesListModal(false)} - parent={parentIssue} + issues={ + issues?.results.filter( + (i) => + (i.parent === "" || i.parent === null) && + i.id !== parentIssue?.id && + i.id !== parentIssue?.parent + ) ?? [] + } + handleOnSubmit={addAsSubIssue} /> {subIssues && subIssues.length > 0 ? ( diff --git a/apps/app/pages/[workspaceSlug]/projects/[projectId]/cycles/[cycleId].tsx b/apps/app/pages/[workspaceSlug]/projects/[projectId]/cycles/[cycleId].tsx index cc3a2c805d4..31f8ebb2d95 100644 --- a/apps/app/pages/[workspaceSlug]/projects/[projectId]/cycles/[cycleId].tsx +++ b/apps/app/pages/[workspaceSlug]/projects/[projectId]/cycles/[cycleId].tsx @@ -118,7 +118,6 @@ const SingleCycle: React.FC = (props) => { setCycleIssuesListModal(false)} - type="cycle" issues={issues?.results.filter((i) => !i.issue_cycle) ?? []} handleOnSubmit={handleAddIssuesToCycle} /> diff --git a/apps/app/pages/[workspaceSlug]/projects/[projectId]/modules/[moduleId].tsx b/apps/app/pages/[workspaceSlug]/projects/[projectId]/modules/[moduleId].tsx index bf97a69881b..948a4b84ff7 100644 --- a/apps/app/pages/[workspaceSlug]/projects/[projectId]/modules/[moduleId].tsx +++ b/apps/app/pages/[workspaceSlug]/projects/[projectId]/modules/[moduleId].tsx @@ -113,7 +113,6 @@ const SingleModule: React.FC = (props) => { setModuleIssuesListModal(false)} - type="module" issues={issues?.results.filter((i) => !i.issue_module) ?? []} handleOnSubmit={handleAddIssuesToModule} /> diff --git a/apps/app/services/issues.service.ts b/apps/app/services/issues.service.ts index 97c104238be..64e63f54599 100644 --- a/apps/app/services/issues.service.ts +++ b/apps/app/services/issues.service.ts @@ -277,6 +277,22 @@ class ProjectIssuesServices extends APIService { throw error?.response?.data; }); } + + async addSubIssues( + workspaceSlug: string, + projectId: string, + issueId: string, + data: { sub_issue_ids: string[] } + ): Promise { + return this.post( + `/api/workspaces/${workspaceSlug}/projects/${projectId}/issues/${issueId}/sub-issues/`, + data + ) + .then((response) => response?.data) + .catch((error) => { + throw error?.response?.data; + }); + } } export default new ProjectIssuesServices();