[WEB-2189] fix: issue peek overview and issue detail unauthorised delete action (#5341)

* fix: issue peek overview and issue detail delete action

* chore: code refactor

* chore: code refactor
This commit is contained in:
Anmol Singh Bhatia 2024-08-09 19:09:25 +05:30 committed by GitHub
parent 421bf2abc7
commit 679b0b6465
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 53 additions and 31 deletions

View file

@ -8,7 +8,7 @@ import { AlertModalCore, TOAST_TYPE, setToast } from "@plane/ui";
// constants
import { PROJECT_ERROR_MESSAGES } from "@/constants/project";
// hooks
import { useIssues, useProject } from "@/hooks/store";
import { useIssues, useProject, useUser } from "@/hooks/store";
type Props = {
isOpen: boolean;
@ -26,6 +26,7 @@ export const DeleteIssueModal: React.FC<Props> = (props) => {
// store hooks
const { issueMap } = useIssues();
const { getProjectById } = useProject();
const { data: currentUser, canPerformProjectAdminActions } = useUser();
useEffect(() => {
setIsDeleting(false);
@ -36,6 +37,8 @@ export const DeleteIssueModal: React.FC<Props> = (props) => {
// derived values
const issue = data ? data : issueMap[dataId!];
const projectDetails = getProjectById(issue?.project_id);
const isIssueCreator = issue?.created_by === currentUser?.id;
const authorized = isIssueCreator || canPerformProjectAdminActions;
const onClose = () => {
setIsDeleting(false);
@ -44,6 +47,16 @@ export const DeleteIssueModal: React.FC<Props> = (props) => {
const handleIssueDelete = async () => {
setIsDeleting(true);
if (!authorized) {
setToast({
title: PROJECT_ERROR_MESSAGES.permissionError.title,
type: TOAST_TYPE.ERROR,
message: PROJECT_ERROR_MESSAGES.permissionError.message,
});
onClose();
return;
}
if (onSubmit)
await onSubmit()
.then(() => {

View file

@ -70,11 +70,12 @@ export const useRelationOperations = (): TRelationIssueOperations => {
},
remove: async (workspaceSlug: string, projectId: string, issueId: string) => {
try {
await removeIssue(workspaceSlug, projectId, issueId);
captureIssueEvent({
eventName: ISSUE_DELETED,
payload: { id: issueId, state: "SUCCESS", element: "Issue detail page" },
path: pathname,
return removeIssue(workspaceSlug, projectId, issueId).then(() => {
captureIssueEvent({
eventName: ISSUE_DELETED,
payload: { id: issueId, state: "SUCCESS", element: "Issue detail page" },
path: pathname,
});
});
} catch (error) {
captureIssueEvent({

View file

@ -150,13 +150,14 @@ export const useSubIssueOperations = (): TSubIssueOperations => {
deleteSubIssue: async (workspaceSlug: string, projectId: string, parentIssueId: string, issueId: string) => {
try {
setSubIssueHelpers(parentIssueId, "issue_loader", issueId);
await deleteSubIssue(workspaceSlug, projectId, parentIssueId, issueId);
captureIssueEvent({
eventName: "Sub-issue deleted",
payload: { id: issueId, state: "SUCCESS", element: "Issue detail page" },
path: pathname,
return deleteSubIssue(workspaceSlug, projectId, parentIssueId, issueId).then(() => {
captureIssueEvent({
eventName: "Sub-issue deleted",
payload: { id: issueId, state: "SUCCESS", element: "Issue detail page" },
path: pathname,
});
setSubIssueHelpers(parentIssueId, "issue_loader", issueId);
});
setSubIssueHelpers(parentIssueId, "issue_loader", issueId);
} catch (error) {
captureIssueEvent({
eventName: "Sub-issue removed",

View file

@ -78,14 +78,18 @@ export const IssueDetailQuickActions: FC<Props> = observer((props) => {
const handleDeleteIssue = async () => {
try {
if (issue?.archived_at) await removeArchivedIssue(workspaceSlug, projectId, issueId);
else await removeIssue(workspaceSlug, projectId, issueId);
router.push(`/${workspaceSlug}/projects/${projectId}/issues`);
captureIssueEvent({
eventName: ISSUE_DELETED,
payload: { id: issueId, state: "SUCCESS", element: "Issue detail page" },
path: pathname,
});
if (issue?.archived_at) {
return removeArchivedIssue(workspaceSlug, projectId, issueId).then(() => {
router.push(`/${workspaceSlug}/projects/${projectId}/issues`);
captureIssueEvent({
eventName: ISSUE_DELETED,
payload: { id: issueId, state: "SUCCESS", element: "Issue detail page" },
path: pathname,
});
});
} else {
return removeIssue(workspaceSlug, projectId, issueId);
}
} catch (error) {
setToast({
title: "Error!",

View file

@ -34,6 +34,7 @@ export const IssuePeekOverview: FC<IIssuePeekOverview> = observer((props) => {
} = useIssues(EIssuesStoreType.ARCHIVED);
const {
peekIssue,
setPeekIssue,
issue: { fetchIssue },
fetchActivities,
} = useIssueDetail();
@ -44,6 +45,11 @@ export const IssuePeekOverview: FC<IIssuePeekOverview> = observer((props) => {
const [loader, setLoader] = useState(true);
const [error, setError] = useState(false);
const removeRoutePeekId = () => {
setPeekIssue(undefined);
if (embedIssue) embedRemoveCurrentNotification && embedRemoveCurrentNotification();
};
const issueOperations: TIssueOperations = useMemo(
() => ({
fetch: async (workspaceSlug: string, projectId: string, issueId: string, loader = true) => {
@ -95,16 +101,13 @@ export const IssuePeekOverview: FC<IIssuePeekOverview> = observer((props) => {
},
remove: async (workspaceSlug: string, projectId: string, issueId: string) => {
try {
issues?.removeIssue(workspaceSlug, projectId, issueId);
setToast({
title: "Success!",
type: TOAST_TYPE.SUCCESS,
message: "Issue deleted successfully",
});
captureIssueEvent({
eventName: ISSUE_DELETED,
payload: { id: issueId, state: "SUCCESS", element: "Issue peek-overview" },
path: pathname,
return issues?.removeIssue(workspaceSlug, projectId, issueId).then(() => {
captureIssueEvent({
eventName: ISSUE_DELETED,
payload: { id: issueId, state: "SUCCESS", element: "Issue peek-overview" },
path: pathname,
});
removeRoutePeekId();
});
} catch (error) {
setToast({

View file

@ -131,7 +131,7 @@ export const IssueView: FC<IIssueView> = observer((props) => {
toggleDeleteIssueModal(null);
}}
data={issue}
onSubmit={() => issueOperations.remove(workspaceSlug, projectId, issueId).then(() => removeRoutePeekId())}
onSubmit={async () => issueOperations.remove(workspaceSlug, projectId, issueId)}
/>
)}