mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
[WEB-1142] chore: optimistically add issue to cycle/modules (#4334)
* chore: optimistically add issue to cycle and module * chore: update toast alerts * refactor: module issue store * chore: added addCycleToIssueFunction
This commit is contained in:
parent
a85517de99
commit
780b239ecb
@ -34,7 +34,7 @@ export const IssueCycleSelect: React.FC<TIssueCycleSelect> = observer((props) =>
|
||||
const handleIssueCycleChange = async (cycleId: string | null) => {
|
||||
if (!issue || issue.cycle_id === cycleId) return;
|
||||
setIsUpdating(true);
|
||||
if (cycleId) await issueOperations.addIssueToCycle?.(workspaceSlug, projectId, cycleId, [issueId]);
|
||||
if (cycleId) await issueOperations.addCycleToIssue?.(workspaceSlug, projectId, cycleId, issueId);
|
||||
else await issueOperations.removeIssueFromCycle?.(workspaceSlug, projectId, issue.cycle_id ?? "", issueId);
|
||||
setIsUpdating(false);
|
||||
};
|
||||
|
@ -26,6 +26,7 @@ export type TIssueOperations = {
|
||||
remove: (workspaceSlug: string, projectId: string, issueId: string) => Promise<void>;
|
||||
archive?: (workspaceSlug: string, projectId: string, issueId: string) => Promise<void>;
|
||||
restore?: (workspaceSlug: string, projectId: string, issueId: string) => Promise<void>;
|
||||
addCycleToIssue?: (workspaceSlug: string, projectId: string, cycleId: string, issueId: string) => Promise<void>;
|
||||
addIssueToCycle?: (workspaceSlug: string, projectId: string, cycleId: string, issueIds: string[]) => Promise<void>;
|
||||
removeIssueFromCycle?: (workspaceSlug: string, projectId: string, cycleId: string, issueId: string) => Promise<void>;
|
||||
addModulesToIssue?: (workspaceSlug: string, projectId: string, issueId: string, moduleIds: string[]) => Promise<void>;
|
||||
@ -62,6 +63,7 @@ export const IssueDetailRoot: FC<TIssueDetailRoot> = observer((props) => {
|
||||
updateIssue,
|
||||
removeIssue,
|
||||
archiveIssue,
|
||||
addCycleToIssue,
|
||||
addIssueToCycle,
|
||||
removeIssueFromCycle,
|
||||
addModulesToIssue,
|
||||
@ -158,21 +160,38 @@ export const IssueDetailRoot: FC<TIssueDetailRoot> = observer((props) => {
|
||||
});
|
||||
}
|
||||
},
|
||||
addCycleToIssue: async (workspaceSlug: string, projectId: string, cycleId: string, issueId: string) => {
|
||||
try {
|
||||
await addCycleToIssue(workspaceSlug, projectId, cycleId, issueId);
|
||||
captureIssueEvent({
|
||||
eventName: ISSUE_UPDATED,
|
||||
payload: { issueId, state: "SUCCESS", element: "Issue detail page" },
|
||||
updates: {
|
||||
changed_property: "cycle_id",
|
||||
change_details: cycleId,
|
||||
},
|
||||
path: router.asPath,
|
||||
});
|
||||
} catch (error) {
|
||||
setToast({
|
||||
type: TOAST_TYPE.ERROR,
|
||||
title: "Error!",
|
||||
message: "Issue could not be added to the cycle. Please try again.",
|
||||
});
|
||||
captureIssueEvent({
|
||||
eventName: ISSUE_UPDATED,
|
||||
payload: { state: "FAILED", element: "Issue detail page" },
|
||||
updates: {
|
||||
changed_property: "cycle_id",
|
||||
change_details: cycleId,
|
||||
},
|
||||
path: router.asPath,
|
||||
});
|
||||
}
|
||||
},
|
||||
addIssueToCycle: async (workspaceSlug: string, projectId: string, cycleId: string, issueIds: string[]) => {
|
||||
try {
|
||||
const addToCyclePromise = addIssueToCycle(workspaceSlug, projectId, cycleId, issueIds);
|
||||
setPromiseToast(addToCyclePromise, {
|
||||
loading: "Adding cycle to issue...",
|
||||
success: {
|
||||
title: "Success!",
|
||||
message: () => "Cycle added to issue successfully",
|
||||
},
|
||||
error: {
|
||||
title: "Error!",
|
||||
message: () => "Cycle add to issue failed",
|
||||
},
|
||||
});
|
||||
await addToCyclePromise;
|
||||
await addIssueToCycle(workspaceSlug, projectId, cycleId, issueIds);
|
||||
captureIssueEvent({
|
||||
eventName: ISSUE_UPDATED,
|
||||
payload: { ...issueIds, state: "SUCCESS", element: "Issue detail page" },
|
||||
@ -183,6 +202,11 @@ export const IssueDetailRoot: FC<TIssueDetailRoot> = observer((props) => {
|
||||
path: router.asPath,
|
||||
});
|
||||
} catch (error) {
|
||||
setToast({
|
||||
type: TOAST_TYPE.ERROR,
|
||||
title: "Error!",
|
||||
message: "Issue could not be added to the cycle. Please try again.",
|
||||
});
|
||||
captureIssueEvent({
|
||||
eventName: ISSUE_UPDATED,
|
||||
payload: { state: "FAILED", element: "Issue detail page" },
|
||||
@ -198,14 +222,14 @@ export const IssueDetailRoot: FC<TIssueDetailRoot> = observer((props) => {
|
||||
try {
|
||||
const removeFromCyclePromise = removeIssueFromCycle(workspaceSlug, projectId, cycleId, issueId);
|
||||
setPromiseToast(removeFromCyclePromise, {
|
||||
loading: "Removing cycle from issue...",
|
||||
loading: "Removing issue from the cycle...",
|
||||
success: {
|
||||
title: "Success!",
|
||||
message: () => "Cycle removed from issue successfully",
|
||||
message: () => "Issue removed from the cycle successfully.",
|
||||
},
|
||||
error: {
|
||||
title: "Error!",
|
||||
message: () => "Cycle remove from issue failed",
|
||||
message: () => "Issue could not be removed from the cycle. Please try again.",
|
||||
},
|
||||
});
|
||||
await removeFromCyclePromise;
|
||||
@ -232,19 +256,7 @@ export const IssueDetailRoot: FC<TIssueDetailRoot> = observer((props) => {
|
||||
},
|
||||
addModulesToIssue: async (workspaceSlug: string, projectId: string, issueId: string, moduleIds: string[]) => {
|
||||
try {
|
||||
const addToModulePromise = addModulesToIssue(workspaceSlug, projectId, issueId, moduleIds);
|
||||
setPromiseToast(addToModulePromise, {
|
||||
loading: "Adding module to issue...",
|
||||
success: {
|
||||
title: "Success!",
|
||||
message: () => "Module added to issue successfully",
|
||||
},
|
||||
error: {
|
||||
title: "Error!",
|
||||
message: () => "Module add to issue failed",
|
||||
},
|
||||
});
|
||||
const response = await addToModulePromise;
|
||||
const response = await addModulesToIssue(workspaceSlug, projectId, issueId, moduleIds);
|
||||
captureIssueEvent({
|
||||
eventName: ISSUE_UPDATED,
|
||||
payload: { ...response, state: "SUCCESS", element: "Issue detail page" },
|
||||
@ -255,6 +267,11 @@ export const IssueDetailRoot: FC<TIssueDetailRoot> = observer((props) => {
|
||||
path: router.asPath,
|
||||
});
|
||||
} catch (error) {
|
||||
setToast({
|
||||
type: TOAST_TYPE.ERROR,
|
||||
title: "Error!",
|
||||
message: "Issue could not be added to the module. Please try again.",
|
||||
});
|
||||
captureIssueEvent({
|
||||
eventName: ISSUE_UPDATED,
|
||||
payload: { id: issueId, state: "FAILED", element: "Issue detail page" },
|
||||
@ -270,14 +287,14 @@ export const IssueDetailRoot: FC<TIssueDetailRoot> = observer((props) => {
|
||||
try {
|
||||
const removeFromModulePromise = removeIssueFromModule(workspaceSlug, projectId, moduleId, issueId);
|
||||
setPromiseToast(removeFromModulePromise, {
|
||||
loading: "Removing module from issue...",
|
||||
loading: "Removing issue from the module...",
|
||||
success: {
|
||||
title: "Success!",
|
||||
message: () => "Module removed from issue successfully",
|
||||
message: () => "Issue removed from the module successfully.",
|
||||
},
|
||||
error: {
|
||||
title: "Error!",
|
||||
message: () => "Module remove from issue failed",
|
||||
message: () => "Issue could not be removed from the module. Please try again.",
|
||||
},
|
||||
});
|
||||
await removeFromModulePromise;
|
||||
@ -335,6 +352,8 @@ export const IssueDetailRoot: FC<TIssueDetailRoot> = observer((props) => {
|
||||
addModulesToIssue,
|
||||
removeIssueFromModule,
|
||||
removeModulesFromIssue,
|
||||
captureIssueEvent,
|
||||
router.asPath,
|
||||
]
|
||||
);
|
||||
|
||||
|
@ -1,19 +1,18 @@
|
||||
import { FC, useEffect, useState, useMemo } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useRouter } from "next/router";
|
||||
// types
|
||||
import { TIssue } from "@plane/types";
|
||||
// hooks
|
||||
import { TOAST_TYPE, setPromiseToast, setToast } from "@plane/ui";
|
||||
import { IssueView } from "@/components/issues";
|
||||
// ui
|
||||
import { TOAST_TYPE, setPromiseToast, setToast } from "@plane/ui";
|
||||
// components
|
||||
import { IssueView } from "@/components/issues";
|
||||
// constants
|
||||
import { ISSUE_UPDATED, ISSUE_DELETED, ISSUE_ARCHIVED, ISSUE_RESTORED } from "@/constants/event-tracker";
|
||||
import { EIssuesStoreType } from "@/constants/issue";
|
||||
import { EUserProjectRoles } from "@/constants/project";
|
||||
// hooks
|
||||
import { useEventTracker, useIssueDetail, useIssues, useUser } from "@/hooks/store";
|
||||
// components
|
||||
// types
|
||||
// constants
|
||||
|
||||
interface IIssuePeekOverview {
|
||||
is_archived?: boolean;
|
||||
@ -60,8 +59,14 @@ export const IssuePeekOverview: FC<IIssuePeekOverview> = observer((props) => {
|
||||
archiveIssue,
|
||||
issue: { getIssueById, fetchIssue },
|
||||
} = useIssueDetail();
|
||||
const { addIssueToCycle, removeIssueFromCycle, addModulesToIssue, removeIssueFromModule, removeModulesFromIssue } =
|
||||
useIssueDetail();
|
||||
const {
|
||||
addCycleToIssue,
|
||||
addIssueToCycle,
|
||||
removeIssueFromCycle,
|
||||
addModulesToIssue,
|
||||
removeIssueFromModule,
|
||||
removeModulesFromIssue,
|
||||
} = useIssueDetail();
|
||||
const { captureIssueEvent } = useEventTracker();
|
||||
// state
|
||||
const [loader, setLoader] = useState(false);
|
||||
@ -174,21 +179,39 @@ export const IssuePeekOverview: FC<IIssuePeekOverview> = observer((props) => {
|
||||
});
|
||||
}
|
||||
},
|
||||
addCycleToIssue: async (workspaceSlug: string, projectId: string, cycleId: string, issueId: string) => {
|
||||
try {
|
||||
console.log("Peek adding...");
|
||||
await addCycleToIssue(workspaceSlug, projectId, cycleId, issueId);
|
||||
captureIssueEvent({
|
||||
eventName: ISSUE_UPDATED,
|
||||
payload: { issueId, state: "SUCCESS", element: "Issue peek-overview" },
|
||||
updates: {
|
||||
changed_property: "cycle_id",
|
||||
change_details: cycleId,
|
||||
},
|
||||
path: router.asPath,
|
||||
});
|
||||
} catch (error) {
|
||||
setToast({
|
||||
type: TOAST_TYPE.ERROR,
|
||||
title: "Error!",
|
||||
message: "Issue could not be added to the cycle. Please try again.",
|
||||
});
|
||||
captureIssueEvent({
|
||||
eventName: ISSUE_UPDATED,
|
||||
payload: { state: "FAILED", element: "Issue peek-overview" },
|
||||
updates: {
|
||||
changed_property: "cycle_id",
|
||||
change_details: cycleId,
|
||||
},
|
||||
path: router.asPath,
|
||||
});
|
||||
}
|
||||
},
|
||||
addIssueToCycle: async (workspaceSlug: string, projectId: string, cycleId: string, issueIds: string[]) => {
|
||||
try {
|
||||
const addToCyclePromise = addIssueToCycle(workspaceSlug, projectId, cycleId, issueIds);
|
||||
setPromiseToast(addToCyclePromise, {
|
||||
loading: "Adding cycle to issue...",
|
||||
success: {
|
||||
title: "Success!",
|
||||
message: () => "Cycle added to issue successfully",
|
||||
},
|
||||
error: {
|
||||
title: "Error!",
|
||||
message: () => "Cycle add to issue failed",
|
||||
},
|
||||
});
|
||||
await addToCyclePromise;
|
||||
await addIssueToCycle(workspaceSlug, projectId, cycleId, issueIds);
|
||||
captureIssueEvent({
|
||||
eventName: ISSUE_UPDATED,
|
||||
payload: { ...issueIds, state: "SUCCESS", element: "Issue peek-overview" },
|
||||
@ -199,6 +222,11 @@ export const IssuePeekOverview: FC<IIssuePeekOverview> = observer((props) => {
|
||||
path: router.asPath,
|
||||
});
|
||||
} catch (error) {
|
||||
setToast({
|
||||
type: TOAST_TYPE.ERROR,
|
||||
title: "Error!",
|
||||
message: "Issue could not be added to the cycle. Please try again.",
|
||||
});
|
||||
captureIssueEvent({
|
||||
eventName: ISSUE_UPDATED,
|
||||
payload: { state: "FAILED", element: "Issue peek-overview" },
|
||||
@ -214,14 +242,14 @@ export const IssuePeekOverview: FC<IIssuePeekOverview> = observer((props) => {
|
||||
try {
|
||||
const removeFromCyclePromise = removeIssueFromCycle(workspaceSlug, projectId, cycleId, issueId);
|
||||
setPromiseToast(removeFromCyclePromise, {
|
||||
loading: "Removing cycle from issue...",
|
||||
loading: "Removing issue from the cycle...",
|
||||
success: {
|
||||
title: "Success!",
|
||||
message: () => "Cycle removed from issue successfully",
|
||||
message: () => "Issue removed from the cycle successfully.",
|
||||
},
|
||||
error: {
|
||||
title: "Error!",
|
||||
message: () => "Cycle remove from issue failed",
|
||||
message: () => "Issue could not be removed from the cycle. Please try again.",
|
||||
},
|
||||
});
|
||||
await removeFromCyclePromise;
|
||||
@ -248,19 +276,7 @@ export const IssuePeekOverview: FC<IIssuePeekOverview> = observer((props) => {
|
||||
},
|
||||
addModulesToIssue: async (workspaceSlug: string, projectId: string, issueId: string, moduleIds: string[]) => {
|
||||
try {
|
||||
const addToModulePromise = addModulesToIssue(workspaceSlug, projectId, issueId, moduleIds);
|
||||
setPromiseToast(addToModulePromise, {
|
||||
loading: "Adding module to issue...",
|
||||
success: {
|
||||
title: "Success!",
|
||||
message: () => "Module added to issue successfully",
|
||||
},
|
||||
error: {
|
||||
title: "Error!",
|
||||
message: () => "Module add to issue failed",
|
||||
},
|
||||
});
|
||||
const response = await addToModulePromise;
|
||||
const response = await addModulesToIssue(workspaceSlug, projectId, issueId, moduleIds);
|
||||
captureIssueEvent({
|
||||
eventName: ISSUE_UPDATED,
|
||||
payload: { ...response, state: "SUCCESS", element: "Issue peek-overview" },
|
||||
@ -271,6 +287,11 @@ export const IssuePeekOverview: FC<IIssuePeekOverview> = observer((props) => {
|
||||
path: router.asPath,
|
||||
});
|
||||
} catch (error) {
|
||||
setToast({
|
||||
type: TOAST_TYPE.ERROR,
|
||||
title: "Error!",
|
||||
message: "Issue could not be added to the module. Please try again.",
|
||||
});
|
||||
captureIssueEvent({
|
||||
eventName: ISSUE_UPDATED,
|
||||
payload: { id: issueId, state: "FAILED", element: "Issue peek-overview" },
|
||||
@ -286,14 +307,14 @@ export const IssuePeekOverview: FC<IIssuePeekOverview> = observer((props) => {
|
||||
try {
|
||||
const removeFromModulePromise = removeIssueFromModule(workspaceSlug, projectId, moduleId, issueId);
|
||||
setPromiseToast(removeFromModulePromise, {
|
||||
loading: "Removing module from issue...",
|
||||
loading: "Removing issue from the module...",
|
||||
success: {
|
||||
title: "Success!",
|
||||
message: () => "Module removed from issue successfully",
|
||||
message: () => "Issue removed from the module successfully.",
|
||||
},
|
||||
error: {
|
||||
title: "Error!",
|
||||
message: () => "Module remove from issue failed",
|
||||
message: () => "Issue could not be removed from the module. Please try again.",
|
||||
},
|
||||
});
|
||||
await removeFromModulePromise;
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { EIssueFilterType, EIssuesStoreType } from "@/constants/issue";
|
||||
import { useApplication, useIssues } from "./store";
|
||||
import { useCallback, useMemo } from "react";
|
||||
// types
|
||||
import {
|
||||
IIssueDisplayFilterOptions,
|
||||
IIssueDisplayProperties,
|
||||
@ -8,7 +8,10 @@ import {
|
||||
TIssueKanbanFilters,
|
||||
TLoader,
|
||||
} from "@plane/types";
|
||||
import { useCallback, useMemo } from "react";
|
||||
// constants
|
||||
import { EIssueFilterType, EIssuesStoreType } from "@/constants/issue";
|
||||
// hooks
|
||||
import { useApplication, useIssues } from "./store";
|
||||
|
||||
interface IssueActions {
|
||||
fetchIssues?: (projectId: string, loadType: TLoader) => Promise<TIssue[] | undefined>;
|
||||
@ -238,9 +241,9 @@ const useModuleIssueActions = () => {
|
||||
const removeIssueFromView = useCallback(
|
||||
async (projectId: string, issueId: string) => {
|
||||
if (!moduleId || !workspaceSlug) return;
|
||||
return await issues.removeIssueFromModule(workspaceSlug, projectId, moduleId, issueId);
|
||||
return await issues.removeIssuesFromModule(workspaceSlug, projectId, moduleId, [issueId]);
|
||||
},
|
||||
[issues.removeIssueFromModule, moduleId, workspaceSlug]
|
||||
[issues.removeIssuesFromModule, moduleId, workspaceSlug]
|
||||
);
|
||||
const archiveIssue = useCallback(
|
||||
async (projectId: string, issueId: string) => {
|
||||
|
@ -1,9 +1,9 @@
|
||||
// services
|
||||
import { API_BASE_URL } from "@/helpers/common.helper";
|
||||
import { APIService } from "@/services/api.service";
|
||||
// type
|
||||
// types
|
||||
import type { TIssue, IIssueDisplayProperties, TIssueLink, TIssueSubIssues, TIssueActivity } from "@plane/types";
|
||||
// helper
|
||||
// helpers
|
||||
import { API_BASE_URL } from "@/helpers/common.helper";
|
||||
// services
|
||||
import { APIService } from "@/services/api.service";
|
||||
|
||||
export class IssueService extends APIService {
|
||||
constructor() {
|
||||
|
@ -1,8 +1,8 @@
|
||||
// types
|
||||
import type { IModule, TIssue, ILinkDetails, ModuleLink } from "@plane/types";
|
||||
// services
|
||||
import { API_BASE_URL } from "@/helpers/common.helper";
|
||||
import { APIService } from "@/services/api.service";
|
||||
// types
|
||||
import type { IModule, TIssue, ILinkDetails, ModuleLink } from "@plane/types";
|
||||
|
||||
export class ModuleService extends APIService {
|
||||
constructor() {
|
||||
@ -106,19 +106,6 @@ export class ModuleService extends APIService {
|
||||
});
|
||||
}
|
||||
|
||||
async removeIssueFromModule(
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
moduleId: string,
|
||||
issueId: string
|
||||
): Promise<any> {
|
||||
return this.delete(`/api/workspaces/${workspaceSlug}/projects/${projectId}/modules/${moduleId}/issues/${issueId}/`)
|
||||
.then((response) => response?.data)
|
||||
.catch((error) => {
|
||||
throw error?.response?.data;
|
||||
});
|
||||
}
|
||||
|
||||
async removeIssuesFromModuleBulk(
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
|
@ -4,12 +4,12 @@ import set from "lodash/set";
|
||||
import uniq from "lodash/uniq";
|
||||
import update from "lodash/update";
|
||||
import { action, observable, makeObservable, computed, runInAction } from "mobx";
|
||||
// base class
|
||||
// types
|
||||
import { TIssue, TSubGroupedIssues, TGroupedIssues, TLoader, TUnGroupedIssues, ViewFlags } from "@plane/types";
|
||||
// services
|
||||
import { CycleService } from "@/services/cycle.service";
|
||||
import { IssueService } from "@/services/issue";
|
||||
// types
|
||||
import { TIssue, TSubGroupedIssues, TGroupedIssues, TLoader, TUnGroupedIssues, ViewFlags } from "@plane/types";
|
||||
import { IssueHelperStore } from "../helpers/issue-helper.store";
|
||||
import { IIssueRootStore } from "../root.store";
|
||||
|
||||
@ -59,6 +59,7 @@ export interface ICycleIssues {
|
||||
fetchAddedIssues?: boolean
|
||||
) => Promise<void>;
|
||||
removeIssueFromCycle: (workspaceSlug: string, projectId: string, cycleId: string, issueId: string) => Promise<void>;
|
||||
addCycleToIssue: (workspaceSlug: string, projectId: string, cycleId: string, issueId: string) => Promise<void>;
|
||||
transferIssuesFromCycle: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
@ -101,6 +102,7 @@ export class CycleIssues extends IssueHelperStore implements ICycleIssues {
|
||||
quickAddIssue: action,
|
||||
addIssueToCycle: action,
|
||||
removeIssueFromCycle: action,
|
||||
addCycleToIssue: action,
|
||||
transferIssuesFromCycle: action,
|
||||
fetchActiveCycleIssues: action,
|
||||
});
|
||||
@ -303,18 +305,22 @@ export class CycleIssues extends IssueHelperStore implements ICycleIssues {
|
||||
|
||||
if (fetchAddedIssues) await this.rootIssueStore.issues.getIssues(workspaceSlug, projectId, issueIds);
|
||||
|
||||
// add the new issue ids to the cycle issues map
|
||||
runInAction(() => {
|
||||
update(this.issues, cycleId, (cycleIssueIds = []) => uniq(concat(cycleIssueIds, issueIds)));
|
||||
});
|
||||
issueIds.forEach((issueId) => {
|
||||
const issueCycleId = this.rootIssueStore.issues.getIssueById(issueId)?.cycle_id;
|
||||
// remove issue from previous cycle if it exists
|
||||
if (issueCycleId && issueCycleId !== cycleId) {
|
||||
runInAction(() => {
|
||||
pull(this.issues[issueCycleId], issueId);
|
||||
});
|
||||
}
|
||||
// update the root issue map with the new cycle id
|
||||
this.rootStore.issues.updateIssue(issueId, { cycle_id: cycleId });
|
||||
});
|
||||
|
||||
this.rootIssueStore.rootStore.cycle.fetchCycleDetails(workspaceSlug, projectId, cycleId);
|
||||
} catch (error) {
|
||||
throw error;
|
||||
@ -336,6 +342,43 @@ export class CycleIssues extends IssueHelperStore implements ICycleIssues {
|
||||
}
|
||||
};
|
||||
|
||||
addCycleToIssue = async (workspaceSlug: string, projectId: string, cycleId: string, issueId: string) => {
|
||||
const issueCycleId = this.rootIssueStore.issues.getIssueById(issueId)?.cycle_id;
|
||||
try {
|
||||
// add the new issue ids to the cycle issues map
|
||||
runInAction(() => {
|
||||
update(this.issues, cycleId, (cycleIssueIds = []) => uniq(concat(cycleIssueIds, [issueId])));
|
||||
});
|
||||
// remove issue from previous cycle if it exists
|
||||
if (issueCycleId && issueCycleId !== cycleId) {
|
||||
runInAction(() => {
|
||||
pull(this.issues[issueCycleId], issueId);
|
||||
});
|
||||
}
|
||||
// update the root issue map with the new cycle id
|
||||
this.rootStore.issues.updateIssue(issueId, { cycle_id: cycleId });
|
||||
|
||||
await this.issueService.addIssueToCycle(workspaceSlug, projectId, cycleId, {
|
||||
issues: [issueId],
|
||||
});
|
||||
|
||||
this.rootIssueStore.rootStore.cycle.fetchCycleDetails(workspaceSlug, projectId, cycleId);
|
||||
} catch (error) {
|
||||
// remove the new issue ids from the cycle issues map
|
||||
runInAction(() => {
|
||||
pull(this.issues[cycleId], issueId);
|
||||
});
|
||||
// add issue back to the previous cycle if it exists
|
||||
if (issueCycleId)
|
||||
runInAction(() => {
|
||||
update(this.issues, issueCycleId, (cycleIssueIds = []) => uniq(concat(cycleIssueIds, [issueId])));
|
||||
});
|
||||
// update the root issue map with the original cycle id
|
||||
this.rootStore.issues.updateIssue(issueId, { cycle_id: issueCycleId ?? null });
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
transferIssuesFromCycle = async (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
|
@ -1,9 +1,10 @@
|
||||
import { makeObservable } from "mobx";
|
||||
// services
|
||||
import { computedFn } from "mobx-utils";
|
||||
import { IssueArchiveService, IssueDraftService, IssueService } from "@/services/issue";
|
||||
// types
|
||||
import { TIssue } from "@plane/types";
|
||||
// services
|
||||
import { IssueArchiveService, IssueDraftService, IssueService } from "@/services/issue";
|
||||
// types
|
||||
import { IIssueDetail } from "./root.store";
|
||||
|
||||
export interface IIssueStoreActions {
|
||||
@ -17,6 +18,7 @@ export interface IIssueStoreActions {
|
||||
updateIssue: (workspaceSlug: string, projectId: string, issueId: string, data: Partial<TIssue>) => Promise<void>;
|
||||
removeIssue: (workspaceSlug: string, projectId: string, issueId: string) => Promise<void>;
|
||||
archiveIssue: (workspaceSlug: string, projectId: string, issueId: string) => Promise<void>;
|
||||
addCycleToIssue: (workspaceSlug: string, projectId: string, cycleId: string, issueId: string) => Promise<void>;
|
||||
addIssueToCycle: (workspaceSlug: string, projectId: string, cycleId: string, issueIds: string[]) => Promise<void>;
|
||||
removeIssueFromCycle: (workspaceSlug: string, projectId: string, cycleId: string, issueId: string) => Promise<void>;
|
||||
addModulesToIssue: (workspaceSlug: string, projectId: string, issueId: string, moduleIds: string[]) => Promise<any>;
|
||||
@ -161,6 +163,16 @@ export class IssueStore implements IIssueStore {
|
||||
archiveIssue = async (workspaceSlug: string, projectId: string, issueId: string) =>
|
||||
this.rootIssueDetailStore.rootIssueStore.projectIssues.archiveIssue(workspaceSlug, projectId, issueId);
|
||||
|
||||
addCycleToIssue = async (workspaceSlug: string, projectId: string, cycleId: string, issueId: string) => {
|
||||
await this.rootIssueDetailStore.rootIssueStore.cycleIssues.addCycleToIssue(
|
||||
workspaceSlug,
|
||||
projectId,
|
||||
cycleId,
|
||||
issueId
|
||||
);
|
||||
await this.rootIssueDetailStore.activity.fetchActivities(workspaceSlug, projectId, issueId);
|
||||
};
|
||||
|
||||
addIssueToCycle = async (workspaceSlug: string, projectId: string, cycleId: string, issueIds: string[]) => {
|
||||
await this.rootIssueDetailStore.rootIssueStore.cycleIssues.addIssueToCycle(
|
||||
workspaceSlug,
|
||||
@ -208,11 +220,11 @@ export class IssueStore implements IIssueStore {
|
||||
};
|
||||
|
||||
removeIssueFromModule = async (workspaceSlug: string, projectId: string, moduleId: string, issueId: string) => {
|
||||
const currentModule = await this.rootIssueDetailStore.rootIssueStore.moduleIssues.removeIssueFromModule(
|
||||
const currentModule = await this.rootIssueDetailStore.rootIssueStore.moduleIssues.removeIssuesFromModule(
|
||||
workspaceSlug,
|
||||
projectId,
|
||||
moduleId,
|
||||
issueId
|
||||
[issueId]
|
||||
);
|
||||
await this.rootIssueDetailStore.activity.fetchActivities(workspaceSlug, projectId, issueId);
|
||||
return currentModule;
|
||||
|
@ -191,6 +191,8 @@ export class IssueDetail implements IIssueDetail {
|
||||
this.issue.removeIssue(workspaceSlug, projectId, issueId);
|
||||
archiveIssue = async (workspaceSlug: string, projectId: string, issueId: string) =>
|
||||
this.issue.archiveIssue(workspaceSlug, projectId, issueId);
|
||||
addCycleToIssue = async (workspaceSlug: string, projectId: string, cycleId: string, issueId: string) =>
|
||||
this.issue.addCycleToIssue(workspaceSlug, projectId, cycleId, issueId);
|
||||
addIssueToCycle = async (workspaceSlug: string, projectId: string, cycleId: string, issueIds: string[]) =>
|
||||
this.issue.addIssueToCycle(workspaceSlug, projectId, cycleId, issueIds);
|
||||
removeIssueFromCycle = async (workspaceSlug: string, projectId: string, cycleId: string, issueId: string) =>
|
||||
|
@ -1,13 +1,13 @@
|
||||
import isEmpty from "lodash/isEmpty";
|
||||
import set from "lodash/set";
|
||||
// store
|
||||
import { action, makeObservable, observable, runInAction } from "mobx";
|
||||
import { computedFn } from "mobx-utils";
|
||||
// types
|
||||
import { IssueService } from "@/services/issue";
|
||||
import { TIssue } from "@plane/types";
|
||||
// helpers
|
||||
import { getCurrentDateTimeInISO } from "@/helpers/date-time.helper";
|
||||
// services
|
||||
import { IssueService } from "@/services/issue";
|
||||
|
||||
export type IIssueStore = {
|
||||
// observables
|
||||
|
@ -4,13 +4,14 @@ import set from "lodash/set";
|
||||
import uniq from "lodash/uniq";
|
||||
import update from "lodash/update";
|
||||
import { action, observable, makeObservable, computed, runInAction } from "mobx";
|
||||
// base class
|
||||
// types
|
||||
import { TIssue, TLoader, TGroupedIssues, TSubGroupedIssues, TUnGroupedIssues, ViewFlags } from "@plane/types";
|
||||
// services
|
||||
import { IssueService } from "@/services/issue";
|
||||
import { ModuleService } from "@/services/module.service";
|
||||
// types
|
||||
import { TIssue, TLoader, TGroupedIssues, TSubGroupedIssues, TUnGroupedIssues, ViewFlags } from "@plane/types";
|
||||
// helpers
|
||||
import { IssueHelperStore } from "../helpers/issue-helper.store";
|
||||
// store
|
||||
import { IIssueRootStore } from "../root.store";
|
||||
|
||||
export interface IModuleIssues {
|
||||
@ -69,7 +70,6 @@ export interface IModuleIssues {
|
||||
issueId: string,
|
||||
moduleIds: string[]
|
||||
) => Promise<void>;
|
||||
removeIssueFromModule: (workspaceSlug: string, projectId: string, moduleId: string, issueId: string) => Promise<void>;
|
||||
}
|
||||
|
||||
export class ModuleIssues extends IssueHelperStore implements IModuleIssues {
|
||||
@ -106,7 +106,6 @@ export class ModuleIssues extends IssueHelperStore implements IModuleIssues {
|
||||
removeIssuesFromModule: action,
|
||||
addModulesToIssue: action,
|
||||
removeModulesFromIssue: action,
|
||||
removeIssueFromModule: action,
|
||||
});
|
||||
|
||||
this.rootIssueStore = _rootStore;
|
||||
@ -301,27 +300,39 @@ export class ModuleIssues extends IssueHelperStore implements IModuleIssues {
|
||||
fetchAddedIssues = true
|
||||
) => {
|
||||
try {
|
||||
await this.moduleService.addIssuesToModule(workspaceSlug, projectId, moduleId, {
|
||||
issues: issueIds,
|
||||
});
|
||||
|
||||
if (fetchAddedIssues) await this.rootIssueStore.issues.getIssues(workspaceSlug, projectId, issueIds);
|
||||
|
||||
// add the new issue ids to the module issues map
|
||||
runInAction(() => {
|
||||
update(this.issues, moduleId, (moduleIssueIds = []) => {
|
||||
if (!moduleIssueIds) return [...issueIds];
|
||||
else return uniq(concat(moduleIssueIds, issueIds));
|
||||
});
|
||||
});
|
||||
|
||||
// update the root issue map with the new module ids
|
||||
issueIds.forEach((issueId) => {
|
||||
update(this.rootStore.issues.issuesMap, [issueId, "module_ids"], (issueModuleIds = []) => {
|
||||
if (issueModuleIds.includes(moduleId)) return issueModuleIds;
|
||||
else return uniq(concat(issueModuleIds, [moduleId]));
|
||||
});
|
||||
});
|
||||
|
||||
await this.moduleService.addIssuesToModule(workspaceSlug, projectId, moduleId, {
|
||||
issues: issueIds,
|
||||
});
|
||||
|
||||
if (fetchAddedIssues) await this.rootIssueStore.issues.getIssues(workspaceSlug, projectId, issueIds);
|
||||
|
||||
this.rootIssueStore.rootStore.module.fetchModuleDetails(workspaceSlug, projectId, moduleId);
|
||||
} catch (error) {
|
||||
issueIds.forEach((issueId) => {
|
||||
runInAction(() => {
|
||||
// remove the new issue ids from the module issues map
|
||||
pull(this.issues[moduleId], issueId);
|
||||
// remove the new module ids from the root issue map
|
||||
update(this.rootStore.issues.issuesMap, [issueId, "module_ids"], (issueModuleIds = []) =>
|
||||
pull(issueModuleIds, moduleId)
|
||||
);
|
||||
});
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
@ -358,25 +369,38 @@ export class ModuleIssues extends IssueHelperStore implements IModuleIssues {
|
||||
};
|
||||
|
||||
addModulesToIssue = async (workspaceSlug: string, projectId: string, issueId: string, moduleIds: string[]) => {
|
||||
// keep a copy of the original module ids
|
||||
const originalModuleIds = this.rootStore.issues.issuesMap[issueId]?.module_ids ?? [];
|
||||
try {
|
||||
const issueToModule = await this.moduleService.addModulesToIssue(workspaceSlug, projectId, issueId, {
|
||||
modules: moduleIds,
|
||||
});
|
||||
|
||||
runInAction(() => {
|
||||
// add the new issue ids to the module issues map
|
||||
moduleIds.forEach((moduleId) => {
|
||||
update(this.issues, moduleId, (moduleIssueIds = []) => {
|
||||
if (moduleIssueIds.includes(issueId)) return moduleIssueIds;
|
||||
else return uniq(concat(moduleIssueIds, [issueId]));
|
||||
});
|
||||
});
|
||||
// update the root issue map with the new module ids
|
||||
update(this.rootStore.issues.issuesMap, [issueId, "module_ids"], (issueModuleIds = []) =>
|
||||
uniq(concat(issueModuleIds, moduleIds))
|
||||
);
|
||||
});
|
||||
|
||||
const issueToModule = await this.moduleService.addModulesToIssue(workspaceSlug, projectId, issueId, {
|
||||
modules: moduleIds,
|
||||
});
|
||||
|
||||
return issueToModule;
|
||||
} catch (error) {
|
||||
// revert the issue back to its original module ids
|
||||
set(this.rootStore.issues.issuesMap, [issueId, "module_ids"], originalModuleIds);
|
||||
// remove the new issue ids from the module issues map
|
||||
moduleIds.forEach((moduleId) => {
|
||||
runInAction(() => {
|
||||
update(this.issues, moduleId, (moduleIssueIds = []) => pull(moduleIssueIds, issueId));
|
||||
});
|
||||
});
|
||||
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
@ -407,21 +431,4 @@ export class ModuleIssues extends IssueHelperStore implements IModuleIssues {
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
removeIssueFromModule = async (workspaceSlug: string, projectId: string, moduleId: string, issueId: string) => {
|
||||
try {
|
||||
runInAction(() => {
|
||||
pull(this.issues[moduleId], issueId);
|
||||
update(this.rootStore.issues.issuesMap, [issueId, "module_ids"], (issueModuleIds = []) =>
|
||||
pull(issueModuleIds, moduleId)
|
||||
);
|
||||
});
|
||||
|
||||
const response = await this.moduleService.removeIssueFromModule(workspaceSlug, projectId, moduleId, issueId);
|
||||
|
||||
return response;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
import isEmpty from "lodash/isEmpty";
|
||||
import { autorun, makeObservable, observable } from "mobx";
|
||||
import { ICycle, IIssueLabel, IModule, IProject, IState, IUserLite } from "@plane/types";
|
||||
// root store
|
||||
import { IWorkspaceMembership } from "@/store/member/workspace-member.store";
|
||||
import { ICycle, IIssueLabel, IModule, IProject, IState, IUserLite } from "@plane/types";
|
||||
import { RootStore } from "../root.store";
|
||||
import { IStateStore, StateStore } from "../state.store";
|
||||
// issues data store
|
||||
|
Loading…
Reference in New Issue
Block a user