From 5b30fab0552f623a35ff5b89b90dab82dfcee258 Mon Sep 17 00:00:00 2001 From: Peter Amstutz Date: Mon, 30 Oct 2023 15:24:39 -0400 Subject: [PATCH] 21067: Fix bug deleting projects from shared-with-me Arvados-DCO-1.1-Signed-off-by: Peter Amstutz --- cypress/integration/page-not-found.spec.js | 5 +- src/store/trash/trash-actions.ts | 174 +++++++++--------- .../collection-panel/collection-panel.tsx | 16 +- src/views/not-found-panel/not-found-panel.tsx | 27 +++ .../process-panel/process-panel-root.tsx | 18 +- src/views/project-panel/project-panel.tsx | 19 +- .../registered-workflow-panel.tsx | 20 +- 7 files changed, 145 insertions(+), 134 deletions(-) diff --git a/cypress/integration/page-not-found.spec.js b/cypress/integration/page-not-found.spec.js index 4df4135c..6eab27c8 100644 --- a/cypress/integration/page-not-found.spec.js +++ b/cypress/integration/page-not-found.spec.js @@ -45,8 +45,7 @@ describe('Page not found tests', function() { cy.goToPath(path); // then - cy.get('[data-cy=not-found-page]').should('not.exist'); - cy.get('[data-cy=not-found-content]').should('exist'); + cy.get('[data-cy=not-found-view]').should('exist'); }); }); -}) \ No newline at end of file +}) diff --git a/src/store/trash/trash-actions.ts b/src/store/trash/trash-actions.ts index 884293a9..62b66922 100644 --- a/src/store/trash/trash-actions.ts +++ b/src/store/trash/trash-actions.ts @@ -9,104 +9,112 @@ import { snackbarActions, SnackbarKind } from "store/snackbar/snackbar-actions"; import { trashPanelActions } from "store/trash-panel/trash-panel-action"; import { activateSidePanelTreeItem, loadSidePanelTreeProjects } from "store/side-panel-tree/side-panel-tree-actions"; import { projectPanelActions } from "store/project-panel/project-panel-action-bind"; +import { sharedWithMePanelActions } from "store/shared-with-me-panel/shared-with-me-panel-actions"; import { ResourceKind } from "models/resource"; import { navigateTo, navigateToTrash } from "store/navigation/navigation-action"; -import { matchCollectionRoute } from "routes/routes"; +import { matchCollectionRoute, matchSharedWithMeRoute } from "routes/routes"; export const toggleProjectTrashed = (uuid: string, ownerUuid: string, isTrashed: boolean, isMulti: boolean) => - async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository): Promise => { - let errorMessage = ""; - let successMessage = ""; - let untrashedResource; - try { - if (isTrashed) { - errorMessage = "Could not restore project from trash"; - successMessage = "Restored project from trash"; - untrashedResource = await services.groupsService.untrash(uuid); - dispatch(isMulti || !untrashedResource ? navigateToTrash : navigateTo(uuid)); - dispatch(activateSidePanelTreeItem(uuid)); - } else { - errorMessage = "Could not move project to trash"; - successMessage = "Added project to trash"; - await services.groupsService.trash(uuid); - dispatch(loadSidePanelTreeProjects(ownerUuid)); - dispatch(navigateTo(ownerUuid)); - } - if (untrashedResource) { - dispatch( - snackbarActions.OPEN_SNACKBAR({ - message: successMessage, - hideDuration: 2000, - kind: SnackbarKind.SUCCESS, - }) - ); - } - } catch (e) { - if (e.status === 422) { - dispatch( - snackbarActions.OPEN_SNACKBAR({ - message: "Could not restore project from trash: Duplicate name at destination", - kind: SnackbarKind.ERROR, - }) - ); - } else { - dispatch( - snackbarActions.OPEN_SNACKBAR({ - message: errorMessage, - kind: SnackbarKind.ERROR, - }) - ); + async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository): Promise => { + let errorMessage = ""; + let successMessage = ""; + let untrashedResource; + try { + if (isTrashed) { + errorMessage = "Could not restore project from trash"; + successMessage = "Restored project from trash"; + untrashedResource = await services.groupsService.untrash(uuid); + dispatch(isMulti || !untrashedResource ? navigateToTrash : navigateTo(uuid)); + dispatch(activateSidePanelTreeItem(uuid)); + } else { + errorMessage = "Could not move project to trash"; + successMessage = "Added project to trash"; + await services.groupsService.trash(uuid); + dispatch(loadSidePanelTreeProjects(ownerUuid)); + + const { location } = getState().router; + if (matchSharedWithMeRoute(location ? location.pathname : "")) { + dispatch(sharedWithMePanelActions.REQUEST_ITEMS()); + } + else { + dispatch(navigateTo(ownerUuid)); + } + } + if (untrashedResource) { + dispatch( + snackbarActions.OPEN_SNACKBAR({ + message: successMessage, + hideDuration: 2000, + kind: SnackbarKind.SUCCESS, + }) + ); + } + } catch (e) { + if (e.status === 422) { + dispatch( + snackbarActions.OPEN_SNACKBAR({ + message: "Could not restore project from trash: Duplicate name at destination", + kind: SnackbarKind.ERROR, + }) + ); + } else { + dispatch( + snackbarActions.OPEN_SNACKBAR({ + message: errorMessage, + kind: SnackbarKind.ERROR, + }) + ); + } } - } - }; + }; export const toggleCollectionTrashed = (uuid: string, isTrashed: boolean) => - async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository): Promise => { - let errorMessage = ""; - let successMessage = ""; - try { - if (isTrashed) { - const { location } = getState().router; - errorMessage = "Could not restore collection from trash"; - successMessage = "Restored from trash"; - await services.collectionService.untrash(uuid); - if (matchCollectionRoute(location ? location.pathname : "")) { - dispatch(navigateToTrash); + async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository): Promise => { + let errorMessage = ""; + let successMessage = ""; + try { + if (isTrashed) { + const { location } = getState().router; + errorMessage = "Could not restore collection from trash"; + successMessage = "Restored from trash"; + await services.collectionService.untrash(uuid); + if (matchCollectionRoute(location ? location.pathname : "")) { + dispatch(navigateToTrash); + } + dispatch(trashPanelActions.REQUEST_ITEMS()); + } else { + errorMessage = "Could not move collection to trash"; + successMessage = "Added to trash"; + await services.collectionService.trash(uuid); + dispatch(projectPanelActions.REQUEST_ITEMS()); } - dispatch(trashPanelActions.REQUEST_ITEMS()); - } else { - errorMessage = "Could not move collection to trash"; - successMessage = "Added to trash"; - await services.collectionService.trash(uuid); - dispatch(projectPanelActions.REQUEST_ITEMS()); - } - dispatch( - snackbarActions.OPEN_SNACKBAR({ - message: successMessage, - hideDuration: 2000, - kind: SnackbarKind.SUCCESS, - }) - ); - } catch (e) { - if (e.status === 422) { dispatch( snackbarActions.OPEN_SNACKBAR({ - message: "Could not restore collection from trash: Duplicate name at destination", - kind: SnackbarKind.ERROR, - }) - ); - } else { - dispatch( - snackbarActions.OPEN_SNACKBAR({ - message: errorMessage, - kind: SnackbarKind.ERROR, + message: successMessage, + hideDuration: 2000, + kind: SnackbarKind.SUCCESS, }) ); + } catch (e) { + if (e.status === 422) { + dispatch( + snackbarActions.OPEN_SNACKBAR({ + message: "Could not restore collection from trash: Duplicate name at destination", + kind: SnackbarKind.ERROR, + }) + ); + } else { + dispatch( + snackbarActions.OPEN_SNACKBAR({ + message: errorMessage, + kind: SnackbarKind.ERROR, + }) + ); + } } - } - }; + }; export const toggleTrashed = (kind: ResourceKind, uuid: string, ownerUuid: string, isTrashed: boolean) => (dispatch: Dispatch) => { if (kind === ResourceKind.PROJECT) { diff --git a/src/views/collection-panel/collection-panel.tsx b/src/views/collection-panel/collection-panel.tsx index eed9c7de..d93d6e92 100644 --- a/src/views/collection-panel/collection-panel.tsx +++ b/src/views/collection-panel/collection-panel.tsx @@ -37,7 +37,7 @@ import { Link as ButtonLink } from '@material-ui/core'; import { ResourceWithName, ResponsiblePerson } from 'views-components/data-explorer/renderers'; import { MPVContainer, MPVPanelContent, MPVPanelState } from 'components/multi-panel-view/multi-panel-view'; import { resourceIsFrozen } from 'common/frozen-resources'; -import { DefaultView } from "components/default-view/default-view"; +import { NotFoundView } from 'views/not-found-panel/not-found-panel'; type CssRules = 'root' | 'button' @@ -230,16 +230,10 @@ export const CollectionPanel = withStyles(styles)(connect( - : - - + : ; } diff --git a/src/views/not-found-panel/not-found-panel.tsx b/src/views/not-found-panel/not-found-panel.tsx index 148c331e..f54c00c3 100644 --- a/src/views/not-found-panel/not-found-panel.tsx +++ b/src/views/not-found-panel/not-found-panel.tsx @@ -3,8 +3,12 @@ // SPDX-License-Identifier: AGPL-3.0 import { RootState } from 'store/store'; +import React from 'react'; import { connect } from 'react-redux'; import { NotFoundPanelRoot, NotFoundPanelRootDataProps } from 'views/not-found-panel/not-found-panel-root'; +import { Grid } from '@material-ui/core'; +import { DefaultView } from "components/default-view/default-view"; +import { IconType } from 'components/icon/icon'; const mapStateToProps = (state: RootState): NotFoundPanelRootDataProps => { return { @@ -17,3 +21,26 @@ const mapDispatchToProps = null; export const NotFoundPanel = connect(mapStateToProps, mapDispatchToProps) (NotFoundPanelRoot) as any; + +export interface NotFoundViewDataProps { + messages: string[]; + icon?: IconType; +} + +// TODO: optionally pass in the UUID and check if the +// reason the item is not found is because +// it or a parent project is actually in the trash. +// If so, offer to untrash the item or the parent project. +export const NotFoundView = + ({ messages, icon: Icon }: NotFoundViewDataProps) => + + + ; diff --git a/src/views/process-panel/process-panel-root.tsx b/src/views/process-panel/process-panel-root.tsx index d019d141..7a240899 100644 --- a/src/views/process-panel/process-panel-root.tsx +++ b/src/views/process-panel/process-panel-root.tsx @@ -3,8 +3,7 @@ // SPDX-License-Identifier: AGPL-3.0 import React from "react"; -import { Grid, StyleRulesCallback, WithStyles, withStyles } from "@material-ui/core"; -import { DefaultView } from "components/default-view/default-view"; +import { StyleRulesCallback, WithStyles, withStyles } from "@material-ui/core"; import { ProcessIcon } from "components/icon/icon"; import { Process } from "store/processes/process"; import { SubprocessPanel } from "views/subprocess-panel/subprocess-panel"; @@ -24,6 +23,7 @@ import { AuthState } from "store/auth/auth-reducer"; import { ProcessCmdCard } from "./process-cmd-card"; import { ContainerRequestResource } from "models/container-request"; import { OutputDetails, NodeInstanceType } from "store/process-panel/process-panel"; +import { NotFoundView } from 'views/not-found-panel/not-found-panel'; type CssRules = "root"; @@ -209,16 +209,10 @@ export const ProcessPanelRoot = withStyles(styles)( ) : ( - - - + ); } ); diff --git a/src/views/project-panel/project-panel.tsx b/src/views/project-panel/project-panel.tsx index 2f274c97..2cc751bf 100644 --- a/src/views/project-panel/project-panel.tsx +++ b/src/views/project-panel/project-panel.tsx @@ -6,7 +6,7 @@ import React from 'react'; import withStyles from '@material-ui/core/styles/withStyles'; import { DispatchProp, connect } from 'react-redux'; import { RouteComponentProps } from 'react-router'; -import { StyleRulesCallback, WithStyles, Grid } from '@material-ui/core'; +import { StyleRulesCallback, WithStyles } from '@material-ui/core'; import { DataExplorer } from 'views-components/data-explorer/data-explorer'; import { DataColumns } from 'components/data-table/data-table'; @@ -51,7 +51,7 @@ import { GroupClass, GroupResource } from 'models/group'; import { CollectionResource } from 'models/collection'; import { resourceIsFrozen } from 'common/frozen-resources'; import { ProjectResource } from 'models/project'; -import { DefaultView } from "components/default-view/default-view"; +import { NotFoundView } from 'views/not-found-panel/not-found-panel'; type CssRules = 'root' | 'button'; @@ -276,16 +276,11 @@ export const ProjectPanel = withStyles(styles)( defaultViewMessages={DEFAULT_VIEW_MESSAGES} /> - : - - ; + : + } isCurrentItemChild = (resource: Resource) => { diff --git a/src/views/workflow-panel/registered-workflow-panel.tsx b/src/views/workflow-panel/registered-workflow-panel.tsx index da273719..50192e54 100644 --- a/src/views/workflow-panel/registered-workflow-panel.tsx +++ b/src/views/workflow-panel/registered-workflow-panel.tsx @@ -12,8 +12,7 @@ import { Card, CardHeader, CardContent, - IconButton, - Grid + IconButton } from '@material-ui/core'; import { connect, DispatchProp } from "react-redux"; import { RouteComponentProps } from 'react-router'; @@ -27,7 +26,7 @@ import { getResource } from 'store/resources/resources'; import { openContextMenu, resourceUuidToContextMenuKind } from 'store/context-menu/context-menu-actions'; import { MPVContainer, MPVPanelContent, MPVPanelState } from 'components/multi-panel-view/multi-panel-view'; import { ProcessIOCard, ProcessIOCardType } from 'views/process-panel/process-io-card'; -import { DefaultView } from "components/default-view/default-view"; +import { NotFoundView } from 'views/not-found-panel/not-found-panel'; type CssRules = 'root' | 'button' @@ -202,16 +201,11 @@ export const RegisteredWorkflowPanel = withStyles(styles)(connect( - : - - ; + : + } handleContextMenu = (event: React.MouseEvent) => { -- 2.30.2