merge master
authorPawel Kowalczyk <pawel.kowalczyk@contractors.roche.com>
Thu, 13 Sep 2018 10:08:01 +0000 (12:08 +0200)
committerPawel Kowalczyk <pawel.kowalczyk@contractors.roche.com>
Thu, 13 Sep 2018 10:08:01 +0000 (12:08 +0200)
Feature #14186

Arvados-DCO-1.1-Signed-off-by: Pawel Kowalczyk <pawel.kowalczyk@contractors.roche.com>

29 files changed:
src/components/code-snippet/code-snippet.tsx
src/components/default-code-snippet/default-code-snippet.tsx
src/components/icon/icon.tsx
src/services/api/filter-builder.test.ts
src/services/api/filter-builder.ts
src/store/context-menu/context-menu-actions.ts
src/store/processes/process-command-actions.ts [new file with mode: 0644]
src/store/processes/process-update-actions.ts [new file with mode: 0644]
src/store/trash-panel/trash-panel-middleware-service.ts
src/store/workbench/workbench-actions.ts
src/validators/validators.tsx
src/views-components/context-menu/action-sets/process-action-set.ts
src/views-components/context-menu/action-sets/process-resource-action-set.ts
src/views-components/dialog-forms/update-process-dialog.ts [new file with mode: 0644]
src/views-components/dialog-update/dialog-process-update.tsx [new file with mode: 0644]
src/views-components/form-fields/process-form-fields.tsx [new file with mode: 0644]
src/views-components/process-command-dialog/process-command-dialog.tsx [new file with mode: 0644]
src/views-components/side-panel-button/side-panel-button.tsx [new file with mode: 0644]
src/views-components/side-panel/side-panel.tsx
src/views/process-log-panel/process-log-code-snippet.tsx
src/views/process-log-panel/process-log-main-card.tsx
src/views/process-log-panel/process-log-panel-root.tsx
src/views/process-log-panel/process-log-panel.tsx
src/views/process-panel/process-panel-root.tsx
src/views/process-panel/process-panel.tsx
src/views/process-panel/process-subprocesses.tsx
src/views/project-panel/project-panel.tsx
src/views/trash-panel/trash-panel.tsx
src/views/workbench/workbench.tsx

