import { FC, useState } from "react"; import Link from "next/link"; import { useRouter } from "next/router"; import useSWR, { mutate } from "swr"; // headless ui import { Disclosure, Transition } from "@headlessui/react"; // services import issuesService from "services/issues.service"; // contexts import { useProjectMyMembership } from "contexts/project-member.context"; // components import { ExistingIssuesListModal } from "components/core"; import { CreateUpdateIssueModal } from "components/issues"; // ui import { CustomMenu } from "components/ui"; // icons import { ChevronRightIcon, PlusIcon, XMarkIcon } from "@heroicons/react/24/outline"; // types import { ICurrentUserResponse, IIssue, ISearchIssueResponse, ISubIssueResponse } from "types"; // fetch-keys import { SUB_ISSUES } from "constants/fetch-keys"; type Props = { parentIssue: IIssue; user: ICurrentUserResponse | undefined; disabled?: boolean; }; export const SubIssuesList: FC = ({ parentIssue, user, disabled = false }) => { // states const [createIssueModal, setCreateIssueModal] = useState(false); const [subIssuesListModal, setSubIssuesListModal] = useState(false); const [preloadedData, setPreloadedData] = useState | null>(null); const router = useRouter(); const { workspaceSlug } = router.query; const { memberRole } = useProjectMyMembership(); const { data: subIssuesResponse } = useSWR( workspaceSlug && parentIssue ? SUB_ISSUES(parentIssue.id) : null, workspaceSlug && parentIssue ? () => issuesService.subIssues(workspaceSlug as string, parentIssue.project, parentIssue.id) : null ); const addAsSubIssue = async (data: ISearchIssueResponse[]) => { if (!workspaceSlug || !parentIssue) return; const payload = { sub_issue_ids: data.map((i) => i.id), }; await issuesService .addSubIssues(workspaceSlug as string, parentIssue.project, parentIssue.id, payload) .finally(() => mutate(SUB_ISSUES(parentIssue.id))); }; const handleSubIssueRemove = (issue: IIssue) => { if (!workspaceSlug || !parentIssue) return; mutate( SUB_ISSUES(parentIssue.id), (prevData) => { if (!prevData) return prevData; const stateDistribution = { ...prevData.state_distribution }; const issueGroup = issue.state_detail.group; stateDistribution[issueGroup] = stateDistribution[issueGroup] - 1; return { state_distribution: stateDistribution, sub_issues: prevData.sub_issues.filter((i) => i.id !== issue.id), }; }, false ); issuesService .patchIssue(workspaceSlug.toString(), issue.project, issue.id, { parent: null }, user) .finally(() => mutate(SUB_ISSUES(parentIssue.id))); }; const handleCreateIssueModal = () => { setCreateIssueModal(true); setPreloadedData({ parent: parentIssue.id, }); }; const completedSubIssues = subIssuesResponse ? subIssuesResponse.state_distribution.completed + subIssuesResponse.state_distribution.cancelled : 0; const totalSubIssues = subIssuesResponse ? subIssuesResponse.sub_issues.length : 0; const completionPercentage = (completedSubIssues / totalSubIssues) * 100; const isNotAllowed = memberRole.isGuest || memberRole.isViewer || disabled; return ( <> setCreateIssueModal(false)} /> setSubIssuesListModal(false)} searchParams={{ sub_issue: true, issue_id: parentIssue?.id }} handleOnSubmit={addAsSubIssue} workspaceLevelToggle /> {subIssuesResponse && subIssuesResponse.sub_issues.length > 0 ? ( {({ open }) => ( <>
Sub-issues{" "} {subIssuesResponse.sub_issues.length}
100 ? 100 : completionPercentage.toFixed(0) }%`, }} />
{isNaN(completionPercentage) ? 0 : completionPercentage > 100 ? 100 : completionPercentage.toFixed(0)} % Done
{open && !isNotAllowed ? (
setSubIssuesListModal(true)}> Add an existing issue
) : null}
{subIssuesResponse.sub_issues.map((issue) => (
{issue.project_detail.identifier}-{issue.sequence_id} {issue.name}
{!isNotAllowed && ( )}
))}
)} ) : ( !isNotAllowed && ( Add sub-issue } buttonClassName="whitespace-nowrap" position="left" noBorder noChevron > Create new setSubIssuesListModal(true)}> Add an existing issue ) )} ); };