From: Michal Klobukowski Date: Wed, 12 Sep 2018 07:47:15 +0000 (+0200) Subject: Merge branch 'master' X-Git-Tag: 1.3.0~93^2~8 X-Git-Url: https://git.arvados.org/arvados-workbench2.git/commitdiff_plain/085692af7ee9809e2714edacad1251e78a196bd3?hp=ca1736b82188c7c341f61a779552708263ca5def Merge branch 'master' Feature #13751 Arvados-DCO-1.1-Signed-off-by: Michal Klobukowski --- diff --git a/package.json b/package.json index 0e6435eb..84d1510f 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "react-router-dom": "4.3.1", "react-router-redux": "5.0.0-alpha.9", "react-scripts-ts": "2.17.0", + "react-transition-group": "2.4.0", "redux": "4.0.0", "redux-thunk": "2.3.0", "unionize": "2.1.2" diff --git a/src/components/code-snippet/code-snippet.tsx b/src/components/code-snippet/code-snippet.tsx index eb0e709a..6cba299f 100644 --- a/src/components/code-snippet/code-snippet.tsx +++ b/src/components/code-snippet/code-snippet.tsx @@ -5,15 +5,13 @@ import * as React from 'react'; import { StyleRulesCallback, WithStyles, Typography, withStyles, Theme } from '@material-ui/core'; import { ArvadosTheme } from '~/common/custom-theme'; +import * as classNames from 'classnames'; type CssRules = 'root'; const styles: StyleRulesCallback = (theme: ArvadosTheme) => ({ root: { boxSizing: 'border-box', - width: '100%', - height: 'auto', - maxHeight: '550px', overflow: 'auto', padding: theme.spacing.unit } @@ -21,13 +19,16 @@ const styles: StyleRulesCallback = (theme: ArvadosTheme) => ({ export interface CodeSnippetDataProps { lines: string[]; + className?: string; } type CodeSnippetProps = CodeSnippetDataProps & WithStyles; export const CodeSnippet = withStyles(styles)( - ({ classes, lines }: CodeSnippetProps) => - + ({ classes, lines, className }: CodeSnippetProps) => + { lines.map((line: string, index: number) => { return {line}; diff --git a/src/components/default-code-snippet/default-code-snippet.tsx b/src/components/default-code-snippet/default-code-snippet.tsx index b8c0a7be..e8b89f32 100644 --- a/src/components/default-code-snippet/default-code-snippet.tsx +++ b/src/components/default-code-snippet/default-code-snippet.tsx @@ -23,9 +23,7 @@ const theme = createMuiTheme({ } }); -type DefaultCodeSnippet = CodeSnippetDataProps; - -export const DefaultCodeSnippet = (props: DefaultCodeSnippet) => +export const DefaultCodeSnippet = (props: CodeSnippetDataProps) => - + ; \ No newline at end of file diff --git a/src/index.tsx b/src/index.tsx index e982b454..a76a86ac 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -36,6 +36,7 @@ import { initWebSocket } from '~/websocket/websocket'; import { Config } from '~/common/config'; import { addRouteChangeHandlers } from './routes/route-change-handlers'; import { setCurrentTokenDialogApiHost } from '~/store/current-token-dialog/current-token-dialog-actions'; +import { processResourceActionSet } from './views-components/context-menu/action-sets/process-resource-action-set'; const getBuildNumber = () => "BN-" + (process.env.REACT_APP_BUILD_NUMBER || "dev"); const getGitCommit = () => "GIT-" + (process.env.REACT_APP_GIT_COMMIT || "latest").substr(0, 7); @@ -54,6 +55,7 @@ addMenuActionSet(ContextMenuKind.COLLECTION_FILES_ITEM, collectionFilesItemActio addMenuActionSet(ContextMenuKind.COLLECTION, collectionActionSet); addMenuActionSet(ContextMenuKind.COLLECTION_RESOURCE, collectionResourceActionSet); addMenuActionSet(ContextMenuKind.PROCESS, processActionSet); +addMenuActionSet(ContextMenuKind.PROCESS_RESOURCE, processResourceActionSet); addMenuActionSet(ContextMenuKind.TRASH, trashActionSet); fetchConfig() diff --git a/src/routes/routes.ts b/src/routes/routes.ts index 107b9e6f..fb28bd05 100644 --- a/src/routes/routes.ts +++ b/src/routes/routes.ts @@ -26,6 +26,8 @@ export const getResourceUrl = (uuid: string) => { return getProjectUrl(uuid); case ResourceKind.COLLECTION: return getCollectionUrl(uuid); + case ResourceKind.PROCESS: + return getProcessUrl(uuid); default: return undefined; } diff --git a/src/services/common-service/common-resource-service.ts b/src/services/common-service/common-resource-service.ts index 7b36b71c..09e034f5 100644 --- a/src/services/common-service/common-resource-service.ts +++ b/src/services/common-service/common-resource-service.ts @@ -32,6 +32,7 @@ export interface Errors { export enum CommonResourceServiceError { UNIQUE_VIOLATION = 'UniqueViolation', OWNERSHIP_CYCLE = 'OwnershipCycle', + MODIFYING_CONTAINER_REQUEST_FINAL_STATE = 'ModifyingContainerRequestFinalState', UNKNOWN = 'Unknown', NONE = 'None' } @@ -121,6 +122,8 @@ export const getCommonResourceServiceError = (errorResponse: any) => { return CommonResourceServiceError.UNIQUE_VIOLATION; case /ownership cycle/.test(error): return CommonResourceServiceError.OWNERSHIP_CYCLE; + case /Mounts cannot be modified in state 'Final'/.test(error): + return CommonResourceServiceError.MODIFYING_CONTAINER_REQUEST_FINAL_STATE; default: return CommonResourceServiceError.UNKNOWN; } diff --git a/src/store/collections/collection-copy-actions.ts b/src/store/collections/collection-copy-actions.ts index 87ba0424..09d4e04e 100644 --- a/src/store/collections/collection-copy-actions.ts +++ b/src/store/collections/collection-copy-actions.ts @@ -9,24 +9,19 @@ import { resetPickerProjectTree } from '~/store/project-tree-picker/project-tree import { RootState } from '~/store/store'; import { ServiceRepository } from '~/services/services'; import { getCommonResourceServiceError, CommonResourceServiceError } from '~/services/common-service/common-resource-service'; +import { CopyFormDialogData } from '~/store/copy-dialog/copy-dialog'; export const COLLECTION_COPY_FORM_NAME = 'collectionCopyFormName'; -export interface CollectionCopyFormDialogData { - name: string; - ownerUuid: string; - uuid: string; -} - export const openCollectionCopyDialog = (resource: { name: string, uuid: string }) => (dispatch: Dispatch) => { dispatch(resetPickerProjectTree()); - const initialData: CollectionCopyFormDialogData = { name: `Copy of: ${resource.name}`, ownerUuid: '', uuid: resource.uuid }; + const initialData: CopyFormDialogData = { name: `Copy of: ${resource.name}`, ownerUuid: '', uuid: resource.uuid }; dispatch(initialize(COLLECTION_COPY_FORM_NAME, initialData)); dispatch(dialogActions.OPEN_DIALOG({ id: COLLECTION_COPY_FORM_NAME, data: {} })); }; -export const copyCollection = (resource: CollectionCopyFormDialogData) => +export const copyCollection = (resource: CopyFormDialogData) => async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => { dispatch(startSubmit(COLLECTION_COPY_FORM_NAME)); try { diff --git a/src/store/context-menu/context-menu-actions.ts b/src/store/context-menu/context-menu-actions.ts index bb404b88..d85059d6 100644 --- a/src/store/context-menu/context-menu-actions.ts +++ b/src/store/context-menu/context-menu-actions.ts @@ -13,6 +13,7 @@ import { UserResource } from '~/models/user'; import { isSidePanelTreeCategory } from '~/store/side-panel-tree/side-panel-tree-actions'; import { extractUuidKind, ResourceKind } from '~/models/resource'; import { matchProcessRoute } from '~/routes/routes'; +import { Process } from '~/store/processes/process'; export const contextMenuActions = unionize({ OPEN_CONTEXT_MENU: ofType<{ position: ContextMenuPosition, resource: ContextMenuResource }>(), @@ -84,14 +85,10 @@ export const openSidePanelContextMenu = (event: React.MouseEvent, i } }; -export const openProcessContextMenu = (event: React.MouseEvent) => +export const openProcessContextMenu = (event: React.MouseEvent, process: Process) => (dispatch: Dispatch, getState: () => RootState) => { - const { location } = getState().router; - const pathname = location ? location.pathname : ''; - const match = matchProcessRoute(pathname); - const uuid = match ? match.params.id : ''; const resource = { - uuid, + uuid: process.containerRequest.uuid, ownerUuid: '', kind: ResourceKind.PROCESS, name: '', @@ -108,6 +105,8 @@ export const resourceKindToContextMenuKind = (uuid: string) => { return ContextMenuKind.PROJECT; case ResourceKind.COLLECTION: return ContextMenuKind.COLLECTION_RESOURCE; + case ResourceKind.PROCESS: + return ContextMenuKind.PROCESS_RESOURCE; case ResourceKind.USER: return ContextMenuKind.ROOT_PROJECT; default: diff --git a/src/store/copy-dialog/copy-dialog.ts b/src/store/copy-dialog/copy-dialog.ts new file mode 100644 index 00000000..4450cfc6 --- /dev/null +++ b/src/store/copy-dialog/copy-dialog.ts @@ -0,0 +1,9 @@ +// Copyright (C) The Arvados Authors. All rights reserved. +// +// SPDX-License-Identifier: AGPL-3.0 + +export interface CopyFormDialogData { + name: string; + uuid: string; + ownerUuid: string; +} \ No newline at end of file diff --git a/src/store/processes/process-command-actions.ts b/src/store/processes/process-command-actions.ts new file mode 100644 index 00000000..de55a2cb --- /dev/null +++ b/src/store/processes/process-command-actions.ts @@ -0,0 +1,27 @@ +// Copyright (C) The Arvados Authors. All rights reserved. +// +// SPDX-License-Identifier: AGPL-3.0 + +import { dialogActions } from '~/store/dialog/dialog-actions'; +import { RootState } from '../store'; +import { Dispatch } from 'redux'; +import { getProcess } from '~/store/processes/process'; + +export const PROCESS_COMMAND_DIALOG_NAME = 'processCommandDialog'; + +export interface ProcessCommandDialogData { + command: string; + processName: string; +} + +export const openProcessCommandDialog = (processUuid: string) => + (dispatch: Dispatch, getState: () => RootState) => { + const process = getProcess(processUuid)(getState().resources); + if (process) { + const data: ProcessCommandDialogData = { + command: process.containerRequest.command.join(' '), + processName: process.containerRequest.name, + }; + dispatch(dialogActions.OPEN_DIALOG({ id: PROCESS_COMMAND_DIALOG_NAME, data })); + } + }; diff --git a/src/store/processes/process-copy-actions.ts b/src/store/processes/process-copy-actions.ts new file mode 100644 index 00000000..bb8d8f5a --- /dev/null +++ b/src/store/processes/process-copy-actions.ts @@ -0,0 +1,49 @@ +// Copyright (C) The Arvados Authors. All rights reserved. +// +// SPDX-License-Identifier: AGPL-3.0 + +import { Dispatch } from "redux"; +import { dialogActions } from "~/store/dialog/dialog-actions"; +import { initialize, startSubmit } from 'redux-form'; +import { resetPickerProjectTree } from '~/store/project-tree-picker/project-tree-picker-actions'; +import { RootState } from '~/store/store'; +import { ServiceRepository } from '~/services/services'; +import { CopyFormDialogData } from '~/store/copy-dialog/copy-dialog'; +import { getProcess, ProcessStatus, getProcessStatus } from '~/store/processes/process'; +import { snackbarActions } from '~/store/snackbar/snackbar-actions'; + +export const PROCESS_COPY_FORM_NAME = 'processCopyFormName'; + +export const openCopyProcessDialog = (resource: { name: string, uuid: string }) => + (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => { + const process = getProcess(resource.uuid)(getState().resources); + if (process) { + const processStatus = getProcessStatus(process); + if (processStatus === ProcessStatus.DRAFT) { + dispatch(resetPickerProjectTree()); + const initialData: CopyFormDialogData = { name: `Copy of: ${resource.name}`, uuid: resource.uuid, ownerUuid: '' }; + dispatch(initialize(PROCESS_COPY_FORM_NAME, initialData)); + dispatch(dialogActions.OPEN_DIALOG({ id: PROCESS_COPY_FORM_NAME, data: {} })); + } else { + dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'You can copy only draft processes.', hideDuration: 2000 })); + } + } else { + dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Process not found', hideDuration: 2000 })); + } + }; + +export const copyProcess = (resource: CopyFormDialogData) => + async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => { + dispatch(startSubmit(PROCESS_COPY_FORM_NAME)); + try { + const process = await services.containerRequestService.get(resource.uuid); + const uuidKey = 'uuid'; + delete process[uuidKey]; + await services.containerRequestService.create({ ...process, ownerUuid: resource.ownerUuid, name: resource.name }); + dispatch(dialogActions.CLOSE_DIALOG({ id: PROCESS_COPY_FORM_NAME })); + return process; + } catch (e) { + dispatch(dialogActions.CLOSE_DIALOG({ id: PROCESS_COPY_FORM_NAME })); + throw new Error('Could not copy the process.'); + } + }; \ No newline at end of file diff --git a/src/store/processes/process-move-actions.ts b/src/store/processes/process-move-actions.ts new file mode 100644 index 00000000..6df82699 --- /dev/null +++ b/src/store/processes/process-move-actions.ts @@ -0,0 +1,57 @@ +// Copyright (C) The Arvados Authors. All rights reserved. +// +// SPDX-License-Identifier: AGPL-3.0 + +import { Dispatch } from "redux"; +import { dialogActions } from "~/store/dialog/dialog-actions"; +import { startSubmit, stopSubmit, initialize } from 'redux-form'; +import { ServiceRepository } from '~/services/services'; +import { RootState } from '~/store/store'; +import { getCommonResourceServiceError, CommonResourceServiceError } from "~/services/common-service/common-resource-service"; +import { snackbarActions } from '~/store/snackbar/snackbar-actions'; +import { MoveToFormDialogData } from '~/store/move-to-dialog/move-to-dialog'; +import { resetPickerProjectTree } from '~/store/project-tree-picker/project-tree-picker-actions'; +import { projectPanelActions } from '~/store/project-panel/project-panel-action'; +import { getProcess, getProcessStatus, ProcessStatus } from '~/store/processes/process'; + +export const PROCESS_MOVE_FORM_NAME = 'processMoveFormName'; + +export const openMoveProcessDialog = (resource: { name: string, uuid: string }) => + (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => { + const process = getProcess(resource.uuid)(getState().resources); + if (process) { + const processStatus = getProcessStatus(process); + if (processStatus === ProcessStatus.DRAFT) { + dispatch(resetPickerProjectTree()); + dispatch(initialize(PROCESS_MOVE_FORM_NAME, resource)); + dispatch(dialogActions.OPEN_DIALOG({ id: PROCESS_MOVE_FORM_NAME, data: {} })); + } else { + dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'You can move only draft processes.', hideDuration: 2000 })); + } + } else { + dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Process not found', hideDuration: 2000 })); + } + }; + +export const moveProcess = (resource: MoveToFormDialogData) => + async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => { + dispatch(startSubmit(PROCESS_MOVE_FORM_NAME)); + try { + const process = await services.containerRequestService.get(resource.uuid); + await services.containerRequestService.update(resource.uuid, { ...process, ownerUuid: resource.ownerUuid }); + dispatch(projectPanelActions.REQUEST_ITEMS()); + dispatch(dialogActions.CLOSE_DIALOG({ id: PROCESS_MOVE_FORM_NAME })); + return process; + } catch (e) { + const error = getCommonResourceServiceError(e); + if (error === CommonResourceServiceError.UNIQUE_VIOLATION) { + dispatch(stopSubmit(PROCESS_MOVE_FORM_NAME, { ownerUuid: 'A process with the same name already exists in the target project.' })); + } else if (error === CommonResourceServiceError.MODIFYING_CONTAINER_REQUEST_FINAL_STATE) { + dispatch(stopSubmit(PROCESS_MOVE_FORM_NAME, { ownerUuid: 'You can move only draft processes.' })); + } else { + dispatch(dialogActions.CLOSE_DIALOG({ id: PROCESS_MOVE_FORM_NAME })); + dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Could not move the process.', hideDuration: 2000 })); + } + return; + } + }; \ No newline at end of file diff --git a/src/store/processes/process-update-actions.ts b/src/store/processes/process-update-actions.ts new file mode 100644 index 00000000..92cf032f --- /dev/null +++ b/src/store/processes/process-update-actions.ts @@ -0,0 +1,54 @@ +// Copyright (C) The Arvados Authors. All rights reserved. +// +// SPDX-License-Identifier: AGPL-3.0 + +import { Dispatch } from "redux"; +import { initialize, startSubmit, stopSubmit } from 'redux-form'; +import { RootState } from "~/store/store"; +import { dialogActions } from "~/store/dialog/dialog-actions"; +import { getCommonResourceServiceError, CommonResourceServiceError } from "~/services/common-service/common-resource-service"; +import { ServiceRepository } from "~/services/services"; +import { getProcess } from '~/store/processes/process'; +import { projectPanelActions } from '~/store/project-panel/project-panel-action'; +import { snackbarActions } from '~/store/snackbar/snackbar-actions'; + +export interface ProcessUpdateFormDialogData { + uuid: string; + name: string; +} + +export const PROCESS_UPDATE_FORM_NAME = 'processUpdateFormName'; + +export const openProcessUpdateDialog = (resource: ProcessUpdateFormDialogData) => + (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => { + const process = getProcess(resource.uuid)(getState().resources); + if(process) { + dispatch(initialize(PROCESS_UPDATE_FORM_NAME, { ...resource, name: process.containerRequest.name })); + dispatch(dialogActions.OPEN_DIALOG({ id: PROCESS_UPDATE_FORM_NAME, data: {} })); + } else { + dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Process not found', hideDuration: 2000 })); + } + }; + +export const updateProcess = (resource: ProcessUpdateFormDialogData) => + async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => { + dispatch(startSubmit(PROCESS_UPDATE_FORM_NAME)); + try { + const process = await services.containerRequestService.get(resource.uuid); + const updatedProcess = await services.containerRequestService.update(resource.uuid, { ...process, name: resource.name }); + dispatch(projectPanelActions.REQUEST_ITEMS()); + dispatch(dialogActions.CLOSE_DIALOG({ id: PROCESS_UPDATE_FORM_NAME })); + return updatedProcess; + } catch (e) { + const error = getCommonResourceServiceError(e); + if (error === CommonResourceServiceError.UNIQUE_VIOLATION) { + dispatch(stopSubmit(PROCESS_UPDATE_FORM_NAME, { name: 'Process with the same name already exists.' })); + } else if (error === CommonResourceServiceError.MODIFYING_CONTAINER_REQUEST_FINAL_STATE) { + dispatch(stopSubmit(PROCESS_UPDATE_FORM_NAME, { name: 'You cannot modified in "Final" state.' })); + } else { + dispatch(dialogActions.CLOSE_DIALOG({ id: PROCESS_UPDATE_FORM_NAME })); + dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Could not update the process.', hideDuration: 2000 })); + } + return; + } + }; \ No newline at end of file diff --git a/src/store/workbench/workbench-actions.ts b/src/store/workbench/workbench-actions.ts index 7b8fdb25..bb41fa28 100644 --- a/src/store/workbench/workbench-actions.ts +++ b/src/store/workbench/workbench-actions.ts @@ -29,6 +29,9 @@ import * as collectionCopyActions from '~/store/collections/collection-copy-acti import * as collectionUpdateActions from '~/store/collections/collection-update-actions'; import * as collectionMoveActions from '~/store/collections/collection-move-actions'; import * as processesActions from '../processes/processes-actions'; +import * as processMoveActions from '~/store/processes/process-move-actions'; +import * as processUpdateActions from '~/store/processes/process-update-actions'; +import * as processCopyActions from '~/store/processes/process-copy-actions'; import { trashPanelColumns } from "~/views/trash-panel/trash-panel"; import { loadTrashPanel, trashPanelActions } from "~/store/trash-panel/trash-panel-action"; import { initProcessLogsPanel } from '../process-logs-panel/process-logs-panel-actions'; @@ -36,6 +39,7 @@ import { loadProcessPanel } from '~/store/process-panel/process-panel-actions'; import { sharedWithMePanelActions } from '~/store/shared-with-me-panel/shared-with-me-panel-actions'; import { loadSharedWithMePanel } from '../shared-with-me-panel/shared-with-me-panel-actions'; +import { CopyFormDialogData } from '~/store/copy-dialog/copy-dialog'; export const loadWorkbench = () => async (dispatch: Dispatch, getState: () => RootState) => { @@ -163,7 +167,7 @@ export const updateCollection = (data: collectionUpdateActions.CollectionUpdateF } }; -export const copyCollection = (data: collectionCopyActions.CollectionCopyFormDialogData) => +export const copyCollection = (data: CopyFormDialogData) => async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => { try { const collection = await dispatch(collectionCopyActions.copyCollection(data)); @@ -197,6 +201,47 @@ export const loadProcess = (uuid: string) => }; +export const updateProcess = (data: processUpdateActions.ProcessUpdateFormDialogData) => + async (dispatch: Dispatch) => { + try { + const process = await dispatch(processUpdateActions.updateProcess(data)); + if (process) { + dispatch(snackbarActions.OPEN_SNACKBAR({ + message: "Process has been successfully updated.", + hideDuration: 2000 + })); + dispatch(updateResources([process])); + dispatch(reloadProjectMatchingUuid([process.ownerUuid])); + } + } catch (e) { + dispatch(snackbarActions.OPEN_SNACKBAR({ message: e.message, hideDuration: 2000 })); + } + }; + +export const moveProcess = (data: MoveToFormDialogData) => + async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => { + try { + const process = await dispatch(processMoveActions.moveProcess(data)); + dispatch(updateResources([process])); + dispatch(reloadProjectMatchingUuid([process.ownerUuid])); + dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Process has been moved.', hideDuration: 2000 })); + } catch (e) { + dispatch(snackbarActions.OPEN_SNACKBAR({ message: e.message, hideDuration: 2000 })); + } + }; + +export const copyProcess = (data: CopyFormDialogData) => + async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => { + try { + const process = await dispatch(processCopyActions.copyProcess(data)); + dispatch(updateResources([process])); + dispatch(reloadProjectMatchingUuid([process.ownerUuid])); + dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Process has been copied.', hideDuration: 2000 })); + } catch (e) { + dispatch(snackbarActions.OPEN_SNACKBAR({ message: e.message, hideDuration: 2000 })); + } + }; + export const loadProcessLog = (uuid: string) => async (dispatch: Dispatch) => { const process = await dispatch(processesActions.loadProcess(uuid)); diff --git a/src/validators/validators.tsx b/src/validators/validators.tsx index 95edadfd..755cd7f7 100644 --- a/src/validators/validators.tsx +++ b/src/validators/validators.tsx @@ -19,3 +19,5 @@ export const COPY_NAME_VALIDATION = [require, maxLength(255)]; export const COPY_FILE_VALIDATION = [require]; export const MOVE_TO_VALIDATION = [require]; + +export const PROCESS_NAME_VALIDATION = [require, maxLength(255)]; \ No newline at end of file diff --git a/src/views-components/context-menu/action-sets/process-action-set.ts b/src/views-components/context-menu/action-sets/process-action-set.ts index 2897455b..107f1828 100644 --- a/src/views-components/context-menu/action-sets/process-action-set.ts +++ b/src/views-components/context-menu/action-sets/process-action-set.ts @@ -11,14 +11,16 @@ import { } from "~/components/icon/icon"; import { favoritePanelActions } from "~/store/favorite-panel/favorite-panel-action"; import { navigateToProcessLogs } from '~/store/navigation/navigation-action'; +import { openMoveProcessDialog } from '~/store/processes/process-move-actions'; +import { openProcessUpdateDialog } from "~/store/processes/process-update-actions"; +import { openCopyProcessDialog } from '~/store/processes/process-copy-actions'; +import { openProcessCommandDialog } from '../../../store/processes/process-command-actions'; export const processActionSet: ContextMenuActionSet = [[ { icon: RenameIcon, name: "Edit process", - execute: (dispatch, resource) => { - // add code - } + execute: (dispatch, resource) => dispatch(openProcessUpdateDialog(resource)) }, { icon: ShareIcon, @@ -30,9 +32,7 @@ export const processActionSet: ContextMenuActionSet = [[ { icon: MoveToIcon, name: "Move to", - execute: (dispatch, resource) => { - // add code - } + execute: (dispatch, resource) => dispatch(openMoveProcessDialog(resource)) }, { component: ToggleFavoriteAction, @@ -45,9 +45,7 @@ export const processActionSet: ContextMenuActionSet = [[ { icon: CopyIcon, name: "Copy to project", - execute: (dispatch, resource) => { - // add code - } + execute: (dispatch, resource) => dispatch(openCopyProcessDialog(resource)) }, { icon: ReRunProcessIcon, @@ -74,7 +72,7 @@ export const processActionSet: ContextMenuActionSet = [[ icon: CommandIcon, name: "Command", execute: (dispatch, resource) => { - // add code + dispatch(openProcessCommandDialog(resource.uuid)); } }, { diff --git a/src/views-components/context-menu/action-sets/process-resource-action-set.ts b/src/views-components/context-menu/action-sets/process-resource-action-set.ts new file mode 100644 index 00000000..b1985232 --- /dev/null +++ b/src/views-components/context-menu/action-sets/process-resource-action-set.ts @@ -0,0 +1,59 @@ +// Copyright (C) The Arvados Authors. All rights reserved. +// +// SPDX-License-Identifier: AGPL-3.0 + +import { ContextMenuActionSet } from "../context-menu-action-set"; +import { ToggleFavoriteAction } from "../actions/favorite-action"; +import { toggleFavorite } from "~/store/favorites/favorites-actions"; +import { RenameIcon, ShareIcon, MoveToIcon, CopyIcon, DetailsIcon, RemoveIcon } from "~/components/icon/icon"; +import { favoritePanelActions } from "~/store/favorite-panel/favorite-panel-action"; +import { openMoveProcessDialog } from '~/store/processes/process-move-actions'; +import { openProcessUpdateDialog } from "~/store/processes/process-update-actions"; +import { openCopyProcessDialog } from '~/store/processes/process-copy-actions'; + +export const processResourceActionSet: ContextMenuActionSet = [[ + { + icon: RenameIcon, + name: "Edit process", + execute: (dispatch, resource) => dispatch(openProcessUpdateDialog(resource)) + }, + { + icon: ShareIcon, + name: "Share", + execute: (dispatch, resource) => { + // add code + } + }, + { + icon: MoveToIcon, + name: "Move to", + execute: (dispatch, resource) => dispatch(openMoveProcessDialog(resource)) + }, + { + component: ToggleFavoriteAction, + execute: (dispatch, resource) => { + dispatch(toggleFavorite(resource)).then(() => { + dispatch(favoritePanelActions.REQUEST_ITEMS()); + }); + } + }, + { + icon: CopyIcon, + name: "Copy to project", + execute: (dispatch, resource) => dispatch(openCopyProcessDialog(resource)) + }, + { + icon: DetailsIcon, + name: "View details", + execute: (dispatch, resource) => { + // add code + } + }, + { + icon: RemoveIcon, + name: "Remove", + execute: (dispatch, resource) => { + // add code + } + } +]]; diff --git a/src/views-components/context-menu/context-menu.tsx b/src/views-components/context-menu/context-menu.tsx index a545b2bd..d5c82be2 100644 --- a/src/views-components/context-menu/context-menu.tsx +++ b/src/views-components/context-menu/context-menu.tsx @@ -65,5 +65,6 @@ export enum ContextMenuKind { COLLECTION = 'Collection', COLLECTION_RESOURCE = 'CollectionResource', PROCESS = "Process", + PROCESS_RESOURCE = 'ProcessResource', PROCESS_LOGS = "ProcessLogs" } diff --git a/src/views-components/details-panel/details-panel.tsx b/src/views-components/details-panel/details-panel.tsx index c0d4797f..f0075558 100644 --- a/src/views-components/details-panel/details-panel.tsx +++ b/src/views-components/details-panel/details-panel.tsx @@ -3,8 +3,9 @@ // SPDX-License-Identifier: AGPL-3.0 import * as React from 'react'; -import { Drawer, IconButton, Tabs, Tab, Typography, Grid } from '@material-ui/core'; +import { IconButton, Tabs, Tab, Typography, Grid, Tooltip } from '@material-ui/core'; import { StyleRulesCallback, WithStyles, withStyles } from '@material-ui/core/styles'; +import { Transition } from 'react-transition-group'; import { ArvadosTheme } from '~/common/custom-theme'; import * as classnames from "classnames"; import { connect } from 'react-redux'; @@ -20,45 +21,40 @@ import { ProcessDetails } from "./process-details"; import { EmptyDetails } from "./empty-details"; import { DetailsData } from "./details-data"; import { DetailsResource } from "~/models/details"; -import { getResource } from '../../store/resources/resources'; +import { getResource } from '~/store/resources/resources'; -type CssRules = 'root' | 'container' | 'opened' | 'headerContainer' | 'headerIcon' | 'headerTitle' | 'tabContainer'; +type CssRules = 'root' | 'container' | 'opened' | 'headerContainer' | 'headerIcon' | 'tabContainer'; -const drawerWidth = 320; +const DRAWER_WIDTH = 320; +const SLIDE_TIMEOUT = 500; const styles: StyleRulesCallback = (theme: ArvadosTheme) => ({ root: { - width: 0, - overflowX: 'hidden', - transition: 'width 0.5s ease', background: theme.palette.background.paper, borderLeft: `1px solid ${theme.palette.divider}`, height: '100%', + overflow: 'hidden', + transition: `width ${SLIDE_TIMEOUT}ms ease`, + width: 0, }, opened: { - width: drawerWidth, + width: DRAWER_WIDTH, }, container: { - width: drawerWidth, - }, - drawerPaper: { - position: 'relative', - width: drawerWidth + maxWidth: 'none', + width: DRAWER_WIDTH, }, headerContainer: { color: theme.palette.grey["600"], margin: `${theme.spacing.unit}px 0`, - textAlign: 'center' + textAlign: 'center', }, headerIcon: { - fontSize: '2.125rem' - }, - headerTitle: { - overflowWrap: 'break-word', - wordWrap: 'break-word' + fontSize: '2.125rem', }, tabContainer: { - padding: theme.spacing.unit * 3 - } + overflow: 'auto', + padding: theme.spacing.unit * 3, + }, }); const getItem = (resource: DetailsResource): DetailsData => { @@ -108,50 +104,68 @@ export const DetailsPanel = withStyles(styles)( this.setState({ tabsValue: value }); } - renderTabContainer = (children: React.ReactElement) => - - {children} - - render() { - const { classes, onCloseDrawer, isOpened, item } = this.props; - const { tabsValue } = this.state; + const { classes, isOpened } = this.props; return ( -
-
-
- - - {item.getIcon(classes.headerIcon)} - - - - {item.getTitle()} - - - - - {} - - - -
- - - - - {tabsValue === 0 && this.renderTabContainer( - - {item.getDetails()} - - )} - {tabsValue === 1 && this.renderTabContainer( - - )} -
-
+ + + {this.renderContent()} + + ); } + + renderContent() { + const { classes, onCloseDrawer, item } = this.props; + const { tabsValue } = this.state; + return + + + {item.getIcon(classes.headerIcon)} + + + + + {item.getTitle()} + + + + + + + + + + + + + + + + + {tabsValue === 0 + ? item.getDetails() + : null} + + ; + } } ) ); diff --git a/src/views-components/dialog-copy/dialog-collection-partial-copy.tsx b/src/views-components/dialog-copy/dialog-collection-partial-copy.tsx index 7fc301fa..7c335a35 100644 --- a/src/views-components/dialog-copy/dialog-collection-partial-copy.tsx +++ b/src/views-components/dialog-copy/dialog-collection-partial-copy.tsx @@ -19,10 +19,8 @@ export const DialogCollectionPartialCopy = (props: DialogCollectionPartialCopyPr {...props} />; -export const CollectionPartialCopyFields = () =>
-
- - -
+export const CollectionPartialCopyFields = () =>
+ +
; diff --git a/src/views-components/dialog-copy/dialog-collection-copy.tsx b/src/views-components/dialog-copy/dialog-copy.tsx similarity index 78% rename from src/views-components/dialog-copy/dialog-collection-copy.tsx rename to src/views-components/dialog-copy/dialog-copy.tsx index 029db578..41554159 100644 --- a/src/views-components/dialog-copy/dialog-collection-copy.tsx +++ b/src/views-components/dialog-copy/dialog-copy.tsx @@ -9,19 +9,19 @@ import { FormDialog } from '~/components/form-dialog/form-dialog'; import { ProjectTreePickerField } from '~/views-components/project-tree-picker/project-tree-picker'; import { COPY_NAME_VALIDATION, COPY_FILE_VALIDATION } from '~/validators/validators'; import { TextField } from "~/components/text-field/text-field"; -import { CollectionCopyFormDialogData } from "~/store/collections/collection-copy-actions"; +import { CopyFormDialogData } from '~/store/copy-dialog/copy-dialog'; -type CopyFormDialogProps = WithDialogProps & InjectedFormProps; +type CopyFormDialogProps = WithDialogProps & InjectedFormProps; -export const DialogCollectionCopy = (props: CopyFormDialogProps) => +export const DialogCopy = (props: CopyFormDialogProps) => ; -const CollectionCopyFields = () => +const CopyDialogFields = () => ({ + reduxForm({ form: COLLECTION_COPY_FORM_NAME, onSubmit: (data, dispatch) => { dispatch(copyCollection(data)); } }) -)(DialogCollectionCopy); \ No newline at end of file +)(DialogCopy); \ No newline at end of file diff --git a/src/views-components/dialog-forms/copy-process-dialog.ts b/src/views-components/dialog-forms/copy-process-dialog.ts new file mode 100644 index 00000000..4ec17c65 --- /dev/null +++ b/src/views-components/dialog-forms/copy-process-dialog.ts @@ -0,0 +1,21 @@ +// Copyright (C) The Arvados Authors. All rights reserved. +// +// SPDX-License-Identifier: AGPL-3.0 + +import { compose } from "redux"; +import { withDialog } from "~/store/dialog/with-dialog"; +import { reduxForm } from 'redux-form'; +import { PROCESS_COPY_FORM_NAME } from '~/store/processes/process-copy-actions'; +import { DialogCopy } from "~/views-components/dialog-copy/dialog-copy"; +import { copyProcess } from '~/store/workbench/workbench-actions'; +import { CopyFormDialogData } from '~/store/copy-dialog/copy-dialog'; + +export const CopyProcessDialog = compose( + withDialog(PROCESS_COPY_FORM_NAME), + reduxForm({ + form: PROCESS_COPY_FORM_NAME, + onSubmit: (data, dispatch) => { + dispatch(copyProcess(data)); + } + }) +)(DialogCopy); \ No newline at end of file diff --git a/src/views-components/dialog-forms/move-process-dialog.ts b/src/views-components/dialog-forms/move-process-dialog.ts new file mode 100644 index 00000000..baea34bc --- /dev/null +++ b/src/views-components/dialog-forms/move-process-dialog.ts @@ -0,0 +1,21 @@ +// Copyright (C) The Arvados Authors. All rights reserved. +// +// SPDX-License-Identifier: AGPL-3.0 + +import { compose } from 'redux'; +import { withDialog } from "~/store/dialog/with-dialog"; +import { reduxForm } from 'redux-form'; +import { PROCESS_MOVE_FORM_NAME } from '~/store/processes/process-move-actions'; +import { MoveToFormDialogData } from '~/store/move-to-dialog/move-to-dialog'; +import { DialogMoveTo } from '~/views-components/dialog-move/dialog-move-to'; +import { moveProcess } from '~/store/workbench/workbench-actions'; + +export const MoveProcessDialog = compose( + withDialog(PROCESS_MOVE_FORM_NAME), + reduxForm({ + form: PROCESS_MOVE_FORM_NAME, + onSubmit: (data, dispatch) => { + dispatch(moveProcess(data)); + } + }) +)(DialogMoveTo); \ No newline at end of file diff --git a/src/views-components/dialog-forms/update-process-dialog.ts b/src/views-components/dialog-forms/update-process-dialog.ts new file mode 100644 index 00000000..12d896d9 --- /dev/null +++ b/src/views-components/dialog-forms/update-process-dialog.ts @@ -0,0 +1,20 @@ +// Copyright (C) The Arvados Authors. All rights reserved. +// +// SPDX-License-Identifier: AGPL-3.0 + +import { compose } from "redux"; +import { reduxForm } from 'redux-form'; +import { withDialog } from "~/store/dialog/with-dialog"; +import { DialogProcessUpdate } from '~/views-components/dialog-update/dialog-process-update'; +import { PROCESS_UPDATE_FORM_NAME, ProcessUpdateFormDialogData } from '~/store/processes/process-update-actions'; +import { updateProcess } from "~/store/workbench/workbench-actions"; + +export const UpdateProcessDialog = compose( + withDialog(PROCESS_UPDATE_FORM_NAME), + reduxForm({ + form: PROCESS_UPDATE_FORM_NAME, + onSubmit: (data, dispatch) => { + dispatch(updateProcess(data)); + } + }) +)(DialogProcessUpdate); \ No newline at end of file diff --git a/src/views-components/dialog-update/dialog-process-update.tsx b/src/views-components/dialog-update/dialog-process-update.tsx new file mode 100644 index 00000000..d5bbce69 --- /dev/null +++ b/src/views-components/dialog-update/dialog-process-update.tsx @@ -0,0 +1,24 @@ +// Copyright (C) The Arvados Authors. All rights reserved. +// +// SPDX-License-Identifier: AGPL-3.0 + +import * as React from 'react'; +import { InjectedFormProps } from 'redux-form'; +import { WithDialogProps } from '~/store/dialog/with-dialog'; +import { ProcessUpdateFormDialogData } from '~/store/processes/process-update-actions'; +import { FormDialog } from '~/components/form-dialog/form-dialog'; +import { ProcessNameField } from '~/views-components/form-fields/process-form-fields'; + +type DialogProcessProps = WithDialogProps<{}> & InjectedFormProps; + +export const DialogProcessUpdate = (props: DialogProcessProps) => + ; + +const ProcessEditFields = () => + +; diff --git a/src/views-components/form-fields/collection-form-fields.tsx b/src/views-components/form-fields/collection-form-fields.tsx index af240fc5..ddd5bcef 100644 --- a/src/views-components/form-fields/collection-form-fields.tsx +++ b/src/views-components/form-fields/collection-form-fields.tsx @@ -29,6 +29,6 @@ export const CollectionProjectPickerField = () => validate={COLLECTION_PROJECT_VALIDATION} />; const ProjectPicker = (props: WrappedFieldProps) => -
+
props.input.onChange(projectUuid)} />
; diff --git a/src/views-components/form-fields/process-form-fields.tsx b/src/views-components/form-fields/process-form-fields.tsx new file mode 100644 index 00000000..cf471b67 --- /dev/null +++ b/src/views-components/form-fields/process-form-fields.tsx @@ -0,0 +1,15 @@ +// Copyright (C) The Arvados Authors. All rights reserved. +// +// SPDX-License-Identifier: AGPL-3.0 + +import * as React from "react"; +import { Field, WrappedFieldProps } from "redux-form"; +import { TextField } from "~/components/text-field/text-field"; +import { PROCESS_NAME_VALIDATION } from "~/validators/validators"; + +export const ProcessNameField = () => + ; diff --git a/src/views-components/process-command-dialog/process-command-dialog.tsx b/src/views-components/process-command-dialog/process-command-dialog.tsx new file mode 100644 index 00000000..4bde68d8 --- /dev/null +++ b/src/views-components/process-command-dialog/process-command-dialog.tsx @@ -0,0 +1,46 @@ +// Copyright (C) The Arvados Authors. All rights reserved. +// +// SPDX-License-Identifier: AGPL-3.0 + +import * as React from "react"; +import { Dialog, DialogTitle, DialogActions, Button, StyleRulesCallback, WithStyles, withStyles } from '@material-ui/core'; +import { withDialog } from "~/store/dialog/with-dialog"; +import { PROCESS_COMMAND_DIALOG_NAME } from '~/store/processes/process-command-actions'; +import { WithDialogProps } from '~/store/dialog/with-dialog'; +import { ProcessCommandDialogData } from '~/store/processes/process-command-actions'; +import { DefaultCodeSnippet } from "~/components/default-code-snippet/default-code-snippet"; +import { compose } from 'redux'; + +type CssRules = 'codeSnippet'; + +const styles: StyleRulesCallback = theme => ({ + codeSnippet: { + marginLeft: theme.spacing.unit * 3, + marginRight: theme.spacing.unit * 3, + } +}); + +export const ProcessCommandDialog = compose( + withDialog(PROCESS_COMMAND_DIALOG_NAME), + withStyles(styles), +)( + (props: WithDialogProps & WithStyles) => + + {`Command - ${props.data.processName}`} + + + + + +); \ No newline at end of file diff --git a/src/views/process-log-panel/process-log-code-snippet.tsx b/src/views/process-log-panel/process-log-code-snippet.tsx index ff6320ee..d02fc02c 100644 --- a/src/views/process-log-panel/process-log-code-snippet.tsx +++ b/src/views/process-log-panel/process-log-code-snippet.tsx @@ -3,10 +3,18 @@ // SPDX-License-Identifier: AGPL-3.0 import * as React from 'react'; -import { MuiThemeProvider, createMuiTheme } from '@material-ui/core/styles'; -import { CodeSnippet, CodeSnippetDataProps } from '~/components/code-snippet/code-snippet'; +import { MuiThemeProvider, createMuiTheme, StyleRulesCallback, withStyles, WithStyles } from '@material-ui/core/styles'; +import { CodeSnippet } from '~/components/code-snippet/code-snippet'; import grey from '@material-ui/core/colors/grey'; +type CssRules = 'codeSnippet'; + +const styles: StyleRulesCallback = () => ({ + codeSnippet: { + maxHeight: '550px', + } +}); + const theme = createMuiTheme({ overrides: { MuiTypography: { @@ -23,9 +31,12 @@ const theme = createMuiTheme({ } }); -type ProcessLogCodeSnippet = CodeSnippetDataProps; +interface ProcessLogCodeSnippetProps { + lines: string[]; +} -export const ProcessLogCodeSnippet = (props: ProcessLogCodeSnippet) => - - - ; \ No newline at end of file +export const ProcessLogCodeSnippet = withStyles(styles)( + (props: ProcessLogCodeSnippetProps & WithStyles) => + + + ); \ No newline at end of file diff --git a/src/views/process-log-panel/process-log-main-card.tsx b/src/views/process-log-panel/process-log-main-card.tsx index 66811f47..626568d1 100644 --- a/src/views/process-log-panel/process-log-main-card.tsx +++ b/src/views/process-log-panel/process-log-main-card.tsx @@ -53,10 +53,18 @@ interface ProcessLogMainCardDataProps { process: Process; } -export type ProcessLogMainCardProps = ProcessLogMainCardDataProps & CodeSnippetDataProps & ProcessLogFormDataProps & ProcessLogFormActionProps; +export interface ProcessLogMainCardActionProps { + onContextMenu: (event: React.MouseEvent, process: Process) => void; +} + +export type ProcessLogMainCardProps = ProcessLogMainCardDataProps + & ProcessLogMainCardActionProps + & CodeSnippetDataProps + & ProcessLogFormDataProps + & ProcessLogFormActionProps; export const ProcessLogMainCard = withStyles(styles)( - ({ classes, process, selectedFilter, filters, onChange, lines }: ProcessLogMainCardProps & WithStyles) => + ({ classes, process, selectedFilter, filters, onChange, lines, onContextMenu }: ProcessLogMainCardProps & WithStyles) => Back @@ -65,34 +73,35 @@ export const ProcessLogMainCard = withStyles(styles)( } action={ -
- - - - - -
- } + onContextMenu(event, process)} aria-label="More options"> + + + + } title={ {process.containerRequest.name} - - } + } subheader={process.containerRequest.description} /> {lines.length > 0 - ? < Grid container spacing={24} alignItems='center'> - - - - - - Go to Log collection + ? < Grid + container + spacing={24} + direction='column'> + + + + + + + Go to Log collection + - + diff --git a/src/views/process-log-panel/process-log-panel-root.tsx b/src/views/process-log-panel/process-log-panel-root.tsx index 0845a410..38870c40 100644 --- a/src/views/process-log-panel/process-log-panel-root.tsx +++ b/src/views/process-log-panel/process-log-panel-root.tsx @@ -10,14 +10,13 @@ import { ProcessLogFormDataProps, ProcessLogFormActionProps } from '~/views/proc import { DefaultView } from '~/components/default-view/default-view'; import { ProcessIcon } from '~/components/icon/icon'; import { CodeSnippetDataProps } from '~/components/code-snippet/code-snippet'; +import { ProcessLogMainCardActionProps } from './process-log-main-card'; export type ProcessLogPanelRootDataProps = { process?: Process; } & ProcessLogFormDataProps & CodeSnippetDataProps; -export type ProcessLogPanelRootActionProps = { - onContextMenu: (event: React.MouseEvent) => void; -} & ProcessLogFormActionProps; +export type ProcessLogPanelRootActionProps = ProcessLogMainCardActionProps & ProcessLogFormActionProps; export type ProcessLogPanelRootProps = ProcessLogPanelRootDataProps & ProcessLogPanelRootActionProps; diff --git a/src/views/process-log-panel/process-log-panel.tsx b/src/views/process-log-panel/process-log-panel.tsx index 2b2d6842..d578e784 100644 --- a/src/views/process-log-panel/process-log-panel.tsx +++ b/src/views/process-log-panel/process-log-panel.tsx @@ -2,13 +2,11 @@ // // SPDX-License-Identifier: AGPL-3.0 -import * as React from 'react'; import { RootState } from '~/store/store'; import { connect } from 'react-redux'; import { getProcess } from '~/store/processes/process'; import { Dispatch } from 'redux'; import { openProcessContextMenu } from '~/store/context-menu/context-menu-actions'; -import { matchProcessLogRoute } from '~/routes/routes'; import { ProcessLogPanelRootDataProps, ProcessLogPanelRootActionProps, ProcessLogPanelRoot } from './process-log-panel-root'; import { getProcessPanelLogs } from '~/store/process-logs-panel/process-logs-panel'; import { setProcessLogsPanelFilter } from '~/store/process-logs-panel/process-logs-panel-actions'; @@ -39,10 +37,10 @@ const mapStateToProps = (state: RootState): ProcessLogPanelRootDataProps => { }; const mapDispatchToProps = (dispatch: Dispatch): ProcessLogPanelRootActionProps => ({ - onContextMenu: (event: React.MouseEvent) => { - dispatch(openProcessContextMenu(event)); + onContextMenu: (event, process) => { + dispatch(openProcessContextMenu(event, process)); }, - onChange: (filter: FilterOption) => { + onChange: filter => { dispatch(setProcessLogsPanelFilter(filter.value)); } }); diff --git a/src/views/process-panel/process-panel-root.tsx b/src/views/process-panel/process-panel-root.tsx index d12704bc..ab8af36f 100644 --- a/src/views/process-panel/process-panel-root.tsx +++ b/src/views/process-panel/process-panel-root.tsx @@ -20,19 +20,19 @@ export interface ProcessPanelRootDataProps { } export interface ProcessPanelRootActionProps { - onContextMenu: (event: React.MouseEvent) => void; + onContextMenu: (event: React.MouseEvent, process: Process) => void; onToggle: (status: string) => void; } export type ProcessPanelRootProps = ProcessPanelRootDataProps & ProcessPanelRootActionProps; -export const ProcessPanelRoot = (props: ProcessPanelRootProps) => - props.process +export const ProcessPanelRoot = ({process, ...props}: ProcessPanelRootProps) => + process ? + process={process} + onContextMenu={event => props.onContextMenu(event, process)} /> ({ - onContextMenu: event => { - dispatch(openProcessContextMenu(event)); + onContextMenu: (event, process) => { + dispatch(openProcessContextMenu(event, process)); }, onToggle: status => { dispatch(toggleProcessPanelFilter(status)); diff --git a/src/views/process-panel/process-subprocesses.tsx b/src/views/process-panel/process-subprocesses.tsx index 6e00deb0..d3f87701 100644 --- a/src/views/process-panel/process-subprocesses.tsx +++ b/src/views/process-panel/process-subprocesses.tsx @@ -9,14 +9,16 @@ import { Process } from '~/store/processes/process'; export interface ProcessSubprocessesDataProps { subprocesses: Array; - onContextMenu: (event: React.MouseEvent) => void; + onContextMenu: (event: React.MouseEvent, process: Process) => void; } export const ProcessSubprocesses = ({ onContextMenu, subprocesses }: ProcessSubprocessesDataProps) => { return {subprocesses.map(subprocess => - + onContextMenu(event, subprocess)} + subprocess={subprocess} /> )} ; diff --git a/src/views/workbench/workbench.tsx b/src/views/workbench/workbench.tsx index e075680d..fee7652b 100644 --- a/src/views/workbench/workbench.tsx +++ b/src/views/workbench/workbench.tsx @@ -30,17 +30,20 @@ import { ProcessLogPanel } from '~/views/process-log-panel/process-log-panel'; import { CreateProjectDialog } from '~/views-components/dialog-forms/create-project-dialog'; import { CreateCollectionDialog } from '~/views-components/dialog-forms/create-collection-dialog'; import { CopyCollectionDialog } from '~/views-components/dialog-forms/copy-collection-dialog'; +import { CopyProcessDialog } from '~/views-components/dialog-forms/copy-process-dialog'; import { UpdateCollectionDialog } from '~/views-components/dialog-forms/update-collection-dialog'; +import { UpdateProcessDialog } from '~/views-components/dialog-forms/update-process-dialog'; import { UpdateProjectDialog } from '~/views-components/dialog-forms/update-project-dialog'; +import { MoveProcessDialog } from '~/views-components/dialog-forms/move-process-dialog'; import { MoveProjectDialog } from '~/views-components/dialog-forms/move-project-dialog'; import { MoveCollectionDialog } from '~/views-components/dialog-forms/move-collection-dialog'; import { FilesUploadCollectionDialog } from '~/views-components/dialog-forms/files-upload-collection-dialog'; import { PartialCopyCollectionDialog } from '~/views-components/dialog-forms/partial-copy-collection-dialog'; - import { TrashPanel } from "~/views/trash-panel/trash-panel"; -import { MainContentBar } from '../../views-components/main-content-bar/main-content-bar'; +import { MainContentBar } from '~/views-components/main-content-bar/main-content-bar'; import { Grid } from '@material-ui/core'; import { SharedWithMePanel } from '../shared-with-me-panel/shared-with-me-panel'; +import { ProcessCommandDialog } from '~/views-components/process-command-dialog/process-command-dialog'; type CssRules = 'root' | 'contentWrapper' | 'content' | 'appBar'; @@ -144,21 +147,25 @@ export const Workbench = withStyles(styles)( } - - + + - - + + - - - - + - + + + + + + + + ; } diff --git a/yarn.lock b/yarn.lock index 67c12647..35992710 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6307,7 +6307,7 @@ react-test-renderer@^16.0.0-0: prop-types "^15.6.0" react-is "^16.4.1" -react-transition-group@^2.2.1: +react-transition-group@2.4.0, react-transition-group@^2.2.1: version "2.4.0" resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-2.4.0.tgz#1d9391fabfd82e016f26fabd1eec329dbd922b5a" dependencies: