forked from github/plane
[WEB - 466] perf: improve performance for cycle and module endpoints (#3711)
* dev: improve performance for cycle apis * dev: reduce module endpoints and create a new endpoint for getting issues by list * dev: remove unwanted fields from module * dev: update module endpoints * dev: optimize cycle endpoints * change module and cycle types * dev: module optimizations * dev: fix the issues check * dev: fix issues endpoint * dev: update module detail serializer * modify adding issues to modules and cycles * dev: update cycle issues * fix module links * dev: optimize issue list endpoint * fix: removing issues from the module when removing module_id from issue peekoverview * fix: updated the tooltip and ui for cycle select (#3718) * fix: updated the tooltip and ui for module select (#3716) --------- Co-authored-by: rahulramesha <rahulramesham@gmail.com> Co-authored-by: gurusainath <gurusainath007@gmail.com>
This commit is contained in:
@@ -102,7 +102,7 @@ export class CycleStore implements ICycleStore {
|
||||
get currentProjectCycleIds() {
|
||||
const projectId = this.rootStore.app.router.projectId;
|
||||
if (!projectId || !this.fetchedMap[projectId]) return null;
|
||||
let allCycles = Object.values(this.cycleMap ?? {}).filter((c) => c?.project === projectId);
|
||||
let allCycles = Object.values(this.cycleMap ?? {}).filter((c) => c?.project_id === projectId);
|
||||
allCycles = sortBy(allCycles, [(c) => c.sort_order]);
|
||||
const allCycleIds = allCycles.map((c) => c.id);
|
||||
return allCycleIds;
|
||||
@@ -116,7 +116,7 @@ export class CycleStore implements ICycleStore {
|
||||
if (!projectId || !this.fetchedMap[projectId]) return null;
|
||||
let completedCycles = Object.values(this.cycleMap ?? {}).filter((c) => {
|
||||
const hasEndDatePassed = isPast(new Date(c.end_date ?? ""));
|
||||
return c.project === projectId && hasEndDatePassed;
|
||||
return c.project_id === projectId && hasEndDatePassed;
|
||||
});
|
||||
completedCycles = sortBy(completedCycles, [(c) => c.sort_order]);
|
||||
const completedCycleIds = completedCycles.map((c) => c.id);
|
||||
@@ -131,7 +131,7 @@ export class CycleStore implements ICycleStore {
|
||||
if (!projectId || !this.fetchedMap[projectId]) return null;
|
||||
let upcomingCycles = Object.values(this.cycleMap ?? {}).filter((c) => {
|
||||
const isStartDateUpcoming = isFuture(new Date(c.start_date ?? ""));
|
||||
return c.project === projectId && isStartDateUpcoming;
|
||||
return c.project_id === projectId && isStartDateUpcoming;
|
||||
});
|
||||
upcomingCycles = sortBy(upcomingCycles, [(c) => c.sort_order]);
|
||||
const upcomingCycleIds = upcomingCycles.map((c) => c.id);
|
||||
@@ -146,7 +146,7 @@ export class CycleStore implements ICycleStore {
|
||||
if (!projectId || !this.fetchedMap[projectId]) return null;
|
||||
let incompleteCycles = Object.values(this.cycleMap ?? {}).filter((c) => {
|
||||
const hasEndDatePassed = isPast(new Date(c.end_date ?? ""));
|
||||
return c.project === projectId && !hasEndDatePassed;
|
||||
return c.project_id === projectId && !hasEndDatePassed;
|
||||
});
|
||||
incompleteCycles = sortBy(incompleteCycles, [(c) => c.sort_order]);
|
||||
const incompleteCycleIds = incompleteCycles.map((c) => c.id);
|
||||
@@ -160,7 +160,7 @@ export class CycleStore implements ICycleStore {
|
||||
const projectId = this.rootStore.app.router.projectId;
|
||||
if (!projectId || !this.fetchedMap[projectId]) return null;
|
||||
let draftCycles = Object.values(this.cycleMap ?? {}).filter(
|
||||
(c) => c.project === projectId && !c.start_date && !c.end_date
|
||||
(c) => c.project_id === projectId && !c.start_date && !c.end_date
|
||||
);
|
||||
draftCycles = sortBy(draftCycles, [(c) => c.sort_order]);
|
||||
const draftCycleIds = draftCycles.map((c) => c.id);
|
||||
@@ -174,7 +174,7 @@ export class CycleStore implements ICycleStore {
|
||||
const projectId = this.rootStore.app.router.projectId;
|
||||
if (!projectId) return null;
|
||||
const activeCycle = Object.keys(this.activeCycleIdMap ?? {}).find(
|
||||
(cycleId) => this.cycleMap?.[cycleId]?.project === projectId
|
||||
(cycleId) => this.cycleMap?.[cycleId]?.project_id === projectId
|
||||
);
|
||||
return activeCycle || null;
|
||||
}
|
||||
@@ -202,7 +202,7 @@ export class CycleStore implements ICycleStore {
|
||||
getProjectCycleIds = computedFn((projectId: string): string[] | null => {
|
||||
if (!this.fetchedMap[projectId]) return null;
|
||||
|
||||
let cycles = Object.values(this.cycleMap ?? {}).filter((c) => c.project === projectId);
|
||||
let cycles = Object.values(this.cycleMap ?? {}).filter((c) => c.project_id === projectId);
|
||||
cycles = sortBy(cycles, [(c) => c.sort_order]);
|
||||
const cycleIds = cycles.map((c) => c.id);
|
||||
return cycleIds || null;
|
||||
|
||||
@@ -54,7 +54,13 @@ export interface ICycleIssues {
|
||||
data: TIssue,
|
||||
cycleId?: string | undefined
|
||||
) => Promise<TIssue>;
|
||||
addIssueToCycle: (workspaceSlug: string, projectId: string, cycleId: string, issueIds: string[]) => Promise<TIssue>;
|
||||
addIssueToCycle: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
cycleId: string,
|
||||
issueIds: string[],
|
||||
fetchAddedIssues?: boolean
|
||||
) => Promise<void>;
|
||||
removeIssueFromCycle: (workspaceSlug: string, projectId: string, cycleId: string, issueId: string) => Promise<TIssue>;
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ export interface IIssueStoreActions {
|
||||
fetchIssue: (workspaceSlug: string, projectId: string, issueId: string, isArchived?: boolean) => Promise<TIssue>;
|
||||
updateIssue: (workspaceSlug: string, projectId: string, issueId: string, data: Partial<TIssue>) => Promise<TIssue>;
|
||||
removeIssue: (workspaceSlug: string, projectId: string, issueId: string) => Promise<TIssue>;
|
||||
addIssueToCycle: (workspaceSlug: string, projectId: string, cycleId: string, issueIds: string[]) => Promise<TIssue>;
|
||||
addIssueToCycle: (workspaceSlug: string, projectId: string, cycleId: string, issueIds: string[]) => Promise<void>;
|
||||
removeIssueFromCycle: (workspaceSlug: string, projectId: string, cycleId: string, issueId: string) => Promise<TIssue>;
|
||||
addModulesToIssue: (workspaceSlug: string, projectId: string, issueId: string, moduleIds: string[]) => Promise<any>;
|
||||
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) => {
|
||||
|
||||
@@ -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<string, TIssue>; // Record defines issue_id as key and TIssue as value
|
||||
// actions
|
||||
getIssues(workspaceSlug: string, projectId: string, issueIds: string[]): Promise<TIssue[]>;
|
||||
addIssue(issues: TIssue[], shouldReplace?: boolean): void;
|
||||
updateIssue(issueId: string, issue: Partial<TIssue>): 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
|
||||
|
||||
@@ -52,7 +52,13 @@ export interface IModuleIssues {
|
||||
data: TIssue,
|
||||
moduleId?: string | undefined
|
||||
) => Promise<TIssue | undefined>;
|
||||
addIssuesToModule: (workspaceSlug: string, projectId: string, moduleId: string, issueIds: string[]) => Promise<void>;
|
||||
addIssuesToModule: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
moduleId: string,
|
||||
issueIds: string[],
|
||||
fetchAddedIssues?: boolean
|
||||
) => Promise<void>;
|
||||
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;
|
||||
}
|
||||
@@ -356,7 +368,7 @@ export class ModuleIssues extends IssueHelperStore implements IModuleIssues {
|
||||
runInAction(() => {
|
||||
moduleIds.forEach((moduleId) => {
|
||||
update(this.issues, moduleId, (moduleIssueIds = []) => {
|
||||
if (moduleIssueIds.includes(issueId)) return moduleIssueIds;
|
||||
if (moduleIssueIds.includes(issueId)) return pull(moduleIssueIds, issueId);
|
||||
else return uniq(concat(moduleIssueIds, [issueId]));
|
||||
});
|
||||
update(this.rootStore.issues.issuesMap, [issueId, "module_ids"], (issueModuleIds = []) =>
|
||||
|
||||
@@ -99,7 +99,7 @@ export class ModulesStore implements IModuleStore {
|
||||
get projectModuleIds() {
|
||||
const projectId = this.rootStore.app.router.projectId;
|
||||
if (!projectId || !this.fetchedMap[projectId]) return null;
|
||||
let projectModules = Object.values(this.moduleMap).filter((m) => m.project === projectId);
|
||||
let projectModules = Object.values(this.moduleMap).filter((m) => m.project_id === projectId);
|
||||
projectModules = sortBy(projectModules, [(m) => m.sort_order]);
|
||||
const projectModuleIds = projectModules.map((m) => m.id);
|
||||
return projectModuleIds || null;
|
||||
@@ -119,7 +119,7 @@ export class ModulesStore implements IModuleStore {
|
||||
getProjectModuleIds = computedFn((projectId: string) => {
|
||||
if (!this.fetchedMap[projectId]) return null;
|
||||
|
||||
let projectModules = Object.values(this.moduleMap).filter((m) => m.project === projectId);
|
||||
let projectModules = Object.values(this.moduleMap).filter((m) => m.project_id === projectId);
|
||||
projectModules = sortBy(projectModules, [(m) => m.sort_order]);
|
||||
const projectModuleIds = projectModules.map((m) => m.id);
|
||||
return projectModuleIds;
|
||||
|
||||
Reference in New Issue
Block a user