diff --git a/web/components/cycles/transfer-issues-modal.tsx b/web/components/cycles/transfer-issues-modal.tsx index 5956e4a1e..adff19545 100644 --- a/web/components/cycles/transfer-issues-modal.tsx +++ b/web/components/cycles/transfer-issues-modal.tsx @@ -56,7 +56,7 @@ export const TransferIssuesModal: React.FC = observer((props) => { const filteredOptions = currentProjectIncompleteCycleIds?.filter((optionId) => { const cycleDetails = getCycleById(optionId); - return cycleDetails?.name.toLowerCase().includes(query.toLowerCase()); + return cycleDetails?.name?.toLowerCase().includes(query?.toLowerCase()); }); // useEffect(() => { diff --git a/web/components/issues/issue-detail/root.tsx b/web/components/issues/issue-detail/root.tsx index 1fab25d96..e0d54e1ea 100644 --- a/web/components/issues/issue-detail/root.tsx +++ b/web/components/issues/issue-detail/root.tsx @@ -160,7 +160,7 @@ export const IssueDetailRoot: FC = observer((props) => { }, addIssueToCycle: async (workspaceSlug: string, projectId: string, cycleId: string, issueIds: string[]) => { try { - const response = await addIssueToCycle(workspaceSlug, projectId, cycleId, issueIds); + await addIssueToCycle(workspaceSlug, projectId, cycleId, issueIds); setToastAlert({ title: "Cycle added to issue successfully", type: "success", @@ -168,7 +168,7 @@ export const IssueDetailRoot: FC = observer((props) => { }); captureIssueEvent({ eventName: ISSUE_UPDATED, - payload: { ...response, state: "SUCCESS", element: "Issue detail page" }, + payload: { ...issueIds, state: "SUCCESS", element: "Issue detail page" }, updates: { changed_property: "cycle_id", change_details: cycleId, diff --git a/web/components/issues/issue-layouts/empty-states/cycle.tsx b/web/components/issues/issue-layouts/empty-states/cycle.tsx index 69c2279dd..4b7676173 100644 --- a/web/components/issues/issue-layouts/empty-states/cycle.tsx +++ b/web/components/issues/issue-layouts/empty-states/cycle.tsx @@ -60,19 +60,13 @@ export const CycleEmptyState: React.FC = observer((props) => { const issueIds = data.map((i) => i.id); - await issues - .addIssueToCycle(workspaceSlug.toString(), projectId, cycleId.toString(), issueIds) - .then((res) => { - updateIssue(workspaceSlug, projectId, res.id, res); - fetchIssue(workspaceSlug, projectId, res.id); - }) - .catch(() => { - setToastAlert({ - type: "error", - title: "Error!", - message: "Selected issues could not be added to the cycle. Please try again.", - }); + await issues.addIssueToCycle(workspaceSlug.toString(), projectId, cycleId.toString(), issueIds).catch(() => { + setToastAlert({ + type: "error", + title: "Error!", + message: "Selected issues could not be added to the cycle. Please try again.", }); + }); }; const emptyStateDetail = CYCLE_EMPTY_STATE_DETAILS["no-issues"]; diff --git a/web/components/issues/peek-overview/root.tsx b/web/components/issues/peek-overview/root.tsx index c49c0a503..564b5a019 100644 --- a/web/components/issues/peek-overview/root.tsx +++ b/web/components/issues/peek-overview/root.tsx @@ -146,7 +146,7 @@ export const IssuePeekOverview: FC = observer((props) => { }, addIssueToCycle: async (workspaceSlug: string, projectId: string, cycleId: string, issueIds: string[]) => { try { - const response = await addIssueToCycle(workspaceSlug, projectId, cycleId, issueIds); + await addIssueToCycle(workspaceSlug, projectId, cycleId, issueIds); setToastAlert({ title: "Cycle added to issue successfully", type: "success", @@ -154,7 +154,7 @@ export const IssuePeekOverview: FC = observer((props) => { }); captureIssueEvent({ eventName: ISSUE_UPDATED, - payload: { ...response, state: "SUCCESS", element: "Issue peek-overview" }, + payload: { ...issueIds, state: "SUCCESS", element: "Issue peek-overview" }, updates: { changed_property: "cycle_id", change_details: cycleId, diff --git a/web/services/issue/issue.service.ts b/web/services/issue/issue.service.ts index 0b65b0482..5d3663dd6 100644 --- a/web/services/issue/issue.service.ts +++ b/web/services/issue/issue.service.ts @@ -59,6 +59,16 @@ export class IssueService extends APIService { }); } + async retrieveIssues(workspaceSlug: string, projectId: string, issueIds: string[]): Promise { + return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/issues/list/`, { + params: { issues: issueIds.join(",") }, + }) + .then((response) => response?.data) + .catch((error) => { + throw error?.response?.data; + }); + } + async getIssueActivities(workspaceSlug: string, projectId: string, issueId: string): Promise { return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/issues/${issueId}/history/`) .then((response) => response?.data) diff --git a/web/store/issue/cycle/issue.store.ts b/web/store/issue/cycle/issue.store.ts index 71618d51c..5a9cae62c 100644 --- a/web/store/issue/cycle/issue.store.ts +++ b/web/store/issue/cycle/issue.store.ts @@ -54,7 +54,13 @@ export interface ICycleIssues { data: TIssue, cycleId?: string | undefined ) => Promise; - addIssueToCycle: (workspaceSlug: string, projectId: string, cycleId: string, issueIds: string[]) => Promise; + addIssueToCycle: ( + workspaceSlug: string, + projectId: string, + cycleId: string, + issueIds: string[], + fetchAddedIssues?: boolean + ) => Promise; removeIssueFromCycle: (workspaceSlug: string, projectId: string, cycleId: string, issueId: string) => Promise; transferIssuesFromCycle: ( workspaceSlug: string, @@ -182,7 +188,7 @@ export class CycleIssues extends IssueHelperStore implements ICycleIssues { if (!cycleId) throw new Error("Cycle Id is required"); const response = await this.rootIssueStore.projectIssues.createIssue(workspaceSlug, projectId, data); - await this.addIssueToCycle(workspaceSlug, projectId, cycleId, [response.id]); + await this.addIssueToCycle(workspaceSlug, projectId, cycleId, [response.id], false); this.rootIssueStore.rootStore.cycle.fetchCycleDetails(workspaceSlug, projectId, cycleId); return response; @@ -265,21 +271,33 @@ export class CycleIssues extends IssueHelperStore implements ICycleIssues { } }; - addIssueToCycle = async (workspaceSlug: string, projectId: string, cycleId: string, issueIds: string[]) => { + addIssueToCycle = async ( + workspaceSlug: string, + projectId: string, + cycleId: string, + issueIds: string[], + fetchAddedIssues = true + ) => { try { - const issueToCycle = await this.issueService.addIssueToCycle(workspaceSlug, projectId, cycleId, { + await this.issueService.addIssueToCycle(workspaceSlug, projectId, cycleId, { issues: issueIds, }); + if (fetchAddedIssues) await this.rootIssueStore.issues.getIssues(workspaceSlug, projectId, issueIds); + runInAction(() => { update(this.issues, cycleId, (cycleIssueIds = []) => uniq(concat(cycleIssueIds, issueIds))); }); issueIds.forEach((issueId) => { + const issueCycleId = this.rootIssueStore.issues.getIssueById(issueId)?.cycle_id; + if (issueCycleId && issueCycleId !== cycleId) { + runInAction(() => { + pull(this.issues[issueCycleId], issueId); + }); + } this.rootStore.issues.updateIssue(issueId, { cycle_id: cycleId }); }); this.rootIssueStore.rootStore.cycle.fetchCycleDetails(workspaceSlug, projectId, cycleId); - - return issueToCycle; } catch (error) { throw error; } diff --git a/web/store/issue/issue-details/issue.store.ts b/web/store/issue/issue-details/issue.store.ts index 43a7ca093..ccde8c26b 100644 --- a/web/store/issue/issue-details/issue.store.ts +++ b/web/store/issue/issue-details/issue.store.ts @@ -11,7 +11,7 @@ export interface IIssueStoreActions { fetchIssue: (workspaceSlug: string, projectId: string, issueId: string, isArchived?: boolean) => Promise; updateIssue: (workspaceSlug: string, projectId: string, issueId: string, data: Partial) => Promise; removeIssue: (workspaceSlug: string, projectId: string, issueId: string) => Promise; - addIssueToCycle: (workspaceSlug: string, projectId: string, cycleId: string, issueIds: string[]) => Promise; + addIssueToCycle: (workspaceSlug: string, projectId: string, cycleId: string, issueIds: string[]) => Promise; removeIssueFromCycle: (workspaceSlug: string, projectId: string, cycleId: string, issueId: string) => Promise; addModulesToIssue: (workspaceSlug: string, projectId: string, issueId: string, moduleIds: string[]) => Promise; removeModulesFromIssue: ( @@ -123,15 +123,15 @@ export class IssueStore implements IIssueStore { this.rootIssueDetailStore.rootIssueStore.projectIssues.removeIssue(workspaceSlug, projectId, issueId); addIssueToCycle = async (workspaceSlug: string, projectId: string, cycleId: string, issueIds: string[]) => { - const cycle = await this.rootIssueDetailStore.rootIssueStore.cycleIssues.addIssueToCycle( + await this.rootIssueDetailStore.rootIssueStore.cycleIssues.addIssueToCycle( workspaceSlug, projectId, cycleId, - issueIds + issueIds, + false ); if (issueIds && issueIds.length > 0) await this.rootIssueDetailStore.activity.fetchActivities(workspaceSlug, projectId, issueIds[0]); - return cycle; }; removeIssueFromCycle = async (workspaceSlug: string, projectId: string, cycleId: string, issueId: string) => { diff --git a/web/store/issue/issue.store.ts b/web/store/issue/issue.store.ts index 36b2d8741..8bdb18dad 100644 --- a/web/store/issue/issue.store.ts +++ b/web/store/issue/issue.store.ts @@ -5,11 +5,14 @@ import { action, makeObservable, observable, runInAction } from "mobx"; import { computedFn } from "mobx-utils"; // types import { TIssue } from "@plane/types"; +//services +import { IssueService } from "services/issue"; export type IIssueStore = { // observables issuesMap: Record; // Record defines issue_id as key and TIssue as value // actions + getIssues(workspaceSlug: string, projectId: string, issueIds: string[]): Promise; addIssue(issues: TIssue[], shouldReplace?: boolean): void; updateIssue(issueId: string, issue: Partial): void; removeIssue(issueId: string): void; @@ -21,6 +24,8 @@ export type IIssueStore = { export class IssueStore implements IIssueStore { // observables issuesMap: { [issue_id: string]: TIssue } = {}; + // service + issueService; constructor() { makeObservable(this, { @@ -31,6 +36,8 @@ export class IssueStore implements IIssueStore { updateIssue: action, removeIssue: action, }); + + this.issueService = new IssueService(); } // actions @@ -48,6 +55,18 @@ export class IssueStore implements IIssueStore { }); }; + getIssues = async (workspaceSlug: string, projectId: string, issueIds: string[]) => { + const issues = await this.issueService.retrieveIssues(workspaceSlug, projectId, issueIds); + + runInAction(() => { + issues.forEach((issue) => { + if (!this.issuesMap[issue.id]) set(this.issuesMap, issue.id, issue); + }); + }); + + return issues; + }; + /** * @description This method will update the issue in the issuesMap * @param {string} issueId diff --git a/web/store/issue/module/issue.store.ts b/web/store/issue/module/issue.store.ts index c9ed459a1..19e469ea4 100644 --- a/web/store/issue/module/issue.store.ts +++ b/web/store/issue/module/issue.store.ts @@ -52,7 +52,13 @@ export interface IModuleIssues { data: TIssue, moduleId?: string | undefined ) => Promise; - addIssuesToModule: (workspaceSlug: string, projectId: string, moduleId: string, issueIds: string[]) => Promise; + addIssuesToModule: ( + workspaceSlug: string, + projectId: string, + moduleId: string, + issueIds: string[], + fetchAddedIssues?: boolean + ) => Promise; removeIssuesFromModule: ( workspaceSlug: string, projectId: string, @@ -187,7 +193,7 @@ export class ModuleIssues extends IssueHelperStore implements IModuleIssues { if (!moduleId) throw new Error("Module Id is required"); const response = await this.rootIssueStore.projectIssues.createIssue(workspaceSlug, projectId, data); - await this.addIssuesToModule(workspaceSlug, projectId, moduleId, [response.id]); + await this.addIssuesToModule(workspaceSlug, projectId, moduleId, [response.id], false); this.rootIssueStore.rootStore.module.fetchModuleDetails(workspaceSlug, projectId, moduleId); return response; @@ -269,12 +275,20 @@ export class ModuleIssues extends IssueHelperStore implements IModuleIssues { } }; - addIssuesToModule = async (workspaceSlug: string, projectId: string, moduleId: string, issueIds: string[]) => { + addIssuesToModule = async ( + workspaceSlug: string, + projectId: string, + moduleId: string, + issueIds: string[], + fetchAddedIssues = true + ) => { try { - const issueToModule = await this.moduleService.addIssuesToModule(workspaceSlug, projectId, moduleId, { + await this.moduleService.addIssuesToModule(workspaceSlug, projectId, moduleId, { issues: issueIds, }); + if (fetchAddedIssues) await this.rootIssueStore.issues.getIssues(workspaceSlug, projectId, issueIds); + runInAction(() => { update(this.issues, moduleId, (moduleIssueIds = []) => { if (!moduleIssueIds) return [...issueIds]; @@ -289,8 +303,6 @@ export class ModuleIssues extends IssueHelperStore implements IModuleIssues { }); }); this.rootIssueStore.rootStore.module.fetchModuleDetails(workspaceSlug, projectId, moduleId); - - return issueToModule; } catch (error) { throw error; }