index eb0e709a9b0dfa572860e15b796b436e065b41e3..6cba299f1580a70d8076afc98606af1479ab5fb1 100644 (file)
@@ -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<CssRules> = (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<CssRules> = (theme: ArvadosTheme) => ({
 
 export interface CodeSnippetDataProps {
     lines: string[];
+    className?: string;
 }
 
 type CodeSnippetProps = CodeSnippetDataProps & WithStyles<CssRules>;
 
 export const CodeSnippet = withStyles(styles)(
-    ({ classes, lines }: CodeSnippetProps) =>
-        <Typography component="div" className={classes.root}>
+    ({ classes, lines, className }: CodeSnippetProps) =>
+        <Typography 
+        component="div" 
+        className={classNames(classes.root, className)}>
             {
                 lines.map((line: string, index: number) => {
                     return <Typography key={index} component="pre">{line}</Typography>;
index b8c0a7be93ba96acebca6e77f4c973859b79876d..e8b89f321ca809302bcbdab1f09f4c13d09f6d38 100644 (file)
@@ -23,9 +23,7 @@ const theme = createMuiTheme({
     }
 });
 
-type DefaultCodeSnippet = CodeSnippetDataProps;
-
-export const DefaultCodeSnippet = (props: DefaultCodeSnippet) => 
+export const DefaultCodeSnippet = (props: CodeSnippetDataProps) => 
     <MuiThemeProvider theme={theme}>
-        <CodeSnippet lines={props.lines} />
+        <CodeSnippet {...props} />
     </MuiThemeProvider>;
\ No newline at end of file
index afc0fed1adbc6c6aba4d4b8096d794d3f7e154fa..c0668b8291fa3e7b89010ef0638cb8f2c16bafdd 100644 (file)
@@ -4,6 +4,7 @@
 
 import * as React from 'react';
 import AccessTime from '@material-ui/icons/AccessTime';
+import Add from '@material-ui/icons/Add';
 import ArrowBack from '@material-ui/icons/ArrowBack';
 import ArrowDropDown from '@material-ui/icons/ArrowDropDown';
 import BubbleChart from '@material-ui/icons/BubbleChart';
@@ -48,6 +49,7 @@ import HelpOutline from '@material-ui/icons/HelpOutline';
 
 export type IconType = React.SFC<{ className?: string }>;
 
+export const AddIcon: IconType = (props) => <Add {...props} />;
 export const AddFavoriteIcon: IconType = (props) => <StarBorder {...props} />;
 export const AdvancedIcon: IconType = (props) => <SettingsApplications {...props} />;
 export const BackIcon: IconType = (props) => <ArrowBack {...props} />;
index de2ba4cba65bbd008f581d20c4db5c16b1cbdc05..5f646de5f72911af6708ae78b80e2155ffe8a1a1 100644 (file)
@@ -12,12 +12,18 @@ describe("FilterBuilder", () => {
         filters = new FilterBuilder();
     });
 
-    it("should add 'equal' rule", () => {
+    it("should add 'equal' rule (string)", () => {
         expect(
             filters.addEqual("etag", "etagValue").getFilters()
         ).toEqual(`["etag","=","etagValue"]`);
     });
 
+    it("should add 'equal' rule (boolean)", () => {
+        expect(
+            filters.addEqual("is_trashed", true).getFilters()
+        ).toEqual(`["is_trashed","=",true]`);
+    });
+
     it("should add 'like' rule", () => {
         expect(
             filters.addLike("etag", "etagValue").getFilters()
index b5558dbb16a291f8ecb81fdbd642f742bb99b9d3..06a040e3cc373f8206c83783c90e8cdf010d095a 100644 (file)
@@ -11,7 +11,7 @@ export function joinFilters(filters0?: string, filters1?: string) {
 export class FilterBuilder {
     constructor(private filters = "") { }
 
-    public addEqual(field: string, value?: string, resourcePrefix?: string) {
+    public addEqual(field: string, value?: string | boolean, resourcePrefix?: string) {
         return this.addCondition(field, "=", value, "", "", resourcePrefix );
     }
 
@@ -35,11 +35,15 @@ export class FilterBuilder {
         return this.filters;
     }
 
-    private addCondition(field: string, cond: string, value?: string | string[], prefix: string = "", postfix: string = "", resourcePrefix?: string) {
+    private addCondition(field: string, cond: string, value?: string | string[] | boolean, prefix: string = "", postfix: string = "", resourcePrefix?: string) {
         if (value) {
-            value = typeof value === "string"
-                ? `"${prefix}${value}${postfix}"`
-                : `["${value.join(`","`)}"]`;
+            if (typeof value === "string") {
+                value = `"${prefix}${value}${postfix}"`;
+            } else if (Array.isArray(value)) {
+                value = `["${value.join(`","`)}"]`;
+            } else {
+                value = value ? "true" : "false";
+            }
 
             const resPrefix = resourcePrefix
                 ? _.snakeCase(resourcePrefix) + "."
index eabf41ae715ad715e6072ca46383c0499461d170..d85059d6e1e25a82dfb20c051a55c1c63ffcab0d 100644 (file)
@@ -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<HTMLElement>, i
         }
     };
 
-export const openProcessContextMenu = (event: React.MouseEvent<HTMLElement>) =>
+export const openProcessContextMenu = (event: React.MouseEvent<HTMLElement>, 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: '',
diff --git a/src/store/processes/process-command-actions.ts b/src/store/processes/process-command-actions.ts
new file mode 100644 (file)
index 0000000..de55a2c
--- /dev/null
@@ -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<any>, 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-update-actions.ts b/src/store/processes/process-update-actions.ts
new file mode 100644 (file)
index 0000000..92cf032
--- /dev/null
@@ -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
index 19ed3be13194982a97527417adb3b4eff8438389..6e8fa542478766368968e0912fc636da5f4c9b6b 100644 (file)
@@ -19,7 +19,6 @@ import { TrashPanelColumnNames, TrashPanelFilter } from "~/views/trash-panel/tra
 import { ProjectResource } from "~/models/project";
 import { ProjectPanelColumnNames } from "~/views/project-panel/project-panel";
 import { updateFavorites } from "~/store/favorites/favorites-actions";
-import { TrashableResource } from "~/models/resource";
 import { snackbarActions } from "~/store/snackbar/snackbar-actions";
 import { updateResources } from "~/store/resources/resources-actions";
 
@@ -57,14 +56,13 @@ export class TrashPanelMiddlewareService extends DataExplorerMiddlewareService {
                         .addIsA("uuid", typeFilters.map(f => f.type))
                         .addILike("name", dataExplorer.searchValue, GroupContentsResourcePrefix.COLLECTION)
                         .addILike("name", dataExplorer.searchValue, GroupContentsResourcePrefix.PROJECT)
+                        .addEqual("is_trashed", true)
                         .getFilters(),
                     recursive: true,
                     includeTrash: true
                 });
 
-            const items = listResults.items
-                .filter(it => (it as TrashableResource).isTrashed)
-                .map(it => it.uuid);
+            const items = listResults.items.map(it => it.uuid);
 
             api.dispatch(trashPanelActions.SET_ITEMS({
                 ...listResultsToDataExplorerItemsMeta(listResults),
index 647395bd77ead7d1b286e2a5a07840bace194d80..d1b0fae953281b562d7edebeb2c9c5d1bcf8435d 100644 (file)
@@ -30,6 +30,7 @@ import * as collectionUpdateActions from '~/store/collections/collection-update-
 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";
@@ -37,7 +38,6 @@ import { initProcessLogsPanel } from '../process-logs-panel/process-logs-panel-a
 import { loadProcessPanel } from '~/store/process-panel/process-panel-actions';
 import { CopyFormDialogData } from '~/store/copy-dialog/copy-dialog';
 
-
 export const loadWorkbench = () =>
     async (dispatch: Dispatch, getState: () => RootState) => {
         const { auth, router } = getState();
@@ -197,6 +197,23 @@ export const loadProcess = (uuid: string) =>
 
     };
 
+export const updateProcess = (data: processUpdateActions.ProcessUpdateFormDialogData) =>
+    async (dispatch: Dispatch) => {
+        try {
+            const process = await dispatch<any>(processUpdateActions.updateProcess(data));
+            if (process) {
+                dispatch(snackbarActions.OPEN_SNACKBAR({
+                    message: "Process has been successfully updated.",
+                    hideDuration: 2000
+                }));
+                dispatch<any>(updateResources([process]));
+                dispatch<any>(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 {
index 95edadfdd1809b23e83aff5ec47043f761b9e30f..755cd7f7b4c7be4b7a1650653c3b7783406c1179 100644 (file)
@@ -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
index 891064bad89a0456946e20bc04b3849db13a6b29..107f1828c609f1d25f65073c4fbdba581282740e 100644 (file)
@@ -12,15 +12,15 @@ import {
 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<any>(openProcessUpdateDialog(resource))
     },
     {
         icon: ShareIcon,
@@ -72,7 +72,7 @@ export const processActionSet: ContextMenuActionSet = [[
         icon: CommandIcon,
         name: "Command",
         execute: (dispatch, resource) => {
-            // add code
+            dispatch<any>(openProcessCommandDialog(resource.uuid));
         }
     },
     {
index ac092268f755ec8321bb19896c5962231200d1ff..b1985232e5f5ab5162d409a98a8c4cb5556605eb 100644 (file)
@@ -8,15 +8,14 @@ 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) => {
-            // add code
-        }
+        execute: (dispatch, resource) => dispatch<any>(openProcessUpdateDialog(resource))
     },
     {
         icon: ShareIcon,
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 (file)
index 0000000..12d896d
--- /dev/null
@@ -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<ProcessUpdateFormDialogData>({
+        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 (file)
index 0000000..d5bbce6
--- /dev/null
@@ -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<ProcessUpdateFormDialogData>;
+
+export const DialogProcessUpdate = (props: DialogProcessProps) =>
+    <FormDialog
+        dialogTitle='Edit Process'
+        formFields={ProcessEditFields}
+        submitLabel='Save'
+        {...props}
+    />;
+
+const ProcessEditFields = () => <span>
+    <ProcessNameField />
+</span>;
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 (file)
index 0000000..cf471b6
--- /dev/null
@@ -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 = () =>
+    <Field
+        name='name'
+        component={TextField}
+        validate={PROCESS_NAME_VALIDATION}
+        label="Process Name" />;
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 (file)
index 0000000..4bde68d
--- /dev/null
@@ -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<CssRules> = 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<ProcessCommandDialogData> & WithStyles<CssRules>) =>
+        <Dialog
+            open={props.open}
+            maxWidth="md"
+            onClose={props.closeDialog}
+            style={{ alignSelf: 'stretch' }}>
+            <DialogTitle>{`Command - ${props.data.processName}`}</DialogTitle>
+            <DefaultCodeSnippet
+                className={props.classes.codeSnippet}
+                lines={[props.data.command]} />
+            <DialogActions>
+                <Button
+                    variant='flat'
+                    color='primary'
+                    onClick={props.closeDialog}>
+                    Close
+                </Button>
+            </DialogActions>
+        </Dialog>
+);
\ No newline at end of file
diff --git a/src/views-components/side-panel-button/side-panel-button.tsx b/src/views-components/side-panel-button/side-panel-button.tsx
new file mode 100644 (file)
index 0000000..e39b378
--- /dev/null
@@ -0,0 +1,120 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as React from 'react';
+import { connect, DispatchProp } from 'react-redux';
+import { RootState } from '~/store/store';
+import { getProperty } from '~/store/properties/properties';
+import { PROJECT_PANEL_CURRENT_UUID } from '~/store/project-panel/project-panel-action';
+import { ArvadosTheme } from '~/common/custom-theme';
+import { PopoverOrigin } from '@material-ui/core/Popover';
+import { StyleRulesCallback, WithStyles, withStyles, Toolbar, Grid, Button, MenuItem, Menu } from '@material-ui/core';
+import { AddIcon, CollectionIcon, ProcessIcon, ProjectIcon } from '~/components/icon/icon';
+import { openProjectCreateDialog } from '~/store/projects/project-create-actions';
+import { openCollectionCreateDialog } from '~/store/collections/collection-create-actions';
+import { matchProjectRoute } from '~/routes/routes';
+
+type CssRules = 'button' | 'menuItem' | 'icon';
+
+const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
+    button: {
+        boxShadow: 'none',
+        padding: '2px 10px 2px 5px',
+        fontSize: '0.75rem'
+    },
+    menuItem: {
+        fontSize: '0.875rem',
+        color: theme.palette.grey["700"]
+    },
+    icon: {
+        marginRight: theme.spacing.unit
+    }
+});
+
+interface SidePanelDataProps {
+    currentItemId: string;
+    buttonVisible: boolean;
+}
+
+interface SidePanelState {
+    anchorEl: any;
+}
+
+type SidePanelProps = SidePanelDataProps & DispatchProp & WithStyles<CssRules>;
+
+const transformOrigin: PopoverOrigin = {
+    vertical: -50,
+    horizontal: 45
+};
+
+const isButtonVisible = ({ router }: RootState) => {
+    const pathname = router.location ? router.location.pathname : '';
+    const match = matchProjectRoute(pathname);
+    return !!match;
+};
+
+export const SidePanelButton = withStyles(styles)(
+    connect((state: RootState) => ({
+        currentItemId: getProperty(PROJECT_PANEL_CURRENT_UUID)(state.properties),
+        buttonVisible: isButtonVisible(state)
+    }))(
+        class extends React.Component<SidePanelProps> {
+
+            state: SidePanelState = {
+                anchorEl: undefined
+            };
+
+            render() {
+                const { classes, buttonVisible  } = this.props;
+                const { anchorEl } = this.state;
+                return <Toolbar>
+                    {buttonVisible  && <Grid container>
+                        <Grid container item xs alignItems="center" justify="center">
+                            <Button variant="contained" color="primary" size="small" className={classes.button}
+                                aria-owns={anchorEl ? 'aside-menu-list' : undefined}
+                                aria-haspopup="true"
+                                onClick={this.handleOpen}>
+                                <AddIcon />
+                                New
+                            </Button>
+                            <Menu
+                                id='aside-menu-list'
+                                anchorEl={anchorEl}
+                                open={Boolean(anchorEl)}
+                                onClose={this.handleClose}
+                                onClick={this.handleClose}
+                                transformOrigin={transformOrigin}>
+                                <MenuItem className={classes.menuItem} onClick={this.handleNewCollectionClick}>
+                                    <CollectionIcon className={classes.icon} /> New collection
+                                </MenuItem>
+                                <MenuItem className={classes.menuItem}>
+                                    <ProcessIcon className={classes.icon} /> Run a process
+                                </MenuItem>
+                                <MenuItem className={classes.menuItem} onClick={this.handleNewProjectClick}>
+                                    <ProjectIcon className={classes.icon} /> New project
+                                </MenuItem>
+                            </Menu>
+                        </Grid>
+                    </Grid> }
+                </Toolbar>;
+            }
+
+            handleNewProjectClick = () => {
+                this.props.dispatch<any>(openProjectCreateDialog(this.props.currentItemId));
+            }
+
+            handleNewCollectionClick = () => {
+                this.props.dispatch<any>(openCollectionCreateDialog(this.props.currentItemId));
+            }
+
+            handleClose = () => {
+                this.setState({ anchorEl: undefined });
+            }
+
+            handleOpen = (event: React.MouseEvent<HTMLButtonElement>) => {
+                this.setState({ anchorEl: event.currentTarget });
+            }
+        }
+    )
+);
\ No newline at end of file
index 70bc92b7162aef6acc157dac5a1dc15d26d295a7..fffe3344c9ce66dd94c3a8d79933f83047aaf7a9 100644 (file)
@@ -4,12 +4,13 @@
 
 import * as React from 'react';
 import { StyleRulesCallback, WithStyles, withStyles } from '@material-ui/core/styles';
-import Drawer from '@material-ui/core/Drawer';
 import { ArvadosTheme } from '~/common/custom-theme';
 import { SidePanelTree, SidePanelTreeProps } from '~/views-components/side-panel-tree/side-panel-tree';
 import { compose, Dispatch } from 'redux';
 import { connect } from 'react-redux';
 import { navigateFromSidePanel } from '../../store/side-panel/side-panel-action';
+import { Grid } from '@material-ui/core';
+import { SidePanelButton } from '~/views-components/side-panel-button/side-panel-button';
 
 const DRAWER_WITDH = 240;
 
@@ -35,6 +36,7 @@ export const SidePanel = compose(
     withStyles(styles),
     connect(undefined, mapDispatchToProps)
 )(({ classes, ...props }: WithStyles<CssRules> & SidePanelTreeProps) =>
-    <div className={classes.root}>
+    <Grid item xs>
+        <SidePanelButton />
         <SidePanelTree {...props} />
-    </div>);
+    </Grid>);
\ No newline at end of file
index ff6320eeaf03f76c711044b3bfc819a0c5c6e837..d02fc02ce1431c74a2aefdaf4137991d0552ba61 100644 (file)
@@ -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<CssRules> = () => ({
+    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) => 
-    <MuiThemeProvider theme={theme}>
-        <CodeSnippet lines={props.lines} />
-    </MuiThemeProvider>;
\ No newline at end of file
+export const ProcessLogCodeSnippet = withStyles(styles)(
+    (props: ProcessLogCodeSnippetProps & WithStyles<CssRules>) =>
+        <MuiThemeProvider theme={theme}>
+            <CodeSnippet lines={props.lines} className={props.classes.codeSnippet} />
+        </MuiThemeProvider>);
\ No newline at end of file
index 66811f47b89209191c7de02d329efa4d2259da49..626568d179e2e6a4428f52db8952bcd5d95f1483 100644 (file)
@@ -53,10 +53,18 @@ interface ProcessLogMainCardDataProps {
     process: Process;
 }
 
-export type ProcessLogMainCardProps = ProcessLogMainCardDataProps & CodeSnippetDataProps & ProcessLogFormDataProps & ProcessLogFormActionProps;
+export interface ProcessLogMainCardActionProps {
+    onContextMenu: (event: React.MouseEvent<any>, process: Process) => void;
+}
+
+export type ProcessLogMainCardProps = ProcessLogMainCardDataProps
+    & ProcessLogMainCardActionProps
+    & CodeSnippetDataProps
+    & ProcessLogFormDataProps
+    & ProcessLogFormActionProps;
 
 export const ProcessLogMainCard = withStyles(styles)(
-    ({ classes, process, selectedFilter, filters, onChange, lines }: ProcessLogMainCardProps & WithStyles<CssRules>) =>
+    ({ classes, process, selectedFilter, filters, onChange, lines, onContextMenu }: ProcessLogMainCardProps & WithStyles<CssRules>) =>
         <Grid item xs={12}>
             <Link to={`/processes/${process.containerRequest.uuid}`} className={classes.backLink}>
                 <BackIcon className={classes.backIcon} /> Back
@@ -65,34 +73,35 @@ export const ProcessLogMainCard = withStyles(styles)(
                 <CardHeader
                     avatar={<ProcessIcon className={classes.iconHeader} />}
                     action={
-                        <div>
-                            <IconButton aria-label="More options">
-                                <Tooltip title="More options">
-                                    <MoreOptionsIcon />
-                                </Tooltip>
-                            </IconButton>
-                        </div>
-                    }
+                        <IconButton onClick={event => onContextMenu(event, process)} aria-label="More options">
+                            <Tooltip title="More options">
+                                <MoreOptionsIcon />
+                            </Tooltip>
+                        </IconButton>}
                     title={
                         <Tooltip title={process.containerRequest.name} placement="bottom-start">
                             <Typography noWrap variant="title" className={classes.title}>
                                 {process.containerRequest.name}
                             </Typography>
-                        </Tooltip>
-                    }
+                        </Tooltip>}
                     subheader={process.containerRequest.description} />
                 <CardContent>
                     {lines.length > 0
-                        ? < Grid container spacing={24} alignItems='center'>
-                            <Grid item xs={6}>
-                                <ProcessLogForm selectedFilter={selectedFilter} filters={filters} onChange={onChange} />
-                            </Grid>
-                            <Grid item xs={6} className={classes.link}>
-                                <Typography component='div'>
-                                    Go to Log collection
+                        ? < Grid
+                            container
+                            spacing={24}
+                            direction='column'>
+                            <Grid container item>
+                                <Grid item xs={6}>
+                                    <ProcessLogForm selectedFilter={selectedFilter} filters={filters} onChange={onChange} />
+                                </Grid>
+                                <Grid item xs={6} className={classes.link}>
+                                    <Typography component='div'>
+                                        Go to Log collection
                                 </Typography>
+                                </Grid>
                             </Grid>
-                            <Grid item xs={12}>
+                            <Grid item xs>
                                 <ProcessLogCodeSnippet lines={lines} />
                             </Grid>
                         </Grid>
index 0845a4109780d1f8995e5d47d80eca99eead0b43..38870c402759814d3e9b4a0d5abaed08b5d4991c 100644 (file)
@@ -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<HTMLElement>) => void;
-} & ProcessLogFormActionProps;
+export type ProcessLogPanelRootActionProps = ProcessLogMainCardActionProps & ProcessLogFormActionProps;
 
 export type ProcessLogPanelRootProps = ProcessLogPanelRootDataProps & ProcessLogPanelRootActionProps;
 
index 2b2d6842774fdc168a200864df22dc7df15bfe48..d578e784566b5d1c1d5bdd3caeb9672998c5e95f 100644 (file)
@@ -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<HTMLElement>) => {
-        dispatch<any>(openProcessContextMenu(event));
+    onContextMenu: (event, process) => {
+        dispatch<any>(openProcessContextMenu(event, process));
     },
-    onChange: (filter: FilterOption) => {
+    onChange: filter => {
         dispatch(setProcessLogsPanelFilter(filter.value));
     }
 });
index d12704bcf4b69c87ec59283dc824feba3e1dd308..ab8af36ffbb5e619e69fa22e2fbbb62b07d0a634 100644 (file)
@@ -20,19 +20,19 @@ export interface ProcessPanelRootDataProps {
 }
 
 export interface ProcessPanelRootActionProps {
-    onContextMenu: (event: React.MouseEvent<HTMLElement>) => void;
+    onContextMenu: (event: React.MouseEvent<HTMLElement>, 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
         ? <Grid container spacing={16} alignItems="stretch">
             <Grid item sm={12} md={7}>
                 <ProcessInformationCard
-                    process={props.process}
-                    onContextMenu={props.onContextMenu} />
+                    process={process}
+                    onContextMenu={event => props.onContextMenu(event, process)} />
             </Grid>
             <Grid item sm={12} md={5}>
                 <SubprocessesCard
index b3e36ab77c1742c646db51d2672b76d198a2c0b7..4f283a6c9cadb56826b74b47d09a19501a0253df 100644 (file)
@@ -27,8 +27,8 @@ const mapStateToProps = ({ router, resources, processPanel }: RootState): Proces
 };
 
 const mapDispatchToProps = (dispatch: Dispatch): ProcessPanelRootActionProps => ({
-    onContextMenu: event => {
-        dispatch<any>(openProcessContextMenu(event));
+    onContextMenu: (event, process) => {
+        dispatch<any>(openProcessContextMenu(event, process));
     },
     onToggle: status => {
         dispatch<any>(toggleProcessPanelFilter(status));
index 6e00deb0f1a0963ce8f69280ec84ed86c0c072dd..d3f87701fc73724799a8968df0b5da29d394d4bb 100644 (file)
@@ -9,14 +9,16 @@ import { Process } from '~/store/processes/process';
 
 export interface ProcessSubprocessesDataProps {
     subprocesses: Array<Process>;
-    onContextMenu: (event: React.MouseEvent<HTMLElement>) => void;
+    onContextMenu: (event: React.MouseEvent<HTMLElement>, process: Process) => void;
 }
 
 export const ProcessSubprocesses = ({ onContextMenu, subprocesses }: ProcessSubprocessesDataProps) => {
     return <Grid container spacing={16}>
         {subprocesses.map(subprocess =>
             <Grid item xs={12} sm={6} md={4} lg={2} key={subprocess.containerRequest.uuid}>
-                <ProcessSubprocessesCard onContextMenu={onContextMenu} subprocess={subprocess} />
+                <ProcessSubprocessesCard
+                    onContextMenu={event => onContextMenu(event, subprocess)}
+                    subprocess={subprocess} />
             </Grid>
         )}
     </Grid>;
index 63aedaddeb4e17a62cfd318a12cdea66114c87e2..2c962ef829a5158b743a52b0ab548221c02ce63a 100644 (file)
@@ -31,7 +31,7 @@ import { filterResources } from '~/store/resources/resources';
 import { PanelDefaultView } from '~/components/panel-default-view/panel-default-view';
 import { DataTableDefaultView } from '~/components/data-table-default-view/data-table-default-view';
 
-type CssRules = 'root' | "toolbar" | "button";
+type CssRules = 'root' | "button";
 
 const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
     root: {
@@ -39,10 +39,6 @@ const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
         width: '100%',
         height: '100%'
     },
-    toolbar: {
-        paddingBottom: theme.spacing.unit * 3,
-        textAlign: "right"
-    },
     button: {
         marginLeft: theme.spacing.unit
     },
@@ -143,17 +139,6 @@ export const ProjectPanel = withStyles(styles)(
             render() {
                 const { classes } = this.props;
                 return <div className={classes.root}>
-                    <div className={classes.toolbar}>
-                        <Button color="primary" onClick={this.handleNewCollectionClick} variant="raised" className={classes.button}>
-                            New collection
-                        </Button>
-                        <Button color="primary" variant="raised" className={classes.button}>
-                            Run a process
-                        </Button>
-                        <Button color="primary" onClick={this.handleNewProjectClick} variant="raised" className={classes.button}>
-                            New project
-                        </Button>
-                    </div>
                     {this.hasAnyItems()
                         ? <DataExplorer
                             id={PROJECT_PANEL_ID}
@@ -179,14 +164,6 @@ export const ProjectPanel = withStyles(styles)(
                 return resource.ownerUuid === this.props.currentItemId;
             }
 
-            handleNewProjectClick = () => {
-                this.props.dispatch<any>(openProjectCreateDialog(this.props.currentItemId));
-            }
-
-            handleNewCollectionClick = () => {
-                this.props.dispatch<any>(openCollectionCreateDialog(this.props.currentItemId));
-            }
-
             handleContextMenu = (event: React.MouseEvent<HTMLElement>, resourceUuid: string) => {
                 const menuKind = resourceKindToContextMenuKind(resourceUuid);
                 const resource = getResource<ProjectResource>(resourceUuid)(this.props.resources);
index 4a1e197d540dc059a8cfe15c6d642c218a308a0d..db7d8f688096e34cb80ca4a1e7bc796b1e0d11bb 100644 (file)
@@ -99,11 +99,6 @@ export const trashPanelColumns: DataColumns<string, TrashPanelFilter> = [
                 selected: true,
                 type: ResourceKind.COLLECTION
             },
-            {
-                name: resourceLabel(ResourceKind.PROCESS),
-                selected: true,
-                type: ResourceKind.PROCESS
-            },
             {
                 name: resourceLabel(ResourceKind.PROJECT),
                 selected: true,
index e5d26a281bc88848d6b2cb4bdd17cc5519b82af2..a24a8c96a300cada422f3f90219381a0ef67fabe 100644 (file)
@@ -32,6 +32,7 @@ import { CreateCollectionDialog } from '~/views-components/dialog-forms/create-c
 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';
@@ -39,11 +40,12 @@ import { MoveCollectionDialog } from '~/views-components/dialog-forms/move-colle
 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 { WorkbenchProgress } from '~/views-components/progress/workbench-progress';
+import { ProcessCommandDialog } from '~/views-components/process-command-dialog/process-command-dialog';
 
-type CssRules = 'root' | 'contentWrapper' | 'content' | 'appBar';
+type CssRules = 'root' | 'asidePanel' | 'contentWrapper' | 'content' | 'appBar';
 
 const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
     root: {
@@ -51,6 +53,10 @@ const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
         width: '100vw',
         height: '100vh'
     },
+    asidePanel: {
+        maxWidth: '240px',
+        background: theme.palette.background.default
+    },
     contentWrapper: {
         background: theme.palette.background.default,
         minWidth: 0,
@@ -93,14 +99,11 @@ export const Workbench = withStyles(styles)(
             state = {
                 searchText: "",
             };
-
             render() {
+                const { classes } = this.props;
                 return <>
-                    <Grid
-                        container
-                        direction="column"
-                        className={this.props.classes.root}>
-                        <Grid className={this.props.classes.appBar}>
+                    <Grid container direction="column" className={classes.root}>
+                        <Grid className={classes.appBar}>
                             <MainAppBar
                                 searchText={this.state.searchText}
                                 user={this.props.user}
@@ -108,27 +111,16 @@ export const Workbench = withStyles(styles)(
                                 buildInfo={this.props.buildInfo} />
                         </Grid>
                         {this.props.user &&
-                            <Grid
-                                container
-                                item
-                                xs
-                                alignItems="stretch"
-                                wrap="nowrap">
-                                <Grid item>
+                            <Grid container item xs alignItems="stretch" wrap="nowrap">
+                                <Grid container item xs component='aside' direction='column' className={classes.asidePanel}>
                                     <SidePanel />
                                 </Grid>
-                                <Grid
-                                    container
-                                    item
-                                    xs
-                                    component="main"
-                                    direction="column"
-                                    className={this.props.classes.contentWrapper}>
+                                <Grid container item xs component="main" direction="column" className={classes.contentWrapper}>
                                     <Grid item>
                                         <WorkbenchProgress />
                                         <MainContentBar />
                                     </Grid>
-                                    <Grid item xs className={this.props.classes.content}>
+                                    <Grid item xs className={classes.content}>
                                         <Switch>
                                             <Route path={Routes.PROJECTS} component={ProjectPanel} />
                                             <Route path={Routes.COLLECTIONS} component={CollectionPanel} />
@@ -145,23 +137,25 @@ export const Workbench = withStyles(styles)(
                             </Grid>}
                     </Grid>
                     <ContextMenu />
-                    <Snackbar />
-                    <CreateProjectDialog />
-                    <CreateCollectionDialog />
-                    <RenameFileDialog />
-                    <PartialCopyCollectionDialog />
-                    <FileRemoveDialog />
                     <CopyCollectionDialog />
                     <CopyProcessDialog />
+                    <CreateCollectionDialog />
+                    <CreateProjectDialog />
+                    <CurrentTokenDialog />
+                    <FileRemoveDialog />
                     <FileRemoveDialog />
-                    <MultipleFilesRemoveDialog />
-                    <UpdateCollectionDialog />
                     <FilesUploadCollectionDialog />
-                    <UpdateProjectDialog />
                     <MoveCollectionDialog />
                     <MoveProcessDialog />
                     <MoveProjectDialog />
-                    <CurrentTokenDialog />
+                    <MultipleFilesRemoveDialog />
+                    <PartialCopyCollectionDialog />
+                    <ProcessCommandDialog />
+                    <RenameFileDialog />
+                    <Snackbar />
+                    <UpdateCollectionDialog />
+                    <UpdateProcessDialog />
+                    <UpdateProjectDialog />
                 </>;
             }