From cee9695a4a8c8b348cfd7ae8e88bf6ade0ba9e5c Mon Sep 17 00:00:00 2001 From: Anmol Singh Bhatia <121005188+anmolsinghbhatia@users.noreply.github.com> Date: Mon, 24 Apr 2023 23:08:13 -0700 Subject: [PATCH] feat: sub issues progress (#895) --- .../app/components/issues/sub-issues-list.tsx | 108 +++++++++++++++--- apps/app/types/issues.d.ts | 13 +++ 2 files changed, 103 insertions(+), 18 deletions(-) diff --git a/apps/app/components/issues/sub-issues-list.tsx b/apps/app/components/issues/sub-issues-list.tsx index bde0b0e97..456140158 100644 --- a/apps/app/components/issues/sub-issues-list.tsx +++ b/apps/app/components/issues/sub-issues-list.tsx @@ -21,7 +21,7 @@ import { ChevronRightIcon, PlusIcon, XMarkIcon } from "@heroicons/react/24/outli // helpers import { orderArrayBy } from "helpers/array.helper"; // types -import { IIssue } from "types"; +import { IIssue, ISubIssueResponse } from "types"; // fetch-keys import { PROJECT_ISSUES_LIST, SUB_ISSUES } from "constants/fetch-keys"; @@ -40,7 +40,7 @@ export const SubIssuesList: FC = ({ parentIssue }) => { const { memberRole } = useProjectMyMembership(); - const { data: subIssues } = useSWR( + const { data: subIssuesResponse } = useSWR( workspaceSlug && projectId && issueId ? SUB_ISSUES(issueId as string) : null, workspaceSlug && projectId && issueId ? () => @@ -57,6 +57,22 @@ export const SubIssuesList: FC = ({ parentIssue }) => { : null ); + const updateSubIssuesState = (data: IIssue[]) => { + const backlogCount = data.filter((i) => i.state_detail.group === "backlog").length; + const unstartedCount = data.filter((i) => i.state_detail.group === "unstarted").length; + const startedCount = data.filter((i) => i.state_detail.group === "started").length; + const completedCount = data.filter((i) => i.state_detail.group === "completed").length; + const cancelledCount = data.filter((i) => i.state_detail.group === "cancelled").length; + + return { + backlog: backlogCount ?? 0, + unstarted: unstartedCount ?? 0, + started: startedCount ?? 0, + completed: completedCount ?? 0, + cancelled: cancelledCount ?? 0, + }; + }; + const addAsSubIssue = async (data: { issues: string[] }) => { if (!workspaceSlug || !projectId) return; @@ -65,20 +81,20 @@ export const SubIssuesList: FC = ({ parentIssue }) => { sub_issue_ids: data.issues, }) .then((res) => { - mutate( + mutate( SUB_ISSUES(parentIssue?.id ?? ""), (prevData) => { - let newSubIssues = [...(prevData as IIssue[])]; - + if (!prevData) return prevData; + let newSubIssues = prevData.sub_issues as IIssue[]; data.issues.forEach((issueId: string) => { const issue = issues?.find((i) => i.id === issueId); - if (issue) newSubIssues.push(issue); }); - newSubIssues = orderArrayBy(newSubIssues, "created_at", "descending"); - - return newSubIssues; + return { + state_distribution: updateSubIssuesState(newSubIssues), + sub_issues: newSubIssues, + }; }, false ); @@ -108,14 +124,21 @@ export const SubIssuesList: FC = ({ parentIssue }) => { const handleSubIssueRemove = (issueId: string) => { if (!workspaceSlug || !projectId) return; - mutate( + mutate( SUB_ISSUES(parentIssue.id ?? ""), - (prevData) => prevData?.filter((i) => i.id !== issueId), + (prevData) => { + if (!prevData) return prevData; + const updatedArray = (prevData.sub_issues ?? []).filter((i) => i.id !== issueId); + return { + state_distribution: updateSubIssuesState(updatedArray), + sub_issues: updatedArray, + }; + }, false ); issuesService - .patchIssue(workspaceSlug as string, projectId as string, issueId, { parent: null }) + .patchIssue(workspaceSlug.toString(), projectId.toString(), issueId, { parent: null }) .then((res) => { mutate(SUB_ISSUES(parentIssue.id ?? "")); @@ -146,6 +169,21 @@ export const SubIssuesList: FC = ({ parentIssue }) => { }); }; + const completedSubIssues = + subIssuesResponse && subIssuesResponse.state_distribution + ? (subIssuesResponse?.state_distribution.completed + ? subIssuesResponse?.state_distribution.completed + : 0) + + (subIssuesResponse?.state_distribution.cancelled + ? subIssuesResponse?.state_distribution.cancelled + : 0) + : 0; + + const totalSubIssues = + subIssuesResponse && subIssuesResponse.sub_issues ? subIssuesResponse?.sub_issues.length : 0; + + const completionPercentage = (completedSubIssues / totalSubIssues) * 100; + const isNotAllowed = memberRole.isGuest || memberRole.isViewer; return ( @@ -168,15 +206,49 @@ export const SubIssuesList: FC = ({ parentIssue }) => { } handleOnSubmit={addAsSubIssue} /> - {subIssues && subIssues.length > 0 ? ( + {subIssuesResponse && + subIssuesResponse.sub_issues && + subIssuesResponse.sub_issues.length > 0 ? ( {({ open }) => ( <>
- - - Sub-issues {subIssues.length} - +
+ + + Sub-issues{" "} + + {subIssuesResponse.sub_issues.length} + + + {subIssuesResponse.state_distribution && ( +
+
+
100 + ? 100 + : completionPercentage.toFixed(0) + }%`, + }} + /> +
+ + {isNaN(completionPercentage) + ? 0 + : completionPercentage > 100 + ? 100 + : completionPercentage.toFixed(0)} + % Done + +
+ )} +
+ {open && !isNotAllowed ? (