Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion web/core/components/command-palette/command-palette.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export const CommandPalette: FC = observer(() => {
canPerformProjectCreateActions,
canPerformWorkspaceCreateActions,
canPerformAnyCreateAction,
canPerformProjectAdminActions,
} = useUser();
const {
issues: { removeIssue },
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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();
Expand All @@ -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();
Expand Down Expand Up @@ -266,6 +285,7 @@ export const CommandPalette: FC = observer(() => {
[
performAnyProjectCreateActions,
performProjectCreateActions,
performProjectBulkDeleteActions,
performWorkspaceCreateActions,
copyIssueUrlToClipboard,
isAnyModalOpen,
Expand Down
17 changes: 13 additions & 4 deletions web/core/components/cycles/delete-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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,
Expand Down
25 changes: 23 additions & 2 deletions web/core/components/inbox/modals/delete-issue-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand All @@ -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 (
Expand Down
14 changes: 10 additions & 4 deletions web/core/components/issues/delete-issue-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand Down Expand Up @@ -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 (
Expand Down
15 changes: 9 additions & 6 deletions web/core/components/modules/delete-module-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you write this better, instead of checking on the same condition twice.

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 (
Expand Down
19 changes: 19 additions & 0 deletions web/core/constants/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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",
},
};
3 changes: 2 additions & 1 deletion web/core/store/inbox/project-inbox.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
};
}
10 changes: 10 additions & 0 deletions web/core/store/user/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export interface IUserStore {
signOut: () => Promise<void>;
// computed
canPerformProjectCreateActions: boolean;
canPerformProjectAdminActions: boolean;
canPerformWorkspaceCreateActions: boolean;
canPerformAnyCreateAction: boolean;
projectsWithCreatePermissions: { [projectId: string]: number } | null;
Expand Down Expand Up @@ -92,6 +93,7 @@ export class UserStore implements IUserStore {
signOut: action,
// computed
canPerformProjectCreateActions: computed,
canPerformProjectAdminActions: computed,
canPerformWorkspaceCreateActions: computed,
canPerformAnyCreateAction: computed,
projectsWithCreatePermissions: computed,
Expand Down Expand Up @@ -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() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be isProjectAdmin.

return !!this.membership.currentProjectRole && this.membership.currentProjectRole === EUserProjectRoles.ADMIN;
}

/**
* @description tells if user has workspace create actions permissions
* @returns {boolean}
Expand Down