mirror of
https://github.com/makeplane/plane.git
synced 2025-12-25 16:19:43 +01:00
[WEB-2043] fix: delete action validation and toast alert (#5254)
* dev: canPerformProjectAdminActions helper function added * chore: deleteInboxIssue action updated * dev: bulk delete modal validation updated * chore: issue, intake, cycle and module delete action toast updated * chore: code refactor
This commit is contained in:
committed by
GitHub
parent
ba9d9fd5eb
commit
35e58e9ec7
@@ -46,6 +46,7 @@ export const CommandPalette: FC = observer(() => {
|
||||
canPerformProjectCreateActions,
|
||||
canPerformWorkspaceCreateActions,
|
||||
canPerformAnyCreateAction,
|
||||
canPerformProjectAdminActions,
|
||||
} = useUser();
|
||||
const {
|
||||
issues: { removeIssue },
|
||||
@@ -113,6 +114,19 @@ export const CommandPalette: FC = observer(() => {
|
||||
[canPerformProjectCreateActions]
|
||||
);
|
||||
|
||||
const performProjectBulkDeleteActions = useCallback(
|
||||
(showToast: boolean = true) => {
|
||||
if (!canPerformProjectAdminActions && showToast)
|
||||
setToast({
|
||||
type: TOAST_TYPE.ERROR,
|
||||
title: "You don't have permission to perform this action.",
|
||||
});
|
||||
|
||||
return canPerformProjectAdminActions;
|
||||
},
|
||||
[canPerformProjectAdminActions]
|
||||
);
|
||||
|
||||
const performWorkspaceCreateActions = useCallback(
|
||||
(showToast: boolean = true) => {
|
||||
if (!canPerformWorkspaceCreateActions && showToast)
|
||||
@@ -210,6 +224,7 @@ export const CommandPalette: FC = observer(() => {
|
||||
const keyPressed = key.toLowerCase();
|
||||
const cmdClicked = ctrlKey || metaKey;
|
||||
const shiftClicked = shiftKey;
|
||||
const deleteKey = keyPressed === "backspace" || keyPressed === "delete";
|
||||
|
||||
if (cmdClicked && keyPressed === "k" && !isAnyModalOpen) {
|
||||
e.preventDefault();
|
||||
@@ -229,7 +244,11 @@ export const CommandPalette: FC = observer(() => {
|
||||
toggleShortcutModal(true);
|
||||
}
|
||||
|
||||
if (cmdClicked) {
|
||||
if (deleteKey) {
|
||||
if (performProjectBulkDeleteActions()) {
|
||||
shortcutsList.project.delete.action();
|
||||
}
|
||||
} else if (cmdClicked) {
|
||||
if (keyPressed === "c" && ((platform === "MacOS" && ctrlKey) || altKey)) {
|
||||
e.preventDefault();
|
||||
copyIssueUrlToClipboard();
|
||||
@@ -266,6 +285,7 @@ export const CommandPalette: FC = observer(() => {
|
||||
[
|
||||
performAnyProjectCreateActions,
|
||||
performProjectCreateActions,
|
||||
performProjectBulkDeleteActions,
|
||||
performWorkspaceCreateActions,
|
||||
copyIssueUrlToClipboard,
|
||||
isAnyModalOpen,
|
||||
|
||||
@@ -9,6 +9,7 @@ import { ICycle } from "@plane/types";
|
||||
import { AlertModalCore, TOAST_TYPE, setToast } from "@plane/ui";
|
||||
// constants
|
||||
import { CYCLE_DELETED } from "@/constants/event-tracker";
|
||||
import { PROJECT_ERROR_MESSAGES } from "@/constants/project";
|
||||
// hooks
|
||||
import { useEventTracker, useCycle } from "@/hooks/store";
|
||||
import { useAppRouter } from "@/hooks/use-app-router";
|
||||
@@ -51,16 +52,24 @@ export const CycleDeleteModal: React.FC<ICycleDelete> = observer((props) => {
|
||||
payload: { ...cycle, state: "SUCCESS" },
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
.catch((errors) => {
|
||||
const isPermissionError = errors?.error === "Only admin or owner can delete the cycle";
|
||||
const currentError = isPermissionError
|
||||
? PROJECT_ERROR_MESSAGES.permissionError
|
||||
: PROJECT_ERROR_MESSAGES.cycleDeleteError;
|
||||
setToast({
|
||||
title: currentError.title,
|
||||
type: TOAST_TYPE.ERROR,
|
||||
message: currentError.message,
|
||||
});
|
||||
captureCycleEvent({
|
||||
eventName: CYCLE_DELETED,
|
||||
payload: { ...cycle, state: "FAILED" },
|
||||
});
|
||||
});
|
||||
})
|
||||
.finally(() => handleClose());
|
||||
|
||||
if (cycleId || peekCycle) router.push(`/${workspaceSlug}/projects/${projectId}/cycles`);
|
||||
|
||||
handleClose();
|
||||
} catch (error) {
|
||||
setToast({
|
||||
type: TOAST_TYPE.ERROR,
|
||||
|
||||
@@ -3,7 +3,9 @@ import { observer } from "mobx-react";
|
||||
// types
|
||||
import type { TIssue } from "@plane/types";
|
||||
// ui
|
||||
import { AlertModalCore } from "@plane/ui";
|
||||
import { AlertModalCore, setToast, TOAST_TYPE } from "@plane/ui";
|
||||
// constants
|
||||
import { PROJECT_ERROR_MESSAGES } from "@/constants/project";
|
||||
// hooks
|
||||
import { useProject } from "@/hooks/store";
|
||||
|
||||
@@ -29,7 +31,26 @@ export const DeleteInboxIssueModal: React.FC<Props> = observer(({ isOpen, onClos
|
||||
|
||||
const handleDelete = async () => {
|
||||
setIsDeleting(true);
|
||||
await onSubmit().finally(() => handleClose());
|
||||
await onSubmit()
|
||||
.then(() => {
|
||||
setToast({
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
title: "Success!",
|
||||
message: `Issue deleted successfully`,
|
||||
});
|
||||
})
|
||||
.catch((errors) => {
|
||||
const isPermissionError = errors?.error === "Only admin or creator can delete the issue";
|
||||
const currentError = isPermissionError
|
||||
? PROJECT_ERROR_MESSAGES.permissionError
|
||||
: PROJECT_ERROR_MESSAGES.issueDeleteError;
|
||||
setToast({
|
||||
title: currentError.title,
|
||||
type: TOAST_TYPE.ERROR,
|
||||
message: currentError.message,
|
||||
});
|
||||
})
|
||||
.finally(() => handleClose());
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -5,6 +5,8 @@ import { useEffect, useState } from "react";
|
||||
import { TIssue } from "@plane/types";
|
||||
// ui
|
||||
import { AlertModalCore, TOAST_TYPE, setToast } from "@plane/ui";
|
||||
// constants
|
||||
import { PROJECT_ERROR_MESSAGES } from "@/constants/project";
|
||||
// hooks
|
||||
import { useIssues, useProject } from "@/hooks/store";
|
||||
|
||||
@@ -52,14 +54,18 @@ export const DeleteIssueModal: React.FC<Props> = (props) => {
|
||||
});
|
||||
onClose();
|
||||
})
|
||||
.catch(() => {
|
||||
.catch((errors) => {
|
||||
const isPermissionError = errors?.error === "Only admin or creator can delete the issue";
|
||||
const currentError = isPermissionError
|
||||
? PROJECT_ERROR_MESSAGES.permissionError
|
||||
: PROJECT_ERROR_MESSAGES.issueDeleteError;
|
||||
setToast({
|
||||
title: "Error",
|
||||
title: currentError.title,
|
||||
type: TOAST_TYPE.ERROR,
|
||||
message: "Failed to delete issue",
|
||||
message: currentError.message,
|
||||
});
|
||||
})
|
||||
.finally(() => setIsDeleting(false));
|
||||
.finally(() => onClose());
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -9,6 +9,7 @@ import type { IModule } from "@plane/types";
|
||||
import { AlertModalCore, TOAST_TYPE, setToast } from "@plane/ui";
|
||||
// constants
|
||||
import { MODULE_DELETED } from "@/constants/event-tracker";
|
||||
import { PROJECT_ERROR_MESSAGES } from "@/constants/project";
|
||||
// hooks
|
||||
import { useEventTracker, useModule } from "@/hooks/store";
|
||||
import { useAppRouter } from "@/hooks/use-app-router";
|
||||
@@ -54,20 +55,22 @@ export const DeleteModuleModal: React.FC<Props> = observer((props) => {
|
||||
payload: { ...data, state: "SUCCESS" },
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
.catch((errors) => {
|
||||
const isPermissionError = errors?.error === "Only admin or creator can delete the module";
|
||||
const currentError = isPermissionError
|
||||
? PROJECT_ERROR_MESSAGES.permissionError
|
||||
: PROJECT_ERROR_MESSAGES.moduleDeleteError;
|
||||
setToast({
|
||||
title: currentError.title,
|
||||
type: TOAST_TYPE.ERROR,
|
||||
title: "Error!",
|
||||
message: "Module could not be deleted. Please try again.",
|
||||
message: currentError.message,
|
||||
});
|
||||
captureModuleEvent({
|
||||
eventName: MODULE_DELETED,
|
||||
payload: { ...data, state: "FAILED" },
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
setIsDeleteLoading(false);
|
||||
});
|
||||
.finally(() => handleClose());
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -168,3 +168,22 @@ export const PROJECT_DISPLAY_FILTER_OPTIONS: {
|
||||
label: "Archived",
|
||||
},
|
||||
];
|
||||
|
||||
export const PROJECT_ERROR_MESSAGES = {
|
||||
permissionError: {
|
||||
title: "You don't have permission to perform this action.",
|
||||
message: undefined,
|
||||
},
|
||||
cycleDeleteError: {
|
||||
title: "Error",
|
||||
message: "Failed to delete project",
|
||||
},
|
||||
moduleDeleteError: {
|
||||
title: "Error",
|
||||
message: "Failed to delete module",
|
||||
},
|
||||
issueDeleteError: {
|
||||
title: "Error",
|
||||
message: "Failed to delete issue",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -455,11 +455,12 @@ export class ProjectInboxStore implements IProjectInboxStore {
|
||||
);
|
||||
});
|
||||
await this.inboxIssueService.destroy(workspaceSlug, projectId, inboxIssueId);
|
||||
} catch {
|
||||
} catch (error) {
|
||||
console.error("Error removing the intake issue");
|
||||
set(this.inboxIssues, [inboxIssueId], currentIssue);
|
||||
set(this, ["inboxIssuePaginationInfo", "total_results"], (this.inboxIssuePaginationInfo?.total_results || 0) + 1);
|
||||
set(this, ["inboxIssueIds"], [...this.inboxIssueIds, inboxIssueId]);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ export interface IUserStore {
|
||||
signOut: () => Promise<void>;
|
||||
// computed
|
||||
canPerformProjectCreateActions: boolean;
|
||||
canPerformProjectAdminActions: boolean;
|
||||
canPerformWorkspaceCreateActions: boolean;
|
||||
canPerformAnyCreateAction: boolean;
|
||||
projectsWithCreatePermissions: { [projectId: string]: number } | null;
|
||||
@@ -92,6 +93,7 @@ export class UserStore implements IUserStore {
|
||||
signOut: action,
|
||||
// computed
|
||||
canPerformProjectCreateActions: computed,
|
||||
canPerformProjectAdminActions: computed,
|
||||
canPerformWorkspaceCreateActions: computed,
|
||||
canPerformAnyCreateAction: computed,
|
||||
projectsWithCreatePermissions: computed,
|
||||
@@ -278,6 +280,14 @@ export class UserStore implements IUserStore {
|
||||
return !!this.membership.currentProjectRole && this.membership.currentProjectRole >= EUserProjectRoles.MEMBER;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description tells if user has project admin actions permissions
|
||||
* @returns {boolean}
|
||||
*/
|
||||
get canPerformProjectAdminActions() {
|
||||
return !!this.membership.currentProjectRole && this.membership.currentProjectRole === EUserProjectRoles.ADMIN;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description tells if user has workspace create actions permissions
|
||||
* @returns {boolean}
|
||||
|
||||
Reference in New Issue
Block a user