[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:
parent
421bf2abc7
commit
679b0b6465
6 changed files with 53 additions and 31 deletions
|
|
@ -8,7 +8,7 @@ import { AlertModalCore, TOAST_TYPE, setToast } from "@plane/ui";
|
||||||
// constants
|
// constants
|
||||||
import { PROJECT_ERROR_MESSAGES } from "@/constants/project";
|
import { PROJECT_ERROR_MESSAGES } from "@/constants/project";
|
||||||
// hooks
|
// hooks
|
||||||
import { useIssues, useProject } from "@/hooks/store";
|
import { useIssues, useProject, useUser } from "@/hooks/store";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
|
|
@ -26,6 +26,7 @@ export const DeleteIssueModal: React.FC<Props> = (props) => {
|
||||||
// store hooks
|
// store hooks
|
||||||
const { issueMap } = useIssues();
|
const { issueMap } = useIssues();
|
||||||
const { getProjectById } = useProject();
|
const { getProjectById } = useProject();
|
||||||
|
const { data: currentUser, canPerformProjectAdminActions } = useUser();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setIsDeleting(false);
|
setIsDeleting(false);
|
||||||
|
|
@ -36,6 +37,8 @@ export const DeleteIssueModal: React.FC<Props> = (props) => {
|
||||||
// derived values
|
// derived values
|
||||||
const issue = data ? data : issueMap[dataId!];
|
const issue = data ? data : issueMap[dataId!];
|
||||||
const projectDetails = getProjectById(issue?.project_id);
|
const projectDetails = getProjectById(issue?.project_id);
|
||||||
|
const isIssueCreator = issue?.created_by === currentUser?.id;
|
||||||
|
const authorized = isIssueCreator || canPerformProjectAdminActions;
|
||||||
|
|
||||||
const onClose = () => {
|
const onClose = () => {
|
||||||
setIsDeleting(false);
|
setIsDeleting(false);
|
||||||
|
|
@ -44,6 +47,16 @@ export const DeleteIssueModal: React.FC<Props> = (props) => {
|
||||||
|
|
||||||
const handleIssueDelete = async () => {
|
const handleIssueDelete = async () => {
|
||||||
setIsDeleting(true);
|
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)
|
if (onSubmit)
|
||||||
await onSubmit()
|
await onSubmit()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
|
|
||||||
|
|
@ -70,12 +70,13 @@ export const useRelationOperations = (): TRelationIssueOperations => {
|
||||||
},
|
},
|
||||||
remove: async (workspaceSlug: string, projectId: string, issueId: string) => {
|
remove: async (workspaceSlug: string, projectId: string, issueId: string) => {
|
||||||
try {
|
try {
|
||||||
await removeIssue(workspaceSlug, projectId, issueId);
|
return removeIssue(workspaceSlug, projectId, issueId).then(() => {
|
||||||
captureIssueEvent({
|
captureIssueEvent({
|
||||||
eventName: ISSUE_DELETED,
|
eventName: ISSUE_DELETED,
|
||||||
payload: { id: issueId, state: "SUCCESS", element: "Issue detail page" },
|
payload: { id: issueId, state: "SUCCESS", element: "Issue detail page" },
|
||||||
path: pathname,
|
path: pathname,
|
||||||
});
|
});
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
captureIssueEvent({
|
captureIssueEvent({
|
||||||
eventName: ISSUE_DELETED,
|
eventName: ISSUE_DELETED,
|
||||||
|
|
|
||||||
|
|
@ -150,13 +150,14 @@ export const useSubIssueOperations = (): TSubIssueOperations => {
|
||||||
deleteSubIssue: async (workspaceSlug: string, projectId: string, parentIssueId: string, issueId: string) => {
|
deleteSubIssue: async (workspaceSlug: string, projectId: string, parentIssueId: string, issueId: string) => {
|
||||||
try {
|
try {
|
||||||
setSubIssueHelpers(parentIssueId, "issue_loader", issueId);
|
setSubIssueHelpers(parentIssueId, "issue_loader", issueId);
|
||||||
await deleteSubIssue(workspaceSlug, projectId, parentIssueId, issueId);
|
return deleteSubIssue(workspaceSlug, projectId, parentIssueId, issueId).then(() => {
|
||||||
captureIssueEvent({
|
captureIssueEvent({
|
||||||
eventName: "Sub-issue deleted",
|
eventName: "Sub-issue deleted",
|
||||||
payload: { id: issueId, state: "SUCCESS", element: "Issue detail page" },
|
payload: { id: issueId, state: "SUCCESS", element: "Issue detail page" },
|
||||||
path: pathname,
|
path: pathname,
|
||||||
});
|
});
|
||||||
setSubIssueHelpers(parentIssueId, "issue_loader", issueId);
|
setSubIssueHelpers(parentIssueId, "issue_loader", issueId);
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
captureIssueEvent({
|
captureIssueEvent({
|
||||||
eventName: "Sub-issue removed",
|
eventName: "Sub-issue removed",
|
||||||
|
|
|
||||||
|
|
@ -78,14 +78,18 @@ export const IssueDetailQuickActions: FC<Props> = observer((props) => {
|
||||||
|
|
||||||
const handleDeleteIssue = async () => {
|
const handleDeleteIssue = async () => {
|
||||||
try {
|
try {
|
||||||
if (issue?.archived_at) await removeArchivedIssue(workspaceSlug, projectId, issueId);
|
if (issue?.archived_at) {
|
||||||
else await removeIssue(workspaceSlug, projectId, issueId);
|
return removeArchivedIssue(workspaceSlug, projectId, issueId).then(() => {
|
||||||
router.push(`/${workspaceSlug}/projects/${projectId}/issues`);
|
router.push(`/${workspaceSlug}/projects/${projectId}/issues`);
|
||||||
captureIssueEvent({
|
captureIssueEvent({
|
||||||
eventName: ISSUE_DELETED,
|
eventName: ISSUE_DELETED,
|
||||||
payload: { id: issueId, state: "SUCCESS", element: "Issue detail page" },
|
payload: { id: issueId, state: "SUCCESS", element: "Issue detail page" },
|
||||||
path: pathname,
|
path: pathname,
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return removeIssue(workspaceSlug, projectId, issueId);
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setToast({
|
setToast({
|
||||||
title: "Error!",
|
title: "Error!",
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ export const IssuePeekOverview: FC<IIssuePeekOverview> = observer((props) => {
|
||||||
} = useIssues(EIssuesStoreType.ARCHIVED);
|
} = useIssues(EIssuesStoreType.ARCHIVED);
|
||||||
const {
|
const {
|
||||||
peekIssue,
|
peekIssue,
|
||||||
|
setPeekIssue,
|
||||||
issue: { fetchIssue },
|
issue: { fetchIssue },
|
||||||
fetchActivities,
|
fetchActivities,
|
||||||
} = useIssueDetail();
|
} = useIssueDetail();
|
||||||
|
|
@ -44,6 +45,11 @@ export const IssuePeekOverview: FC<IIssuePeekOverview> = observer((props) => {
|
||||||
const [loader, setLoader] = useState(true);
|
const [loader, setLoader] = useState(true);
|
||||||
const [error, setError] = useState(false);
|
const [error, setError] = useState(false);
|
||||||
|
|
||||||
|
const removeRoutePeekId = () => {
|
||||||
|
setPeekIssue(undefined);
|
||||||
|
if (embedIssue) embedRemoveCurrentNotification && embedRemoveCurrentNotification();
|
||||||
|
};
|
||||||
|
|
||||||
const issueOperations: TIssueOperations = useMemo(
|
const issueOperations: TIssueOperations = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
fetch: async (workspaceSlug: string, projectId: string, issueId: string, loader = true) => {
|
fetch: async (workspaceSlug: string, projectId: string, issueId: string, loader = true) => {
|
||||||
|
|
@ -95,17 +101,14 @@ export const IssuePeekOverview: FC<IIssuePeekOverview> = observer((props) => {
|
||||||
},
|
},
|
||||||
remove: async (workspaceSlug: string, projectId: string, issueId: string) => {
|
remove: async (workspaceSlug: string, projectId: string, issueId: string) => {
|
||||||
try {
|
try {
|
||||||
issues?.removeIssue(workspaceSlug, projectId, issueId);
|
return issues?.removeIssue(workspaceSlug, projectId, issueId).then(() => {
|
||||||
setToast({
|
|
||||||
title: "Success!",
|
|
||||||
type: TOAST_TYPE.SUCCESS,
|
|
||||||
message: "Issue deleted successfully",
|
|
||||||
});
|
|
||||||
captureIssueEvent({
|
captureIssueEvent({
|
||||||
eventName: ISSUE_DELETED,
|
eventName: ISSUE_DELETED,
|
||||||
payload: { id: issueId, state: "SUCCESS", element: "Issue peek-overview" },
|
payload: { id: issueId, state: "SUCCESS", element: "Issue peek-overview" },
|
||||||
path: pathname,
|
path: pathname,
|
||||||
});
|
});
|
||||||
|
removeRoutePeekId();
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setToast({
|
setToast({
|
||||||
title: "Error!",
|
title: "Error!",
|
||||||
|
|
|
||||||
|
|
@ -131,7 +131,7 @@ export const IssueView: FC<IIssueView> = observer((props) => {
|
||||||
toggleDeleteIssueModal(null);
|
toggleDeleteIssueModal(null);
|
||||||
}}
|
}}
|
||||||
data={issue}
|
data={issue}
|
||||||
onSubmit={() => issueOperations.remove(workspaceSlug, projectId, issueId).then(() => removeRoutePeekId())}
|
onSubmit={async () => issueOperations.remove(workspaceSlug, projectId, issueId)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue