From 829d595bb4b9e7c0a8a1dd38995b4b5e197841f5 Mon Sep 17 00:00:00 2001 From: Stephen Smith Date: Wed, 20 Jul 2022 21:06:03 -0400 Subject: [PATCH] 16070: Replace process command dialog with command card Arvados-DCO-1.1-Signed-off-by: Stephen Smith --- src/components/code-snippet/code-snippet.tsx | 10 +- .../processes/process-command-actions.ts | 28 ---- .../process-resource-action-set.ts | 10 +- .../process-command-dialog.tsx | 61 -------- src/views/process-panel/process-cmd-card.tsx | 133 ++++++++++++++++++ .../process-panel/process-panel-root.tsx | 11 +- src/views/process-panel/process-panel.tsx | 2 +- src/views/workbench/workbench.tsx | 2 - 8 files changed, 148 insertions(+), 109 deletions(-) delete mode 100644 src/store/processes/process-command-actions.ts delete mode 100644 src/views-components/process-command-dialog/process-command-dialog.tsx create mode 100644 src/views/process-panel/process-cmd-card.tsx diff --git a/src/components/code-snippet/code-snippet.tsx b/src/components/code-snippet/code-snippet.tsx index f0a2b213..f156e3e8 100644 --- a/src/components/code-snippet/code-snippet.tsx +++ b/src/components/code-snippet/code-snippet.tsx @@ -33,10 +33,8 @@ export const CodeSnippet = withStyles(styles)( - { - lines.map((line: string, index: number) => { - return {line}; - }) - } + + {lines.join('\n')} + - ); \ No newline at end of file + ); diff --git a/src/store/processes/process-command-actions.ts b/src/store/processes/process-command-actions.ts deleted file mode 100644 index d81a7c4d..00000000 --- a/src/store/processes/process-command-actions.ts +++ /dev/null @@ -1,28 +0,0 @@ -// 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/store'; -import { Dispatch } from 'redux'; -import { getProcess } from 'store/processes/process'; -import shellescape from 'shell-escape'; - -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: shellescape(process.containerRequest.command), - processName: process.containerRequest.name, - }; - dispatch(dialogActions.OPEN_DIALOG({ id: PROCESS_COMMAND_DIALOG_NAME, data })); - } - }; 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 index 55b2d31f..65e29bef 100644 --- 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 @@ -20,7 +20,6 @@ import { toggleDetailsPanel } from 'store/details-panel/details-panel-action'; import { snackbarActions, SnackbarKind } from "store/snackbar/snackbar-actions"; import { openProcessInputDialog } from "store/processes/process-input-actions"; import { navigateToOutput } from "store/process-panel/process-panel-actions"; -import { openProcessCommandDialog } from "store/processes/process-command-actions"; import { openAdvancedTabDialog } from "store/advanced-tab/advanced-tab"; import { TogglePublicFavoriteAction } from "../actions/public-favorite-action"; import { togglePublicFavorite } from "store/public-favorites/public-favorites-actions"; @@ -69,13 +68,6 @@ export const readOnlyProcessResourceActionSet: ContextMenuActionSet = [[ } } }, - { - icon: CommandIcon, - name: "Command", - execute: (dispatch, resource) => { - dispatch(openProcessCommandDialog(resource.uuid)); - } - }, { icon: DetailsIcon, name: "View details", @@ -135,4 +127,4 @@ export const processResourceAdminActionSet: ContextMenuActionSet = [[ }); } }, -]]; \ No newline at end of file +]]; diff --git a/src/views-components/process-command-dialog/process-command-dialog.tsx b/src/views-components/process-command-dialog/process-command-dialog.tsx deleted file mode 100644 index 7695837e..00000000 --- a/src/views-components/process-command-dialog/process-command-dialog.tsx +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (C) The Arvados Authors. All rights reserved. -// -// SPDX-License-Identifier: AGPL-3.0 - -import React from "react"; -import { Dialog, DialogActions, Button, StyleRulesCallback, WithStyles, withStyles, Tooltip, IconButton, CardHeader } 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'; -import CopyToClipboard from "react-copy-to-clipboard"; -import { CopyIcon } from 'components/icon/icon'; - -type CssRules = 'codeSnippet' | 'copyToClipboard'; - -const styles: StyleRulesCallback = theme => ({ - codeSnippet: { - marginLeft: theme.spacing.unit * 3, - marginRight: theme.spacing.unit * 3, - }, - copyToClipboard: { - marginRight: theme.spacing.unit, - } -}); - -export const ProcessCommandDialog = compose( - withDialog(PROCESS_COMMAND_DIALOG_NAME), - withStyles(styles), -)( - (props: WithDialogProps & WithStyles) => - - - - - - - - - } /> - - - - - -); \ No newline at end of file diff --git a/src/views/process-panel/process-cmd-card.tsx b/src/views/process-panel/process-cmd-card.tsx new file mode 100644 index 00000000..36a0128b --- /dev/null +++ b/src/views/process-panel/process-cmd-card.tsx @@ -0,0 +1,133 @@ +// Copyright (C) The Arvados Authors. All rights reserved. +// +// SPDX-License-Identifier: AGPL-3.0 + +import React from 'react'; +import { + StyleRulesCallback, + WithStyles, + withStyles, + Card, + CardHeader, + IconButton, + CardContent, + Tooltip, + Typography, + Grid, +} from '@material-ui/core'; +import { ArvadosTheme } from 'common/custom-theme'; +import { CloseIcon, CommandIcon, CopyIcon } from 'components/icon/icon'; +import { MPVPanelProps } from 'components/multi-panel-view/multi-panel-view'; +import { DefaultCodeSnippet } from 'components/default-code-snippet/default-code-snippet'; +import { Process } from 'store/processes/process'; +import shellescape from 'shell-escape'; +import CopyToClipboard from 'react-copy-to-clipboard'; + +type CssRules = 'card' | 'content' | 'title' | 'header' | 'avatar' | 'iconHeader'; + +const styles: StyleRulesCallback = (theme: ArvadosTheme) => ({ + card: { + height: '100%' + }, + header: { + paddingTop: theme.spacing.unit, + paddingBottom: theme.spacing.unit, + }, + iconHeader: { + fontSize: '1.875rem', + color: theme.customs.colors.green700, + }, + avatar: { + alignSelf: 'flex-start', + paddingTop: theme.spacing.unit * 0.5 + }, + content: { + padding: theme.spacing.unit * 1.0, + paddingTop: theme.spacing.unit * 0.5, + '&:last-child': { + paddingBottom: theme.spacing.unit * 1, + } + }, + title: { + overflow: 'hidden', + paddingTop: theme.spacing.unit * 0.5 + }, +}); + +export interface ProcessCmdCardDataProps { + process: Process; + onCopy: (text: string) => void; +} + +type ProcessCmdCardProps = ProcessCmdCardDataProps & WithStyles & MPVPanelProps; + +export const ProcessCmdCard = withStyles(styles)( + ({ + process, + onCopy, + classes, + doHidePanel, + }: ProcessCmdCardProps) => { + const command = process.containerRequest.command.map((v) => + shellescape([v]) // Escape each arg separately + ); + + let formattedCommand = [...command]; + formattedCommand.forEach((item, i, arr) => { + // Indent lines after the first + const indent = i > 0 ? ' ' : ''; + // Escape newlines on every non-last arg when there are multiple lines + const lineBreak = arr.length > 1 && i < arr.length - 1 ? ' \\' : ''; + arr[i] = `${indent}${item}${lineBreak}`; + }); + + return ( + + } + title={ + + Command + + } + action={ + + + + + onCopy("Command copied to clipboard")} + > + + + + + + + {doHidePanel && ( + + + + + + )} + + + } + /> + + + + + ); + } +); diff --git a/src/views/process-panel/process-panel-root.tsx b/src/views/process-panel/process-panel-root.tsx index 4f95d0d8..f8ff8430 100644 --- a/src/views/process-panel/process-panel-root.tsx +++ b/src/views/process-panel/process-panel-root.tsx @@ -15,6 +15,7 @@ import { ProcessDetailsCard } from './process-details-card'; import { getProcessPanelLogs, ProcessLogsPanel } from 'store/process-logs-panel/process-logs-panel'; import { ProcessLogsCard } from './process-log-card'; import { FilterOption } from 'views/process-panel/process-log-form'; +import { ProcessCmdCard } from './process-cmd-card'; type CssRules = 'root'; @@ -37,13 +38,14 @@ export interface ProcessPanelRootActionProps { cancelProcess: (uuid: string) => void; onLogFilterChange: (filter: FilterOption) => void; navigateToLog: (uuid: string) => void; - onLogCopyToClipboard: (uuid: string) => void; + onCopyToClipboard: (uuid: string) => void; } export type ProcessPanelRootProps = ProcessPanelRootDataProps & ProcessPanelRootActionProps & WithStyles; const panelsData: MPVPanelState[] = [ {name: "Details"}, + {name: "Command"}, {name: "Logs", visible: true}, {name: "Subprocesses"}, ]; @@ -59,9 +61,14 @@ export const ProcessPanelRoot = withStyles(styles)( cancelProcess={props.cancelProcess} /> + + + ({ - onLogCopyToClipboard: (message: string) => { + onCopyToClipboard: (message: string) => { dispatch(snackbarActions.OPEN_SNACKBAR({ message, hideDuration: 2000, diff --git a/src/views/workbench/workbench.tsx b/src/views/workbench/workbench.tsx index 28fae4cd..a6c49e34 100644 --- a/src/views/workbench/workbench.tsx +++ b/src/views/workbench/workbench.tsx @@ -33,7 +33,6 @@ import { MoveProjectDialog } from 'views-components/dialog-forms/move-project-di 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 { ProcessCommandDialog } from 'views-components/process-command-dialog/process-command-dialog'; import { RemoveProcessDialog } from 'views-components/process-remove-dialog/process-remove-dialog'; import { MainContentBar } from 'views-components/main-content-bar/main-content-bar'; import { Grid } from '@material-ui/core'; @@ -241,7 +240,6 @@ export const WorkbenchPanel = - -- 2.30.2