From d1df1cf8ba38f110ff48c670d144d33301a6457e Mon Sep 17 00:00:00 2001 From: gurusainath Date: Thu, 21 Sep 2023 17:35:16 +0530 Subject: [PATCH 1/3] fix: sub issues mutation issues resolved --- web/components/issues/main-content.tsx | 4 +- web/components/issues/sub-issues/issue.tsx | 57 ++- .../issues/sub-issues/issues-list.tsx | 13 +- .../issues/sub-issues/progressbar.tsx | 2 +- web/components/issues/sub-issues/root.tsx | 349 ++++++++++-------- 5 files changed, 244 insertions(+), 181 deletions(-) diff --git a/web/components/issues/main-content.tsx b/web/components/issues/main-content.tsx index 5e14ba4326e..c4c9a780aba 100644 --- a/web/components/issues/main-content.tsx +++ b/web/components/issues/main-content.tsx @@ -43,7 +43,7 @@ export const IssueMainContent: React.FC = ({ uneditable = false, }) => { const router = useRouter(); - const { workspaceSlug, projectId, issueId, archivedIssueId } = router.query; + const { workspaceSlug, projectId, issueId } = router.query; const { setToastAlert } = useToast(); @@ -206,7 +206,7 @@ export const IssueMainContent: React.FC = ({
- +
diff --git a/web/components/issues/sub-issues/issue.tsx b/web/components/issues/sub-issues/issue.tsx index 2e3d8acdbb3..52108f78520 100644 --- a/web/components/issues/sub-issues/issue.tsx +++ b/web/components/issues/sub-issues/issue.tsx @@ -16,9 +16,9 @@ import { SubIssuesRootList } from "./issues-list"; import { IssueProperty } from "./properties"; // ui import { Tooltip, CustomMenu } from "components/ui"; - // types import { ICurrentUserResponse, IIssue } from "types"; +import { ISubIssuesRootLoaders, ISubIssuesRootLoadersHandler } from "./root"; export interface ISubIssues { workspaceSlug: string; @@ -29,8 +29,8 @@ export interface ISubIssues { user: ICurrentUserResponse | undefined; editable: boolean; removeIssueFromSubIssues: (parentIssueId: string, issue: IIssue) => void; - issuesVisibility: string[]; - handleIssuesVisibility: (issueId: string) => void; + issuesLoader: ISubIssuesRootLoaders; + handleIssuesLoader: ({ key, issueId }: ISubIssuesRootLoadersHandler) => void; copyText: (text: string) => void; handleIssueCrudOperation: ( key: "create" | "existing" | "edit" | "delete", @@ -48,8 +48,8 @@ export const SubIssues: React.FC = ({ user, editable, removeIssueFromSubIssues, - issuesVisibility, - handleIssuesVisibility, + issuesLoader, + handleIssuesLoader, copyText, handleIssueCrudOperation, }) => ( @@ -62,19 +62,21 @@ export const SubIssues: React.FC = ({
{issue?.sub_issues_count > 0 && ( <> - {true ? ( + {issuesLoader.sub_issues.includes(issue?.id) ? ( +
+ +
+ ) : (
handleIssuesVisibility(issue?.id)} + onClick={() => handleIssuesLoader({ key: "visibility", issueId: issue?.id })} > - {issuesVisibility && issuesVisibility.includes(issue?.id) ? ( + {issuesLoader && issuesLoader.visibility.includes(issue?.id) ? ( ) : ( )}
- ) : ( - )} )} @@ -132,7 +134,11 @@ export const SubIssues: React.FC = ({ )} - + + copyText(`${workspaceSlug}/projects/${issue.project}/issues/${issue.id}`) + } + >
Copy issue link @@ -142,17 +148,28 @@ export const SubIssues: React.FC = ({
{editable && ( -
removeIssueFromSubIssues(parentIssue?.id, issue)} - > - -
+ <> + {issuesLoader.delete.includes(issue?.id) ? ( +
+ +
+ ) : ( +
{ + handleIssuesLoader({ key: "delete", issueId: issue?.id }); + removeIssueFromSubIssues(parentIssue?.id, issue); + }} + > + +
+ )} + )}
)} - {issuesVisibility.includes(issue?.id) && issue?.sub_issues_count > 0 && ( + {issuesLoader.visibility.includes(issue?.id) && issue?.sub_issues_count > 0 && ( = ({ user={user} editable={editable} removeIssueFromSubIssues={removeIssueFromSubIssues} - issuesVisibility={issuesVisibility} - handleIssuesVisibility={handleIssuesVisibility} + issuesLoader={issuesLoader} + handleIssuesLoader={handleIssuesLoader} copyText={copyText} handleIssueCrudOperation={handleIssueCrudOperation} /> diff --git a/web/components/issues/sub-issues/issues-list.tsx b/web/components/issues/sub-issues/issues-list.tsx index 45c0b18828f..e8f07cd5436 100644 --- a/web/components/issues/sub-issues/issues-list.tsx +++ b/web/components/issues/sub-issues/issues-list.tsx @@ -5,6 +5,7 @@ import useSWR from "swr"; import { SubIssues } from "./issue"; // types import { ICurrentUserResponse, IIssue } from "types"; +import { ISubIssuesRootLoaders, ISubIssuesRootLoadersHandler } from "./root"; // services import issuesService from "services/issues.service"; // fetch keys @@ -18,8 +19,8 @@ export interface ISubIssuesRootList { user: ICurrentUserResponse | undefined; editable: boolean; removeIssueFromSubIssues: (parentIssueId: string, issue: IIssue) => void; - issuesVisibility: string[]; - handleIssuesVisibility: (issueId: string) => void; + issuesLoader: ISubIssuesRootLoaders; + handleIssuesLoader: ({ key, issueId }: ISubIssuesRootLoadersHandler) => void; copyText: (text: string) => void; handleIssueCrudOperation: ( key: "create" | "existing" | "edit" | "delete", @@ -36,8 +37,8 @@ export const SubIssuesRootList: React.FC = ({ user, editable, removeIssueFromSubIssues, - issuesVisibility, - handleIssuesVisibility, + issuesLoader, + handleIssuesLoader, copyText, handleIssueCrudOperation, }) => { @@ -66,8 +67,8 @@ export const SubIssuesRootList: React.FC = ({ user={user} editable={editable} removeIssueFromSubIssues={removeIssueFromSubIssues} - issuesVisibility={issuesVisibility} - handleIssuesVisibility={handleIssuesVisibility} + issuesLoader={issuesLoader} + handleIssuesLoader={handleIssuesLoader} copyText={copyText} handleIssueCrudOperation={handleIssueCrudOperation} /> diff --git a/web/components/issues/sub-issues/progressbar.tsx b/web/components/issues/sub-issues/progressbar.tsx index 368078a3d09..dee91263b2b 100644 --- a/web/components/issues/sub-issues/progressbar.tsx +++ b/web/components/issues/sub-issues/progressbar.tsx @@ -14,7 +14,7 @@ export const ProgressBar = ({ total = 0, done = 0 }: IProgressBar) => {
diff --git a/web/components/issues/sub-issues/root.tsx b/web/components/issues/sub-issues/root.tsx index 75161e63964..3e605b052dc 100644 --- a/web/components/issues/sub-issues/root.tsx +++ b/web/components/issues/sub-issues/root.tsx @@ -14,6 +14,7 @@ import { ProgressBar } from "./progressbar"; import { CustomMenu } from "components/ui"; // hooks import { useProjectMyMembership } from "contexts/project-member.context"; +import useToast from "hooks/use-toast"; // helpers import { copyTextToClipboard } from "helpers/string.helper"; // types @@ -21,22 +22,31 @@ import { ICurrentUserResponse, IIssue, ISearchIssueResponse } from "types"; // services import issuesService from "services/issues.service"; // fetch keys -import { SUB_ISSUES } from "constants/fetch-keys"; +import { ISSUE_DETAILS, SUB_ISSUES } from "constants/fetch-keys"; export interface ISubIssuesRoot { parentIssue: IIssue; - user: ICurrentUserResponse | undefined; - editable: boolean; } -export const SubIssuesRoot: React.FC = ({ parentIssue, user, editable }) => { +export interface ISubIssuesRootLoaders { + visibility: string[]; + delete: string[]; + sub_issues: string[]; +} +export interface ISubIssuesRootLoadersHandler { + key: "visibility" | "delete" | "sub_issues"; + issueId: string; +} + +export const SubIssuesRoot: React.FC = ({ parentIssue, user }) => { const router = useRouter(); const { workspaceSlug, projectId } = router.query as { workspaceSlug: string; projectId: string }; const { memberRole } = useProjectMyMembership(); + const { setToastAlert } = useToast(); - const { data: issues } = useSWR( + const { data: issues, isLoading } = useSWR( workspaceSlug && projectId && parentIssue && parentIssue?.id ? SUB_ISSUES(parentIssue?.id) : null, @@ -45,13 +55,18 @@ export const SubIssuesRoot: React.FC = ({ parentIssue, user, edi : null ); - const [issuesVisibility, setIssuesVisibility] = React.useState([parentIssue?.id]); - const handleIssuesVisibility = (issueId: string) => { - if (issuesVisibility.includes(issueId)) { - setIssuesVisibility(issuesVisibility.filter((i: string) => i !== issueId)); - } else { - setIssuesVisibility([...issuesVisibility, issueId]); - } + const [issuesLoader, setIssuesLoader] = React.useState({ + visibility: [parentIssue?.id], + delete: [], + sub_issues: [], + }); + const handleIssuesLoader = ({ key, issueId }: ISubIssuesRootLoadersHandler) => { + setIssuesLoader((previousData: ISubIssuesRootLoaders) => ({ + ...previousData, + [key]: previousData[key].includes(issueId) + ? previousData[key].filter((i: string) => i !== issueId) + : [...previousData[key], issueId], + })); }; const [issueCrudOperation, setIssueCrudOperation] = React.useState<{ @@ -100,7 +115,6 @@ export const SubIssuesRoot: React.FC = ({ parentIssue, user, edi const payload = { sub_issue_ids: data.map((i) => i.id), }; - await issuesService.addSubIssues(workspaceSlug, projectId, issueId, payload).finally(() => { if (issueId) mutate(SUB_ISSUES(issueId)); }); @@ -110,8 +124,22 @@ export const SubIssuesRoot: React.FC = ({ parentIssue, user, edi if (!workspaceSlug || !parentIssue || !issue?.id) return; issuesService .patchIssue(workspaceSlug, projectId, issue.id, { parent: null }, user) - .finally(() => { - if (parentIssueId) mutate(SUB_ISSUES(parentIssueId)); + .then(async () => { + if (parentIssueId) await mutate(SUB_ISSUES(parentIssueId)); + handleIssuesLoader({ key: "delete", issueId: issue?.id }); + setToastAlert({ + type: "success", + title: `Issue removed!`, + message: `Issue removed successfully.`, + }); + }) + .catch(() => { + handleIssuesLoader({ key: "delete", issueId: issue?.id }); + setToastAlert({ + type: "warning", + title: `Error!`, + message: `Error, Please try again later.`, + }); }); }; @@ -119,11 +147,11 @@ export const SubIssuesRoot: React.FC = ({ parentIssue, user, edi const originURL = typeof window !== "undefined" && window.location.origin ? window.location.origin : ""; copyTextToClipboard(`${originURL}/${text}`).then(() => { - // setToastAlert({ - // type: "success", - // title: "Link Copied!", - // message: "Issue link copied to clipboard.", - // }); + setToastAlert({ + type: "success", + title: "Link Copied!", + message: "Issue link copied to clipboard.", + }); }); }; @@ -135,148 +163,165 @@ export const SubIssuesRoot: React.FC = ({ parentIssue, user, edi return (
- {parentIssue && parentIssue?.sub_issues_count > 0 ? ( + {!issues && isLoading ? ( +
Loading...
+ ) : ( <> - {/* header */} -
-
handleIssuesVisibility(parentIssue?.id)} - > -
- {issuesVisibility.includes(parentIssue?.id) ? ( - - ) : ( - - )} -
-
Sub-issues
-
({parentIssue?.sub_issues_count})
-
- -
- -
- - {isEditable && issuesVisibility.includes(parentIssue?.id) && ( -
+ {issues && issues?.sub_issues && issues?.sub_issues?.length > 0 ? ( + <> + {/* header */} +
handleIssueCrudOperation("create", parentIssue?.id)} + className="rounded border border-custom-border-100 shadow p-1.5 px-2 flex items-center gap-1 hover:bg-custom-background-80 transition-all cursor-pointer select-none" + onClick={() => + handleIssuesLoader({ key: "visibility", issueId: parentIssue?.id }) + } > - Add sub-issue +
+ {issuesLoader.visibility.includes(parentIssue?.id) ? ( + + ) : ( + + )} +
+
Sub-issues
+
({issues?.sub_issues?.length || 0})
-
handleIssueCrudOperation("existing", parentIssue?.id)} - > - Add an existing issue + +
+
+ + {isEditable && issuesLoader.visibility.includes(parentIssue?.id) && ( +
+
handleIssueCrudOperation("create", parentIssue?.id)} + > + Add sub-issue +
+
handleIssueCrudOperation("existing", parentIssue?.id)} + > + Add an existing issue +
+
+ )}
- )} -
- {/* issues */} - {issuesVisibility.includes(parentIssue?.id) && ( -
- -
+ {/* issues */} + {issuesLoader.visibility.includes(parentIssue?.id) && ( +
+ +
+ )} + + ) : ( + isEditable && ( +
+
No sub issues are available
+ <> + + + Add sub-issue + + } + buttonClassName="whitespace-nowrap" + position="left" + noBorder + noChevron + > + { + mutateSubIssues(parentIssue?.id); + handleIssueCrudOperation("create", parentIssue?.id); + }} + > + Create new + + { + mutateSubIssues(parentIssue?.id); + handleIssueCrudOperation("existing", parentIssue?.id); + }} + > + Add an existing issue + + + +
+ ) )} - - ) : ( - isEditable && ( -
-
No sub issues are available
+ {isEditable && issueCrudOperation?.create?.toggle && ( + { + mutateSubIssues(issueCrudOperation?.create?.issueId); + handleIssueCrudOperation("create", null); + }} + /> + )} + {isEditable && + issueCrudOperation?.existing?.toggle && + issueCrudOperation?.existing?.issueId && ( + handleIssueCrudOperation("existing", null)} + searchParams={{ sub_issue: true, issue_id: issueCrudOperation?.existing?.issueId }} + handleOnSubmit={addAsSubIssueFromExistingIssues} + workspaceLevelToggle + /> + )} + {isEditable && issueCrudOperation?.edit?.toggle && issueCrudOperation?.edit?.issueId && ( <> - - - Add sub-issue - - } - buttonClassName="whitespace-nowrap" - position="left" - noBorder - noChevron - > - handleIssueCrudOperation("create", parentIssue?.id)} - > - Create new - - handleIssueCrudOperation("existing", parentIssue?.id)} - > - Add an existing issue - - + { + mutateSubIssues(issueCrudOperation?.edit?.issueId); + handleIssueCrudOperation("edit", null, null); + }} + data={issueCrudOperation?.edit?.issue} + /> -
- ) - )} - - {isEditable && issueCrudOperation?.create?.toggle && ( - handleIssueCrudOperation("create", null)} - /> - )} - - {isEditable && - issueCrudOperation?.existing?.toggle && - issueCrudOperation?.existing?.issueId && ( - handleIssueCrudOperation("existing", null)} - searchParams={{ sub_issue: true, issue_id: issueCrudOperation?.existing?.issueId }} - handleOnSubmit={addAsSubIssueFromExistingIssues} - workspaceLevelToggle - /> - )} - - {isEditable && issueCrudOperation?.edit?.toggle && issueCrudOperation?.edit?.issueId && ( - { - mutateSubIssues(issueCrudOperation?.edit?.issueId); - handleIssueCrudOperation("edit", null, null); - }} - data={issueCrudOperation?.edit?.issue} - /> - )} - - {isEditable && issueCrudOperation?.delete?.toggle && issueCrudOperation?.delete?.issueId && ( - { - mutateSubIssues(issueCrudOperation?.delete?.issueId); - handleIssueCrudOperation("delete", null, null); - }} - data={issueCrudOperation?.delete?.issue} - user={user} - redirection={false} - /> + )} + {isEditable && + issueCrudOperation?.delete?.toggle && + issueCrudOperation?.delete?.issueId && ( + { + mutateSubIssues(issueCrudOperation?.delete?.issueId); + handleIssueCrudOperation("delete", null, null); + }} + data={issueCrudOperation?.delete?.issue} + user={user} + redirection={false} + /> + )} + )}
); From 357349ae0db17a60b1e4154d3e8bc0363cfdd2be Mon Sep 17 00:00:00 2001 From: gurusainath Date: Thu, 21 Sep 2023 17:59:51 +0530 Subject: [PATCH 2/3] fix: sub issues loader --- web/components/issues/sub-issues/issues-list.tsx | 10 ++++++++++ web/components/issues/sub-issues/root.tsx | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/web/components/issues/sub-issues/issues-list.tsx b/web/components/issues/sub-issues/issues-list.tsx index e8f07cd5436..9fc77992e72 100644 --- a/web/components/issues/sub-issues/issues-list.tsx +++ b/web/components/issues/sub-issues/issues-list.tsx @@ -51,6 +51,16 @@ export const SubIssuesRootList: React.FC = ({ : null ); + React.useEffect(() => { + if (isLoading) { + handleIssuesLoader({ key: "sub_issues", issueId: parentIssue?.id }); + } else { + if (issuesLoader.sub_issues.includes(parentIssue?.id)) { + handleIssuesLoader({ key: "sub_issues", issueId: parentIssue?.id }); + } + } + }, [isLoading]); + return (
{issues && diff --git a/web/components/issues/sub-issues/root.tsx b/web/components/issues/sub-issues/root.tsx index 3e605b052dc..305a9150b60 100644 --- a/web/components/issues/sub-issues/root.tsx +++ b/web/components/issues/sub-issues/root.tsx @@ -22,7 +22,7 @@ import { ICurrentUserResponse, IIssue, ISearchIssueResponse } from "types"; // services import issuesService from "services/issues.service"; // fetch keys -import { ISSUE_DETAILS, SUB_ISSUES } from "constants/fetch-keys"; +import { SUB_ISSUES } from "constants/fetch-keys"; export interface ISubIssuesRoot { parentIssue: IIssue; From 014375adb5a098ba732985902fbd21e7654a3c42 Mon Sep 17 00:00:00 2001 From: gurusainath Date: Thu, 21 Sep 2023 19:03:00 +0530 Subject: [PATCH 3/3] fix: assignees mutation issue resolved --- web/components/issues/sub-issues/issue.tsx | 2 +- web/components/issues/sub-issues/properties.tsx | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/web/components/issues/sub-issues/issue.tsx b/web/components/issues/sub-issues/issue.tsx index 52108f78520..2adb7c6f990 100644 --- a/web/components/issues/sub-issues/issue.tsx +++ b/web/components/issues/sub-issues/issue.tsx @@ -94,7 +94,7 @@ export const SubIssues: React.FC = ({ {issue.project_detail.identifier}-{issue?.sequence_id}
-
{issue?.name}
+
{issue?.name}
diff --git a/web/components/issues/sub-issues/properties.tsx b/web/components/issues/sub-issues/properties.tsx index a899efdcfb6..4f2dca43bab 100644 --- a/web/components/issues/sub-issues/properties.tsx +++ b/web/components/issues/sub-issues/properties.tsx @@ -86,12 +86,14 @@ export const IssueProperty: React.FC = ({ }; const handleAssigneeChange = (data: any) => { - const newData = issue.assignees ?? []; + let newData = issue.assignees ?? []; - if (newData.includes(data)) newData.splice(newData.indexOf(data), 1); - else newData.push(data); + if (newData && newData.length > 0) { + if (newData.includes(data)) newData = newData.splice(newData.indexOf(data), 1); + else newData = [...newData, data]; + } else newData = [...newData, data]; - partialUpdateIssue({ assignees_list: data }); + partialUpdateIssue({ assignees_list: data, assignees: data }); trackEventServices.trackIssuePartialPropertyUpdateEvent( {