refs #master Merge branch 'origin/master' into 13828-trash-view
authorDaniel Kos <daniel.kos@contractors.roche.com>
Thu, 30 Aug 2018 17:59:36 +0000 (19:59 +0200)
committerDaniel Kos <daniel.kos@contractors.roche.com>
Thu, 30 Aug 2018 17:59:36 +0000 (19:59 +0200)
# Conflicts:
# package.json
# src/models/container-request.ts
# src/models/resource.ts
# src/services/collection-service/collection-service.ts
# src/services/services.ts
# src/store/navigation/navigation-action.ts
# src/store/project/project-action.ts
# src/store/project/project-reducer.test.ts
# src/store/project/project-reducer.ts
# src/store/side-panel/side-panel-reducer.ts
# src/store/store.ts
# src/views-components/context-menu/action-sets/collection-action-set.ts
# src/views-components/context-menu/action-sets/collection-resource-action-set.ts
# src/views-components/context-menu/action-sets/project-action-set.ts
# src/views/favorite-panel/favorite-panel-item.ts
# src/views/project-panel/project-panel-item.ts
# src/views/workbench/workbench.tsx

Arvados-DCO-1.1-Signed-off-by: Daniel Kos <daniel.kos@contractors.roche.com>

1  2 
src/models/resource.ts
src/services/collection-service/collection-service.ts
src/store/context-menu/context-menu-actions.ts
src/store/store.ts
src/views-components/context-menu/action-sets/collection-action-set.ts
src/views-components/context-menu/action-sets/collection-resource-action-set.ts
src/views-components/data-explorer/renderers.tsx
src/views/collection-panel/collection-panel.tsx
src/views/favorite-panel/favorite-panel.tsx
src/views/project-panel/project-panel.tsx
src/views/workbench/workbench.tsx

diff --combined src/models/resource.ts
index ab487da070c29c5a30632266477c38959d548012,3290bdfe06f07ed60fa27accd067cff47f0d0b6b..aff1b2417d6fe06a04b9ada5e1bf5fbb31282876
@@@ -14,16 -14,52 +14,58 @@@ export interface Resource 
      etag: string;
  }
  
 +export interface TrashResource extends Resource {
 +    trashAt: string;
 +    deleteAt: string;
 +    isTrashed: boolean;
 +}
 +
  export enum ResourceKind {
      COLLECTION = "arvados#collection",
+     CONTAINER = "arvados#container",
+     CONTAINER_REQUEST = "arvados#containerRequest",
      GROUP = "arvados#group",
      PROCESS = "arvados#containerRequest",
      PROJECT = "arvados#group",
-     WORKFLOW = "arvados#workflow"
+     USER = "arvados#user",
+     WORKFLOW = "arvados#workflow",
  }
+ export enum ResourceObjectType {
+     COLLECTION = '4zz18',
+     CONTAINER = 'dz642',
+     CONTAINER_REQUEST = 'xvhdp',
+     GROUP = 'j7d0g',
+     USER = 'tpzed',
+ }
+ export const RESOURCE_UUID_PATTERN = '.{5}-.{5}-.{15}';
+ export const RESOURCE_UUID_REGEX = new RegExp(RESOURCE_UUID_PATTERN);
+ export const isResourceUuid = (uuid: string) =>
+     RESOURCE_UUID_REGEX.test(uuid);
+ export const extractUuidObjectType = (uuid: string) => {
+     const match = RESOURCE_UUID_REGEX.exec(uuid);
+     return match
+         ? match[0].split('-')[1]
+         : undefined;
+ };
+ export const extractUuidKind = (uuid: string = '') => {
+     const objectType = extractUuidObjectType(uuid);
+     switch (objectType) {
+         case ResourceObjectType.USER:
+             return ResourceKind.USER;
+         case ResourceObjectType.GROUP:
+             return ResourceKind.GROUP;
+         case ResourceObjectType.COLLECTION:
+             return ResourceKind.COLLECTION;
+         case ResourceObjectType.CONTAINER_REQUEST:
+             return ResourceKind.CONTAINER_REQUEST;
+         case ResourceObjectType.CONTAINER:
+             return ResourceKind.CONTAINER;
+         default:
+             return undefined;
+     }
+ };
index ad493b5a21483dc04677a52998070c5615a62e95,c0d61bd27b4d5d789a09a0096c16154ddcbbb461..e26da78875b91963a6a1e51703f2a1f9ffbc17cc
  //
  // SPDX-License-Identifier: AGPL-3.0
  
- import * as _ from "lodash";
  import { CommonResourceService } from "~/common/api/common-resource-service";
  import { CollectionResource } from "~/models/collection";
- import axios, { AxiosInstance } from "axios";
- import { KeepService } from "../keep-service/keep-service";
+ import { AxiosInstance } from "axios";
+ import { CollectionFile, CollectionDirectory } from "~/models/collection-file";
  import { WebDAV } from "~/common/webdav";
  import { AuthService } from "../auth-service/auth-service";
- import { mapTree, getNodeChildren, getNode, TreeNode } from "../../models/tree";
- import { getTagValue } from "~/common/xml";
- import { FilterBuilder } from "~/common/api/filter-builder";
- import { CollectionFile, createCollectionFile, CollectionFileType, CollectionDirectory, createCollectionDirectory } from '~/models/collection-file';
- import { parseKeepManifestText, stringifyKeepManifest } from "../collection-files-service/collection-manifest-parser";
- import { KeepManifestStream } from "~/models/keep-manifest";
- import { createCollectionFilesTree } from '~/models/collection-file';
+ import { mapTreeValues } from "~/models/tree";
+ import { parseFilesResponse } from "./collection-service-files-response";
+ import { fileToArrayBuffer } from "~/common/file";
  
  export type UploadProgress = (fileId: number, loaded: number, total: number, currentTime: number) => void;
  
  export class CollectionService extends CommonResourceService<CollectionResource> {
-     constructor(serverApi: AxiosInstance, private keepService: KeepService, private webdavClient: WebDAV, private authService: AuthService) {
+     constructor(serverApi: AxiosInstance, private webdavClient: WebDAV, private authService: AuthService) {
          super(serverApi, "collections");
      }
  
      async files(uuid: string) {
-         const request = await this.webdavClient.propfind(`/c=${uuid}`);
+         const request = await this.webdavClient.propfind(`c=${uuid}`);
          if (request.responseXML != null) {
-             const files = this.extractFilesData(request.responseXML);
-             const tree = createCollectionFilesTree(files);
-             const sortedTree = mapTree(node => {
-                 const children = getNodeChildren(node.id)(tree).map(id => getNode(id)(tree)) as TreeNode<CollectionDirectory | CollectionFile>[];
-                 children.sort((a, b) =>
-                     a.value.type !== b.value.type
-                         ? a.value.type === CollectionFileType.DIRECTORY ? -1 : 1
-                         : a.value.name.localeCompare(b.value.name)
-                 );
-                 return { ...node, children: children.map(child => child.id) };
-             })(tree);
-             return sortedTree;
+             const filesTree = parseFilesResponse(request.responseXML);
+             return mapTreeValues(this.extendFileURL)(filesTree);
          }
          return Promise.reject();
      }
  
-     async deleteFile(collectionUuid: string, filePath: string) {
-         return this.webdavClient.delete(`/c=${collectionUuid}${filePath}`);
-     }
-     extractFilesData(document: Document) {
-         const collectionUrlPrefix = /\/c=[0-9a-zA-Z\-]*/;
-         return Array
-             .from(document.getElementsByTagName('D:response'))
-             .slice(1) // omit first element which is collection itself
-             .map(element => {
-                 const name = getTagValue(element, 'D:displayname', '');
-                 const size = parseInt(getTagValue(element, 'D:getcontentlength', '0'), 10);
-                 const pathname = getTagValue(element, 'D:href', '');
-                 const nameSuffix = `/${name || ''}`;
-                 const directory = pathname
-                     .replace(collectionUrlPrefix, '')
-                     .replace(nameSuffix, '');
-                 const href = this.webdavClient.defaults.baseURL + pathname + '?api_token=' + this.authService.getApiToken();
-                 const data = {
-                     url: href,
-                     id: `${directory}/${name}`,
-                     name,
-                     path: directory,
-                 };
-                 return getTagValue(element, 'D:resourcetype', '')
-                     ? createCollectionDirectory(data)
-                     : createCollectionFile({ ...data, size });
-             });
+     async deleteFiles(collectionUuid: string, filePaths: string[]) {
+         for (const path of filePaths) {
+             await this.webdavClient.delete(`c=${collectionUuid}${path}`);
+         }
      }
  
-     private readFile(file: File): Promise<ArrayBuffer> {
-         return new Promise<ArrayBuffer>(resolve => {
-             const reader = new FileReader();
-             reader.onload = () => {
-                 resolve(reader.result as ArrayBuffer);
-             };
-             reader.readAsArrayBuffer(file);
-         });
+     async uploadFiles(collectionUuid: string, files: File[], onProgress?: UploadProgress) {
+         // files have to be uploaded sequentially
+         for (let idx = 0; idx < files.length; idx++) {
+             await this.uploadFile(collectionUuid, files[idx], idx, onProgress);
+         }
      }
  
-     private uploadFile(keepServiceHost: string, file: File, fileId: number, onProgress?: UploadProgress): Promise<CollectionFile> {
-         return this.readFile(file).then(content => {
-             return axios.post<string>(keepServiceHost, content, {
-                 headers: {
-                     'Content-Type': 'text/octet-stream'
-                 },
-                 onUploadProgress: (e: ProgressEvent) => {
-                     if (onProgress) {
-                         onProgress(fileId, e.loaded, e.total, Date.now());
-                     }
-                     console.log(`${e.loaded} / ${e.total}`);
-                 }
-             }).then(data => createCollectionFile({
-                 id: data.data,
-                 name: file.name,
-                 size: file.size
-             }));
-         });
+     moveFile(collectionUuid: string, oldPath: string, newPath: string) {
+         return this.webdavClient.move(
+             `c=${collectionUuid}${oldPath}`,
+             `c=${collectionUuid}${encodeURI(newPath)}`
+         );
      }
  
-     private async updateManifest(collectionUuid: string, files: CollectionFile[]): Promise<CollectionResource> {
-         const collection = await this.get(collectionUuid);
-         const manifest: KeepManifestStream[] = parseKeepManifestText(collection.manifestText);
-         files.forEach(f => {
-             let kms = manifest.find(stream => stream.name === f.path);
-             if (!kms) {
-                 kms = {
-                     files: [],
-                     locators: [],
-                     name: f.path
-                 };
-                 manifest.push(kms);
+     private extendFileURL = (file: CollectionDirectory | CollectionFile) => ({
+         ...file,
+         url: this.webdavClient.defaults.baseURL + file.url + '?api_token=' + this.authService.getApiToken()
+     })
+     private async uploadFile(collectionUuid: string, file: File, fileId: number, onProgress: UploadProgress = () => { return; }) {
+         const fileURL = `c=${collectionUuid}/${file.name}`;
+         const fileContent = await fileToArrayBuffer(file);
+         const requestConfig = {
+             headers: {
+                 'Content-Type': 'text/octet-stream'
+             },
+             onUploadProgress: (e: ProgressEvent) => {
+                 onProgress(fileId, e.loaded, e.total, Date.now());
              }
-             kms.locators.push(f.id);
-             const len = kms.files.length;
-             const nextPos = len > 0
-                 ? parseInt(kms.files[len - 1].position, 10) + kms.files[len - 1].size
-                 : 0;
-             kms.files.push({
-                 name: f.name,
-                 position: nextPos.toString(),
-                 size: f.size
-             });
-         });
-         console.log(manifest);
-         const manifestText = stringifyKeepManifest(manifest);
-         const data = { ...collection, manifestText };
-         return this.update(collectionUuid, CommonResourceService.mapKeys(_.snakeCase)(data));
-     }
-     uploadFiles(collectionUuid: string, files: File[], onProgress?: UploadProgress): Promise<CollectionResource | never> {
-         const filters = new FilterBuilder()
-             .addEqual("service_type", "proxy");
-         return this.keepService.list({ filters: filters.getFilters() }).then(data => {
-             if (data.items && data.items.length > 0) {
-                 const serviceHost =
-                     (data.items[0].serviceSslFlag ? "https://" : "http://") +
-                     data.items[0].serviceHost +
-                     ":" + data.items[0].servicePort;
-                 console.log("serviceHost", serviceHost);
+         };
+         return this.webdavClient.put(fileURL, fileContent, requestConfig);
  
-                 const files$ = files.map((f, idx) => this.uploadFile(serviceHost, f, idx, onProgress));
-                 return Promise.all(files$).then(values => {
-                     return this.updateManifest(collectionUuid, values);
-                 });
-             } else {
-                 return Promise.reject("Missing keep service host");
-             }
-         });
      }
  
 +    trash(uuid: string): Promise<CollectionResource> {
 +        return this.serverApi
 +            .post(this.resourceType + `${uuid}/trash`)
 +            .then(CommonResourceService.mapResponseKeys);
 +    }
 +
 +    untrash(uuid: string): Promise<CollectionResource> {
 +        const params = {
 +            ensure_unique_name: true
 +        };
 +        return this.serverApi
 +            .post(this.resourceType + `${uuid}/untrash`, {
 +                params: CommonResourceService.mapKeys(_.snakeCase)(params)
 +            })
 +            .then(CommonResourceService.mapResponseKeys);
 +    }
++    
  }
index 8e5eb1e795791260474d92e0fffe6e596560081d,cf66a53d2361587823219d3d698cfa2572fd07d5..a1ed6c5536bc4b71bb2b6b8e26ed5fc4f16dfcf7
  //
  // SPDX-License-Identifier: AGPL-3.0
  
- import { default as unionize, ofType, UnionOf } from "unionize";
+ import { unionize, ofType, UnionOf } from '~/common/unionize';
  import { ContextMenuPosition, ContextMenuResource } from "./context-menu-reducer";
 -import { UserResource } from '../../models/user';
+ import { ContextMenuKind } from '~/views-components/context-menu/context-menu';
+ import { Dispatch } from 'redux';
+ import { RootState } from '~/store/store';
+ import { getResource } from '../resources/resources';
+ import { ProjectResource } from '~/models/project';
++import { UserResource } from '~/models/user';
+ import { isSidePanelTreeCategory } from '~/store/side-panel-tree/side-panel-tree-actions';
+ import { extractUuidKind, ResourceKind } from '~/models/resource';
  
  export const contextMenuActions = unionize({
      OPEN_CONTEXT_MENU: ofType<{ position: ContextMenuPosition, resource: ContextMenuResource }>(),
      CLOSE_CONTEXT_MENU: ofType<{}>()
- }, {
-         tag: 'type',
-         value: 'payload'
-     });
+ });
  
  export type ContextMenuAction = UnionOf<typeof contextMenuActions>;
 -export const openContextMenu = (event: React.MouseEvent<HTMLElement>, resource: { name: string; uuid: string; description?: string; kind: ContextMenuKind; }) =>
 -        const userResource = getResource<UserResource>(projectUuid)(getState().resources);
 -        if (userResource) {
++export type ContextMenuResource = {
++    name: string;
++    uuid: string;
++    ownerUuid: string;
++    description?: string;
++    kind: ContextMenuKind;
++    isTrashed?: boolean;
++}
++
++export const openContextMenu = (event: React.MouseEvent<HTMLElement>, resource: ContextMenuResource) =>
+     (dispatch: Dispatch) => {
+         event.preventDefault();
+         dispatch(
+             contextMenuActions.OPEN_CONTEXT_MENU({
+                 position: { x: event.clientX, y: event.clientY },
+                 resource
+             })
+         );
+     };
+ export const openRootProjectContextMenu = (event: React.MouseEvent<HTMLElement>, projectUuid: string) =>
+     (dispatch: Dispatch, getState: () => RootState) => {
 -                uuid: userResource.uuid,
 -                kind: ContextMenuKind.ROOT_PROJECT
++        const res = getResource<UserResource>(projectUuid)(getState().resources);
++        if (res) {
+             dispatch<any>(openContextMenu(event, {
+                 name: '',
 -        const projectResource = getResource<ProjectResource>(projectUuid)(getState().resources);
 -        if (projectResource) {
++                uuid: res.uuid,
++                ownerUuid: res.uuid,
++                kind: ContextMenuKind.ROOT_PROJECT,
++                isTrashed: false
+             }));
+         }
+     };
+ export const openProjectContextMenu = (event: React.MouseEvent<HTMLElement>, projectUuid: string) =>
+     (dispatch: Dispatch, getState: () => RootState) => {
 -                name: projectResource.name,
 -                uuid: projectResource.uuid,
 -                kind: ContextMenuKind.PROJECT
++        const res = getResource<ProjectResource>(projectUuid)(getState().resources);
++        if (res) {
+             dispatch<any>(openContextMenu(event, {
++                name: res.name,
++                uuid: res.uuid,
++                kind: ContextMenuKind.PROJECT,
++                ownerUuid: res.ownerUuid,
++                isTrashed: res.isTrashed
+             }));
+         }
+     };
+ export const openSidePanelContextMenu = (event: React.MouseEvent<HTMLElement>, id: string) =>
+     (dispatch: Dispatch, getState: () => RootState) => {
+         if (!isSidePanelTreeCategory(id)) {
+             const kind = extractUuidKind(id);
+             if (kind === ResourceKind.USER) {
+                 dispatch<any>(openRootProjectContextMenu(event, id));
+             } else if (kind === ResourceKind.PROJECT) {
+                 dispatch<any>(openProjectContextMenu(event, id));
+             }
+         }
+     };
+ export const openProcessContextMenu = (event: React.MouseEvent<HTMLElement>) =>
+     (dispatch: Dispatch, getState: () => RootState) => {
+         const resource = {
+             uuid: '',
+             name: '',
+             description: '',
+             kind: ContextMenuKind.PROCESS
+         };
+         dispatch<any>(openContextMenu(event, resource));
+     };
+ export const resourceKindToContextMenuKind = (uuid: string) => {
+     const kind = extractUuidKind(uuid);
+     switch (kind) {
+         case ResourceKind.PROJECT:
+             return ContextMenuKind.PROJECT;
+         case ResourceKind.COLLECTION:
+             return ContextMenuKind.COLLECTION_RESOURCE;
+         case ResourceKind.USER:
+             return ContextMenuKind.ROOT_PROJECT;
+         default:
+             return;
+     }
+ };
diff --combined src/store/store.ts
index febaf933d0e099763be33a25cfc75299536c4ce5,4fe0f97a697718ee2e5c37b0375291d658b33e25..584d05e923fd55d8f443be7512c7f16f88234c26
@@@ -3,77 -3,43 +3,43 @@@
  // SPDX-License-Identifier: AGPL-3.0
  
  import { createStore, applyMiddleware, compose, Middleware, combineReducers, Store, Action, Dispatch } from 'redux';
- import { routerMiddleware, routerReducer, RouterState } from "react-router-redux";
+ import { routerMiddleware, routerReducer } from "react-router-redux";
  import thunkMiddleware from 'redux-thunk';
  import { History } from "history";
  
- import { projectsReducer, ProjectState } from "./project/project-reducer";
- import { sidePanelReducer, SidePanelState } from './side-panel/side-panel-reducer';
- import { authReducer, AuthState } from "./auth/auth-reducer";
- import { dataExplorerReducer, DataExplorerState } from './data-explorer/data-explorer-reducer';
- import { detailsPanelReducer, DetailsPanelState } from './details-panel/details-panel-reducer';
- import { contextMenuReducer, ContextMenuState } from './context-menu/context-menu-reducer';
+ import { authReducer } from "./auth/auth-reducer";
+ import { dataExplorerReducer } from './data-explorer/data-explorer-reducer';
+ import { detailsPanelReducer } from './details-panel/details-panel-reducer';
+ import { contextMenuReducer } from './context-menu/context-menu-reducer';
  import { reducer as formReducer } from 'redux-form';
- import { FavoritesState, favoritesReducer } from './favorites/favorites-reducer';
- import { snackbarReducer, SnackbarState } from './snackbar/snackbar-reducer';
- import { CollectionPanelFilesState } from './collection-panel/collection-panel-files/collection-panel-files-state';
+ import { favoritesReducer } from './favorites/favorites-reducer';
+ import { snackbarReducer } from './snackbar/snackbar-reducer';
  import { collectionPanelFilesReducer } from './collection-panel/collection-panel-files/collection-panel-files-reducer';
  import { dataExplorerMiddleware } from "./data-explorer/data-explorer-middleware";
  import { FAVORITE_PANEL_ID } from "./favorite-panel/favorite-panel-action";
  import { PROJECT_PANEL_ID } from "./project-panel/project-panel-action";
  import { ProjectPanelMiddlewareService } from "./project-panel/project-panel-middleware-service";
  import { FavoritePanelMiddlewareService } from "./favorite-panel/favorite-panel-middleware-service";
- import { CollectionPanelState, collectionPanelReducer } from './collection-panel/collection-panel-reducer';
- import { DialogState, dialogReducer } from './dialog/dialog-reducer';
- import { CollectionsState, collectionsReducer } from './collections/collections-reducer';
+ import { collectionPanelReducer } from './collection-panel/collection-panel-reducer';
+ import { dialogReducer } from './dialog/dialog-reducer';
  import { ServiceRepository } from "~/services/services";
  import { treePickerReducer } from './tree-picker/tree-picker-reducer';
- import { TreePicker } from './tree-picker/tree-picker';
- import { TrashPanelMiddlewareService } from "~/store/trash-panel/trash-panel-middleware-service";
- import { TRASH_PANEL_ID } from "~/store/trash-panel/trash-panel-action";
+ import { resourcesReducer } from '~/store/resources/resources-reducer';
+ import { propertiesReducer } from './properties/properties-reducer';
+ import { RootState } from './store';
+ import { fileUploaderReducer } from './file-uploader/file-uploader-reducer';
  
  const composeEnhancers =
      (process.env.NODE_ENV === 'development' &&
          window && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) ||
      compose;
  
- export interface RootState {
-     auth: AuthState;
-     projects: ProjectState;
-     collections: CollectionsState;
-     router: RouterState;
-     dataExplorer: DataExplorerState;
-     sidePanel: SidePanelState;
-     collectionPanel: CollectionPanelState;
-     detailsPanel: DetailsPanelState;
-     contextMenu: ContextMenuState;
-     favorites: FavoritesState;
-     snackbar: SnackbarState;
-     collectionPanelFiles: CollectionPanelFilesState;
-     dialog: DialogState;
-     treePicker: TreePicker;
- }
+ export type RootState = ReturnType<ReturnType<typeof createRootReducer>>;
  
  export type RootStore = Store<RootState, Action> & { dispatch: Dispatch<any> };
  
  export function configureStore(history: History, services: ServiceRepository): RootStore {
-     const rootReducer = combineReducers({
-         auth: authReducer(services),
-         projects: projectsReducer,
-         collections: collectionsReducer,
-         router: routerReducer,
-         dataExplorer: dataExplorerReducer,
-         sidePanel: sidePanelReducer,
-         collectionPanel: collectionPanelReducer,
-         detailsPanel: detailsPanelReducer,
-         contextMenu: contextMenuReducer,
-         form: formReducer,
-         favorites: favoritesReducer,
-         snackbar: snackbarReducer,
-         collectionPanelFiles: collectionPanelFilesReducer,
-         dialog: dialogReducer,
-         treePicker: treePickerReducer,
-     });
+     const rootReducer = createRootReducer(services);
  
      const projectPanelMiddleware = dataExplorerMiddleware(
          new ProjectPanelMiddlewareService(services, PROJECT_PANEL_ID)
      const favoritePanelMiddleware = dataExplorerMiddleware(
          new FavoritePanelMiddlewareService(services, FAVORITE_PANEL_ID)
      );
 +    const trashPanelMiddleware = dataExplorerMiddleware(
 +        new TrashPanelMiddlewareService(services, TRASH_PANEL_ID)
 +    );
  
      const middlewares: Middleware[] = [
          routerMiddleware(history),
          thunkMiddleware.withExtraArgument(services),
          projectPanelMiddleware,
 -        favoritePanelMiddleware
 +        favoritePanelMiddleware,
 +        trashPanelMiddleware
      ];
      const enhancer = composeEnhancers(applyMiddleware(...middlewares));
      return createStore(rootReducer, enhancer);
  }
+ const createRootReducer = (services: ServiceRepository) => combineReducers({
+     auth: authReducer(services),
+     collectionPanel: collectionPanelReducer,
+     collectionPanelFiles: collectionPanelFilesReducer,
+     contextMenu: contextMenuReducer,
+     dataExplorer: dataExplorerReducer,
+     detailsPanel: detailsPanelReducer,
+     dialog: dialogReducer,
+     favorites: favoritesReducer,
+     form: formReducer,
+     properties: propertiesReducer,
+     resources: resourcesReducer,
+     router: routerReducer,
+     snackbar: snackbarReducer,
+     treePicker: treePickerReducer,
+     fileUploader: fileUploaderReducer,
+ });
index c8fb3cbc927d7c51f1f93fcea2b12ea407e1fd0d,b3fdc3fbab642561d823cddd003a70b9ab288149..f26003653382701cf8b700143bd30db830788c46
@@@ -6,17 -6,17 +6,19 @@@ import { ContextMenuActionSet } from ".
  import { ToggleFavoriteAction } from "../actions/favorite-action";
  import { toggleFavorite } from "~/store/favorites/favorites-actions";
  import { RenameIcon, ShareIcon, MoveToIcon, CopyIcon, DetailsIcon, ProvenanceGraphIcon, AdvancedIcon, RemoveIcon } from "~/components/icon/icon";
- import { openUpdater } from "~/store/collections/updater/collection-updater-action";
+ import { openCollectionUpdateDialog } from "~/store/collections/collection-update-actions";
  import { favoritePanelActions } from "~/store/favorite-panel/favorite-panel-action";
+ import { openMoveCollectionDialog } from '~/store/collections/collection-move-actions';
+ import { openCollectionCopyDialog } from "~/store/collections/collection-copy-actions";
 +import { ToggleTrashAction } from "~/views-components/context-menu/actions/trash-action";
 +import { toggleCollectionTrashed } from "~/store/collections/collection-trash-actions";
  
  export const collectionActionSet: ContextMenuActionSet = [[
      {
          icon: RenameIcon,
          name: "Edit collection",
          execute: (dispatch, resource) => {
-             dispatch<any>(openUpdater(resource));
+             dispatch<any>(openCollectionUpdateDialog(resource));
          }
      },
      {
@@@ -29,9 -29,7 +31,7 @@@
      {
          icon: MoveToIcon,
          name: "Move to",
-         execute: (dispatch, resource) => {
-             // add code
-         }
+         execute: (dispatch, resource) => dispatch<any>(openMoveCollectionDialog(resource))
      },
      {
          component: ToggleFavoriteAction,
              });
          }
      },
 +    {
 +        component: ToggleTrashAction,
 +        execute: (dispatch, resource) => {
 +            dispatch<any>(toggleCollectionTrashed(resource));
 +        }
 +    },
      {
          icon: CopyIcon,
          name: "Copy to project",
          execute: (dispatch, resource) => {
-             // add code
+             dispatch<any>(openCollectionCopyDialog(resource));
          }
      },
      {
index dbc9e23698b1d3814889d814bf98adb69b94b837,a299b9370ca93f39a437338b486ab639935b323e..a1df8385a4e1ad1a4a369412c5815d31a9cebb83
@@@ -4,19 -4,19 +4,20 @@@
  
  import { ContextMenuActionSet } from "../context-menu-action-set";
  import { ToggleFavoriteAction } from "../actions/favorite-action";
++import { ToggleTrashAction } from "~/views-components/context-menu/actions/trash-action";
  import { toggleFavorite } from "~/store/favorites/favorites-actions";
  import { RenameIcon, ShareIcon, MoveToIcon, CopyIcon, DetailsIcon, RemoveIcon } from "~/components/icon/icon";
- import { openUpdater } from "~/store/collections/updater/collection-updater-action";
+ import { openCollectionUpdateDialog } from "~/store/collections/collection-update-actions";
  import { favoritePanelActions } from "~/store/favorite-panel/favorite-panel-action";
- import { ToggleTrashAction } from "~/views-components/context-menu/actions/trash-action";
- import { toggleCollectionTrashed } from "~/store/collections/collection-trash-actions";
+ import { openMoveCollectionDialog } from '~/store/collections/collection-move-actions';
+ import { openCollectionCopyDialog } from '~/store/collections/collection-copy-actions';
  
  export const collectionResourceActionSet: ContextMenuActionSet = [[
      {
          icon: RenameIcon,
          name: "Edit collection",
          execute: (dispatch, resource) => {
-             dispatch<any>(openUpdater(resource));
+             dispatch<any>(openCollectionUpdateDialog(resource));
          }
      },
      {
@@@ -29,9 -29,7 +30,7 @@@
      {
          icon: MoveToIcon,
          name: "Move to",
-         execute: (dispatch, resource) => {
-             // add code
-         }
+         execute: (dispatch, resource) => dispatch<any>(openMoveCollectionDialog(resource))
      },
      {
          component: ToggleFavoriteAction,
              });
          }
      },
 +    {
 +        component: ToggleTrashAction,
 +        execute: (dispatch, resource) => {
 +            dispatch<any>(toggleCollectionTrashed(resource));
 +        }
 +    },
      {
          icon: CopyIcon,
          name: "Copy to project",
          execute: (dispatch, resource) => {
-             // add code
-         }
+             dispatch<any>(openCollectionCopyDialog(resource));
+         },
      },
      {
          icon: DetailsIcon,
index 8246d02c37d6cdf8338359537140f90050126256,abf1839286997117c0ecc56d2a5e87f917456e23..390e601f9dbd8bac5e74967f316db3211c35163f
@@@ -9,9 -9,14 +9,14 @@@ import { ResourceKind } from '~/models/
  import { ProjectIcon, CollectionIcon, ProcessIcon, DefaultIcon } from '~/components/icon/icon';
  import { formatDate, formatFileSize } from '~/common/formatters';
  import { resourceLabel } from '~/common/labels';
+ import { connect } from 'react-redux';
+ import { RootState } from '~/store/store';
+ import { getResource } from '../../store/resources/resources';
+ import { GroupContentsResource } from '~/services/groups-service/groups-service';
+ import { ProcessResource } from '~/models/process';
  
  
- export const renderName = (item: {name: string; uuid: string, kind: string}) =>
+ export const renderName = (item: { name: string; uuid: string, kind: string }) =>
      <Grid container alignItems="center" wrap="nowrap" spacing={16}>
          <Grid item>
              {renderIcon(item)}
          </Grid>
      </Grid>;
  
+ export const ResourceName = connect(
+     (state: RootState, props: { uuid: string }) => {
+         const resource = getResource(props.uuid)(state.resources) as GroupContentsResource | undefined;
+         return resource || { name: '', uuid: '', kind: '' };
+     })(renderName);
  
- export const renderIcon = (item: {kind: string}) => {
+ export const renderIcon = (item: { kind: string }) => {
      switch (item.kind) {
          case ResourceKind.PROJECT:
              return <ProjectIcon />;
      }
  };
  
 -export const renderDate = (date: string) => {
 +export const renderDate = (date?: string) => {
      return <Typography noWrap>{formatDate(date)}</Typography>;
  };
  
+ export const ResourceLastModifiedDate = connect(
+     (state: RootState, props: { uuid: string }) => {
+         const resource = getResource(props.uuid)(state.resources) as GroupContentsResource | undefined;
+         return { date: resource ? resource.modifiedAt : '' };
+     })((props: { date: string }) => renderDate(props.date));
  export const renderFileSize = (fileSize?: number) =>
      <Typography noWrap>
          {formatFileSize(fileSize)}
      </Typography>;
  
+ export const ResourceFileSize = connect(
+     (state: RootState, props: { uuid: string }) => {
+         const resource = getResource(props.uuid)(state.resources) as GroupContentsResource | undefined;
+         return {};
+     })((props: { fileSize?: number }) => renderFileSize(props.fileSize));
  export const renderOwner = (owner: string) =>
      <Typography noWrap color="primary" >
          {owner}
      </Typography>;
  
+ export const ResourceOwner = connect(
+     (state: RootState, props: { uuid: string }) => {
+         const resource = getResource(props.uuid)(state.resources) as GroupContentsResource | undefined;
+         return { owner: resource ? resource.ownerUuid : '' };
+     })((props: { owner: string }) => renderOwner(props.owner));
  export const renderType = (type: string) =>
      <Typography noWrap>
          {resourceLabel(type)}
      </Typography>;
  
- export const renderStatus = (item: {status?: string}) =>
+ export const ResourceType = connect(
+     (state: RootState, props: { uuid: string }) => {
+         const resource = getResource(props.uuid)(state.resources) as GroupContentsResource | undefined;
+         return { type: resource ? resource.kind : '' };
+     })((props: { type: string }) => renderType(props.type));
+ export const renderStatus = (item: { status?: string }) =>
      <Typography noWrap align="center" >
          {item.status || "-"}
      </Typography>;
+ export const ProcessStatus = connect(
+     (state: RootState, props: { uuid: string }) => {
+         const resource = getResource(props.uuid)(state.resources) as ProcessResource | undefined;
+         return { status: resource ? resource.state : '-' };
+     })((props: { status: string }) => renderType(props.status));
index 9e32700d034e8eeb5d87bb8c4db65006ae279730,348b548bdb4845eef161ce30210bcf5fde7833d0..8a0e2f8108332c5110218b1711d5d4ac36242767
@@@ -5,7 -5,7 +5,7 @@@
  import * as React from 'react';
  import {
      StyleRulesCallback, WithStyles, withStyles, Card,
-     CardHeader, IconButton, CardContent, Grid, Chip
+     CardHeader, IconButton, CardContent, Grid, Chip, Tooltip
  } from '@material-ui/core';
  import { connect, DispatchProp } from "react-redux";
  import { RouteComponentProps } from 'react-router';
@@@ -19,8 -19,12 +19,12 @@@ import * as CopyToClipboard from 'react
  import { TagResource } from '~/models/tag';
  import { CollectionTagForm } from './collection-tag-form';
  import { deleteCollectionTag } from '~/store/collection-panel/collection-panel-action';
+ import { snackbarActions } from '~/store/snackbar/snackbar-actions';
+ import { getResource } from '~/store/resources/resources';
+ import { contextMenuActions, openContextMenu } from '~/store/context-menu/context-menu-actions';
+ import { ContextMenuKind } from '~/views-components/context-menu/context-menu';
  
- type CssRules = 'card' | 'iconHeader' | 'tag' | 'copyIcon' | 'value';
+ type CssRules = 'card' | 'iconHeader' | 'tag' | 'copyIcon' | 'label' | 'value';
  
  const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
      card: {
          color: theme.palette.grey["500"],
          cursor: 'pointer'
      },
+     label: {
+         fontSize: '0.875rem'
+     },
      value: {
-         textTransform: 'none'
+         textTransform: 'none',
+         fontSize: '0.875rem'
      }
  });
  
@@@ -50,86 -58,103 +58,101 @@@ interface CollectionPanelDataProps 
      tags: TagResource[];
  }
  
- interface CollectionPanelActionProps {
-     onItemRouteChange: (collectionId: string) => void;
-     onContextMenu: (event: React.MouseEvent<HTMLElement>, item: CollectionResource) => void;
- }
- type CollectionPanelProps = CollectionPanelDataProps & CollectionPanelActionProps & DispatchProp
-                             & WithStyles<CssRules> & RouteComponentProps<{ id: string }>;
+ type CollectionPanelProps = CollectionPanelDataProps & DispatchProp
+     & WithStyles<CssRules> & RouteComponentProps<{ id: string }>;
  
  
  export const CollectionPanel = withStyles(styles)(
-     connect((state: RootState) => ({
-         item: state.collectionPanel.item,
-         tags: state.collectionPanel.tags
-     }))(
+     connect((state: RootState, props: RouteComponentProps<{ id: string }>) => {
+         const collection = getResource(props.match.params.id)(state.resources);
+         return {
+             item: collection,
+             tags: state.collectionPanel.tags
+         };
+     })(
          class extends React.Component<CollectionPanelProps> {
 -
              render() {
-                 const { classes, item, tags, onContextMenu } = this.props;
+                 const { classes, item, tags } = this.props;
                  return <div>
-                         <Card className={classes.card}>
-                             <CardHeader
-                                 avatar={ <CollectionIcon className={classes.iconHeader} /> }
-                                 action={
-                                     <IconButton
-                                         aria-label="More options"
-                                         onClick={event => onContextMenu(event, item)}>
-                                         <MoreOptionsIcon />
-                                     </IconButton>
-                                 }
-                                 title={item && item.name }
-                                 subheader={item && item.description} />
-                             <CardContent>
-                                 <Grid container direction="column">
-                                     <Grid item xs={6}>
-                                     <DetailsAttribute classValue={classes.value}
-                                             label='Collection UUID'
-                                             value={item && item.uuid}>
-                                         <CopyToClipboard text={item && item.uuid}>
-                                             <CopyIcon className={classes.copyIcon} />
-                                         </CopyToClipboard>
+                     <Card className={classes.card}>
+                         <CardHeader
+                             avatar={<CollectionIcon className={classes.iconHeader} />}
+                             action={
+                                 <IconButton
+                                     aria-label="More options"
+                                     onClick={this.handleContextMenu}>
+                                     <MoreOptionsIcon />
+                                 </IconButton>
+                             }
+                             title={item && item.name}
+                             subheader={item && item.description} />
+                         <CardContent>
+                             <Grid container direction="column">
+                                 <Grid item xs={6}>
+                                     <DetailsAttribute classLabel={classes.label} classValue={classes.value}
+                                         label='Collection UUID'
+                                         value={item && item.uuid}>
+                                         <Tooltip title="Copy uuid">
+                                             <CopyToClipboard text={item && item.uuid} onCopy={() => this.onCopy()}>
+                                                 <CopyIcon className={classes.copyIcon} />
+                                             </CopyToClipboard>
+                                         </Tooltip>
                                      </DetailsAttribute>
-                                     <DetailsAttribute label='Number of files' value='14' />
-                                     <DetailsAttribute label='Content size' value='54 MB' />
-                                     <DetailsAttribute classValue={classes.value} label='Owner' value={item && item.ownerUuid} />
-                                     </Grid>
+                                     <DetailsAttribute classLabel={classes.label} classValue={classes.value}
+                                         label='Number of files' value='14' />
+                                     <DetailsAttribute classLabel={classes.label} classValue={classes.value}
+                                         label='Content size' value='54 MB' />
+                                     <DetailsAttribute classLabel={classes.label} classValue={classes.value}
+                                         label='Owner' value={item && item.ownerUuid} />
                                  </Grid>
-                             </CardContent>
-                         </Card>
+                             </Grid>
+                         </CardContent>
+                     </Card>
  
-                         <Card className={classes.card}>
-                             <CardHeader title="Properties" />
-                             <CardContent>
-                                 <Grid container direction="column">
-                                     <Grid item xs={12}><CollectionTagForm /></Grid>
-                                     <Grid item xs={12}>
-                                         {
-                                             tags.map(tag => {
-                                                 return <Chip key={tag.etag} className={classes.tag}
-                                                     onDelete={this.handleDelete(tag.uuid)}
-                                                     label={renderTagLabel(tag)}  />;
-                                             })
-                                         }
-                                     </Grid>
+                     <Card className={classes.card}>
+                         <CardHeader title="Properties" />
+                         <CardContent>
+                             <Grid container direction="column">
+                                 <Grid item xs={12}><CollectionTagForm /></Grid>
+                                 <Grid item xs={12}>
+                                     {
+                                         tags.map(tag => {
+                                             return <Chip key={tag.etag} className={classes.tag}
+                                                 onDelete={this.handleDelete(tag.uuid)}
+                                                 label={renderTagLabel(tag)} />;
+                                         })
+                                     }
                                  </Grid>
-                             </CardContent>
-                         </Card>
-                         <div className={classes.card}>
-                             <CollectionPanelFiles/>
-                         </div>
-                     </div>;
+                             </Grid>
+                         </CardContent>
+                     </Card>
+                     <div className={classes.card}>
+                         <CollectionPanelFiles />
+                     </div>
+                 </div>;
+             }
+             handleContextMenu = (event: React.MouseEvent<any>) => {
+                 const { uuid, name, description } = this.props.item;
+                 const resource = {
+                     uuid,
+                     name,
+                     description,
+                     kind: ContextMenuKind.COLLECTION
+                 };
+                 this.props.dispatch<any>(openContextMenu(event, resource));
              }
  
              handleDelete = (uuid: string) => () => {
                  this.props.dispatch<any>(deleteCollectionTag(uuid));
              }
  
-             componentWillReceiveProps({ match, item, onItemRouteChange }: CollectionPanelProps) {
-                 if (!item || match.params.id !== item.uuid) {
-                     onItemRouteChange(match.params.id);
-                 }
+             onCopy = () => {
+                 this.props.dispatch(snackbarActions.OPEN_SNACKBAR({
+                     message: "Uuid has been copied",
+                     hideDuration: 2000
+                 }));
              }
 -
          }
      )
  );
index 49f1f4ab3a593bc96fb2e10bdddeca294dc49c03,9fbae5ced889902d4771af6dbc7bb821e6f15360..62b037e3a56da989baa16aa1c91679c2d47a7a77
@@@ -3,22 -3,25 +3,25 @@@
  // SPDX-License-Identifier: AGPL-3.0
  
  import * as React from 'react';
- import { FavoritePanelItem } from './favorite-panel-item';
  import { StyleRulesCallback, WithStyles, withStyles } from '@material-ui/core';
  import { DataExplorer } from "~/views-components/data-explorer/data-explorer";
  import { DispatchProp, connect } from 'react-redux';
  import { DataColumns } from '~/components/data-table/data-table';
  import { RouteComponentProps } from 'react-router';
- import { RootState } from '~/store/store';
  import { DataTableFilterItem } from '~/components/data-table-filters/data-table-filters';
 -import { ContainerRequestState } from '~/models/container-request';
 +import { ProcessState } from '~/models/process';
  import { SortDirection } from '~/components/data-table/data-column';
  import { ResourceKind } from '~/models/resource';
  import { resourceLabel } from '~/common/labels';
  import { ArvadosTheme } from '~/common/custom-theme';
- import { renderName, renderStatus, renderType, renderOwner, renderFileSize, renderDate } from '~/views-components/data-explorer/renderers';
  import { FAVORITE_PANEL_ID } from "~/store/favorite-panel/favorite-panel-action";
+ import { ResourceFileSize, ResourceLastModifiedDate, ProcessStatus, ResourceType, ResourceOwner, ResourceName } from '~/views-components/data-explorer/renderers';
  import { FavoriteIcon } from '~/components/icon/icon';
+ import { Dispatch } from 'redux';
+ import { contextMenuActions, openContextMenu, resourceKindToContextMenuKind } from '~/store/context-menu/context-menu-actions';
+ import { ContextMenuKind } from '~/views-components/context-menu/context-menu';
+ import { loadDetailsPanel } from '../../store/details-panel/details-panel-action';
+ import { navigateTo } from '~/store/navigation/navigation-action';
  
  type CssRules = "toolbar" | "button";
  
@@@ -42,17 -45,17 +45,17 @@@ export enum FavoritePanelColumnNames 
  }
  
  export interface FavoritePanelFilter extends DataTableFilterItem {
 -    type: ResourceKind | ContainerRequestState;
 +    type: ResourceKind | ProcessState;
  }
  
- export const columns: DataColumns<FavoritePanelItem, FavoritePanelFilter> = [
+ export const favoritePanelColumns: DataColumns<string, FavoritePanelFilter> = [
      {
          name: FavoritePanelColumnNames.NAME,
          selected: true,
          configurable: true,
          sortDirection: SortDirection.ASC,
          filters: [],
-         render: renderName,
+         render: uuid => <ResourceName uuid={uuid} />,
          width: "450px"
      },
      {
          sortDirection: SortDirection.NONE,
          filters: [
              {
 -                name: ContainerRequestState.COMMITTED,
 +                name: ProcessState.COMMITTED,
                  selected: true,
 -                type: ContainerRequestState.COMMITTED
 +                type: ProcessState.COMMITTED
              },
              {
 -                name: ContainerRequestState.FINAL,
 +                name: ProcessState.FINAL,
                  selected: true,
 -                type: ContainerRequestState.FINAL
 +                type: ProcessState.FINAL
              },
              {
 -                name: ContainerRequestState.UNCOMMITTED,
 +                name: ProcessState.UNCOMMITTED,
                  selected: true,
 -                type: ContainerRequestState.UNCOMMITTED
 +                type: ProcessState.UNCOMMITTED
              }
          ],
-         render: renderStatus,
+         render: uuid => <ProcessStatus uuid={uuid} />,
          width: "75px"
      },
      {
                  type: ResourceKind.PROJECT
              }
          ],
-         render: item => renderType(item.kind),
+         render: uuid => <ResourceType uuid={uuid} />,
          width: "125px"
      },
      {
          configurable: true,
          sortDirection: SortDirection.NONE,
          filters: [],
-         render: item => renderOwner(item.owner),
+         render: uuid => <ResourceOwner uuid={uuid} />,
          width: "200px"
      },
      {
          configurable: true,
          sortDirection: SortDirection.NONE,
          filters: [],
-         render: item => renderFileSize(item.fileSize),
+         render: uuid => <ResourceFileSize uuid={uuid} />,
          width: "50px"
      },
      {
          configurable: true,
          sortDirection: SortDirection.NONE,
          filters: [],
-         render: item => renderDate(item.lastModified),
+         render: uuid => <ResourceLastModifiedDate uuid={uuid} />,
          width: "150px"
      }
  ];
@@@ -139,36 -142,42 +142,42 @@@ interface FavoritePanelDataProps 
  }
  
  interface FavoritePanelActionProps {
-     onItemClick: (item: FavoritePanelItem) => void;
-     onContextMenu: (event: React.MouseEvent<HTMLElement>, item: FavoritePanelItem) => void;
+     onItemClick: (item: string) => void;
+     onContextMenu: (event: React.MouseEvent<HTMLElement>, item: string) => void;
      onDialogOpen: (ownerUuid: string) => void;
-     onItemDoubleClick: (item: FavoritePanelItem) => void;
-     onItemRouteChange: (itemId: string) => void;
+     onItemDoubleClick: (item: string) => void;
  }
  
+ const mapDispatchToProps = (dispatch: Dispatch): FavoritePanelActionProps => ({
+     onContextMenu: (event, resourceUuid) => {
+         const kind = resourceKindToContextMenuKind(resourceUuid);
+         if (kind) {
+             dispatch<any>(openContextMenu(event, { name: '', uuid: resourceUuid, kind }));
+         }
+     },
+     onDialogOpen: (ownerUuid: string) => { return; },
+     onItemClick: (resourceUuid: string) => {
+         dispatch<any>(loadDetailsPanel(resourceUuid));
+     },
+     onItemDoubleClick: uuid => {
+         dispatch<any>(navigateTo(uuid));
+     }
+ });
  type FavoritePanelProps = FavoritePanelDataProps & FavoritePanelActionProps & DispatchProp
-                         & WithStyles<CssRules> & RouteComponentProps<{ id: string }>;
+     & WithStyles<CssRules> & RouteComponentProps<{ id: string }>;
  
  export const FavoritePanel = withStyles(styles)(
-     connect((state: RootState) => ({ currentItemId: state.projects.currentItemId }))(
+     connect(undefined, mapDispatchToProps)(
          class extends React.Component<FavoritePanelProps> {
              render() {
                  return <DataExplorer
                      id={FAVORITE_PANEL_ID}
-                     columns={columns}
                      onRowClick={this.props.onItemClick}
                      onRowDoubleClick={this.props.onItemDoubleClick}
                      onContextMenu={this.props.onContextMenu}
-                     extractKey={(item: FavoritePanelItem) => item.uuid}
                      defaultIcon={FavoriteIcon}
-                     defaultMessages={['Your favorites list is empty.']}/>
-                 ;
-             }
-             componentWillReceiveProps({ match, currentItemId, onItemRouteChange }: FavoritePanelProps) {
-                 if (match.params.id !== currentItemId) {
-                     onItemRouteChange(match.params.id);
-                 }
+                     defaultMessages={['Your favorites list is empty.']} />;
              }
          }
      )
index f63584b7866d40b9ce766f9eb01bb2530e55ae98,06946430e71909d711f1bdc6c32b1ac4c0f80021..37a6d202214a6fb1b80114ad4805fac08aace93f
@@@ -3,7 -3,6 +3,6 @@@
  // SPDX-License-Identifier: AGPL-3.0
  
  import * as React from 'react';
- import { ProjectPanelItem } from './project-panel-item';
  import { Button, StyleRulesCallback, WithStyles, withStyles } from '@material-ui/core';
  import { DataExplorer } from "~/views-components/data-explorer/data-explorer";
  import { DispatchProp, connect } from 'react-redux';
@@@ -11,14 -10,25 +10,25 @@@ import { DataColumns } from '~/componen
  import { RouteComponentProps } from 'react-router';
  import { RootState } from '~/store/store';
  import { DataTableFilterItem } from '~/components/data-table-filters/data-table-filters';
 -import { ContainerRequestState } from '~/models/container-request';
 +import { ProcessState } from '~/models/process';
  import { SortDirection } from '~/components/data-table/data-column';
  import { ResourceKind } from '~/models/resource';
  import { resourceLabel } from '~/common/labels';
  import { ArvadosTheme } from '~/common/custom-theme';
- import { renderName, renderStatus, renderType, renderOwner, renderFileSize, renderDate } from '~/views-components/data-explorer/renderers';
- import { restoreBranch } from '~/store/navigation/navigation-action';
+ import { ResourceFileSize, ResourceLastModifiedDate, ProcessStatus, ResourceType, ResourceOwner } from '~/views-components/data-explorer/renderers';
  import { ProjectIcon } from '~/components/icon/icon';
+ import { ResourceName } from '~/views-components/data-explorer/renderers';
+ import { ResourcesState, getResource } from '~/store/resources/resources';
+ import { loadDetailsPanel } from '~/store/details-panel/details-panel-action';
+ import { ContextMenuKind } from '~/views-components/context-menu/context-menu';
+ import { contextMenuActions, resourceKindToContextMenuKind, openContextMenu } from '~/store/context-menu/context-menu-actions';
+ import { CollectionResource } from '~/models/collection';
+ import { ProjectResource } from '~/models/project';
+ import { navigateTo } from '~/store/navigation/navigation-action';
+ import { getProperty } from '~/store/properties/properties';
+ import { PROJECT_PANEL_CURRENT_UUID } from '~/store/project-panel/project-panel-action';
+ import { openCollectionCreateDialog } from '../../store/collections/collection-create-actions';
+ import { openProjectCreateDialog } from '~/store/projects/project-create-actions';
  
  type CssRules = 'root' | "toolbar" | "button";
  
@@@ -47,17 -57,17 +57,17 @@@ export enum ProjectPanelColumnNames 
  }
  
  export interface ProjectPanelFilter extends DataTableFilterItem {
 -    type: ResourceKind | ContainerRequestState;
 +    type: ResourceKind | ProcessState;
  }
  
- export const columns: DataColumns<ProjectPanelItem, ProjectPanelFilter> = [
+ export const projectPanelColumns: DataColumns<string, ProjectPanelFilter> = [
      {
          name: ProjectPanelColumnNames.NAME,
          selected: true,
          configurable: true,
          sortDirection: SortDirection.ASC,
          filters: [],
-         render: renderName,
+         render: uuid => <ResourceName uuid={uuid} />,
          width: "450px"
      },
      {
          sortDirection: SortDirection.NONE,
          filters: [
              {
 -                name: ContainerRequestState.COMMITTED,
 +                name: ProcessState.COMMITTED,
                  selected: true,
 -                type: ContainerRequestState.COMMITTED
 +                type: ProcessState.COMMITTED
              },
              {
 -                name: ContainerRequestState.FINAL,
 +                name: ProcessState.FINAL,
                  selected: true,
 -                type: ContainerRequestState.FINAL
 +                type: ProcessState.FINAL
              },
              {
 -                name: ContainerRequestState.UNCOMMITTED,
 +                name: ProcessState.UNCOMMITTED,
                  selected: true,
 -                type: ContainerRequestState.UNCOMMITTED
 +                type: ProcessState.UNCOMMITTED
              }
          ],
-         render: renderStatus,
+         render: uuid => <ProcessStatus uuid={uuid} />,
          width: "75px"
      },
      {
                  type: ResourceKind.PROJECT
              }
          ],
-         render: item => renderType(item.kind),
+         render: uuid => <ResourceType uuid={uuid} />,
          width: "125px"
      },
      {
          configurable: true,
          sortDirection: SortDirection.NONE,
          filters: [],
-         render: item => renderOwner(item.owner),
+         render: uuid => <ResourceOwner uuid={uuid} />,
          width: "200px"
      },
      {
          configurable: true,
          sortDirection: SortDirection.NONE,
          filters: [],
-         render: item => renderFileSize(item.fileSize),
+         render: uuid => <ResourceFileSize uuid={uuid} />,
          width: "50px"
      },
      {
          configurable: true,
          sortDirection: SortDirection.NONE,
          filters: [],
-         render: item => renderDate(item.lastModified),
+         render: uuid => <ResourceLastModifiedDate uuid={uuid} />,
          width: "150px"
      }
  ];
@@@ -143,22 -153,17 +153,17 @@@ export const PROJECT_PANEL_ID = "projec
  
  interface ProjectPanelDataProps {
      currentItemId: string;
+     resources: ResourcesState;
  }
  
- interface ProjectPanelActionProps {
-     onItemClick: (item: ProjectPanelItem) => void;
-     onContextMenu: (event: React.MouseEvent<HTMLElement>, item: ProjectPanelItem) => void;
-     onProjectCreationDialogOpen: (ownerUuid: string) => void;
-     onCollectionCreationDialogOpen: (ownerUuid: string) => void;
-     onItemDoubleClick: (item: ProjectPanelItem) => void;
-     onItemRouteChange: (itemId: string) => void;
- }
- type ProjectPanelProps = ProjectPanelDataProps & ProjectPanelActionProps & DispatchProp
+ type ProjectPanelProps = ProjectPanelDataProps & DispatchProp
      & WithStyles<CssRules> & RouteComponentProps<{ id: string }>;
  
  export const ProjectPanel = withStyles(styles)(
-     connect((state: RootState) => ({ currentItemId: state.projects.currentItemId }))(
+     connect((state: RootState) => ({
+         currentItemId: getProperty(PROJECT_PANEL_CURRENT_UUID)(state.properties),
+         resources: state.resources
+     }))(
          class extends React.Component<ProjectPanelProps> {
              render() {
                  const { classes } = this.props;
                      </div>
                      <DataExplorer
                          id={PROJECT_PANEL_ID}
-                         columns={columns}
-                         onRowClick={this.props.onItemClick}
-                         onRowDoubleClick={this.props.onItemDoubleClick}
-                         onContextMenu={this.props.onContextMenu}
-                         extractKey={(item: ProjectPanelItem) => item.uuid}
+                         onRowClick={this.handleRowClick}
+                         onRowDoubleClick={this.handleRowDoubleClick}
+                         onContextMenu={this.handleContextMenu}
                          defaultIcon={ProjectIcon}
                          defaultMessages={['Your project is empty.', 'Please create a project or create a collection and upload a data.']} />
                  </div>;
              }
  
              handleNewProjectClick = () => {
-                 this.props.onProjectCreationDialogOpen(this.props.currentItemId);
+                 this.props.dispatch<any>(openProjectCreateDialog(this.props.currentItemId));
              }
  
              handleNewCollectionClick = () => {
-                 this.props.onCollectionCreationDialogOpen(this.props.currentItemId);
+                 this.props.dispatch<any>(openCollectionCreateDialog(this.props.currentItemId));
              }
  
-             componentWillReceiveProps({ match, currentItemId, onItemRouteChange }: ProjectPanelProps) {
-                 if (match.params.id !== currentItemId) {
-                     onItemRouteChange(match.params.id);
+             handleContextMenu = (event: React.MouseEvent<HTMLElement>, resourceUuid: string) => {
+                 const kind = resourceKindToContextMenuKind(resourceUuid);
+                 if (kind) {
+                     this.props.dispatch<any>(openContextMenu(event, { name: '', uuid: resourceUuid, kind }));
                  }
              }
  
-             componentDidMount() {
-                 if (this.props.match.params.id && this.props.currentItemId === '') {
-                     this.props.dispatch<any>(restoreBranch(this.props.match.params.id));
-                 }
+             handleRowDoubleClick = (uuid: string) => {
+                 this.props.dispatch<any>(navigateTo(uuid));
              }
+             handleRowClick = (uuid: string) => {
+                 this.props.dispatch(loadDetailsPanel(uuid));
+             }
          }
      )
  );
index 8028f2c30ffc10cd8c883e2676aedd5a9175531f,ef5fe215290e4e33dfac8988840d0cb4740ed83d..ea3a278bf9090cbe73b29a2a0b64f4b357d6e399
@@@ -4,58 -4,44 +4,47 @@@
  
  import * as React from 'react';
  import { StyleRulesCallback, WithStyles, withStyles } from '@material-ui/core/styles';
- import Drawer from '@material-ui/core/Drawer';
  import { connect, DispatchProp } from "react-redux";
- import { Route, RouteComponentProps, Switch, Redirect } from "react-router";
+ import { Route, Switch } from "react-router";
  import { login, logout } from "~/store/auth/auth-action";
  import { User } from "~/models/user";
  import { RootState } from "~/store/store";
  import { MainAppBar, MainAppBarActionProps, MainAppBarMenuItem } from '~/views-components/main-app-bar/main-app-bar';
- import { Breadcrumb } from '~/components/breadcrumbs/breadcrumbs';
  import { push } from 'react-router-redux';
- import { reset } from 'redux-form';
- import { ProjectTree } from '~/views-components/project-tree/project-tree';
- import { TreeItem } from "~/components/tree/tree";
- import { getTreePath } from '~/store/project/project-reducer';
- import { sidePanelActions } from '~/store/side-panel/side-panel-action';
- import { SidePanel, SidePanelItem } from '~/components/side-panel/side-panel';
- import { ItemMode, setProjectItem } from "~/store/navigation/navigation-action";
- import { projectActions } from "~/store/project/project-action";
- import { collectionCreateActions } from '~/store/collections/creator/collection-creator-action';
  import { ProjectPanel } from "~/views/project-panel/project-panel";
  import { DetailsPanel } from '~/views-components/details-panel/details-panel';
  import { ArvadosTheme } from '~/common/custom-theme';
- import { CreateProjectDialog } from "~/views-components/create-project-dialog/create-project-dialog";
- import { detailsPanelActions, loadDetails } from "~/store/details-panel/details-panel-action";
- import { contextMenuActions } from "~/store/context-menu/context-menu-actions";
- import { ProjectResource } from '~/models/project';
- import { ResourceKind } from '~/models/resource';
- import { ContextMenu, ContextMenuKind } from "~/views-components/context-menu/context-menu";
+ import { detailsPanelActions } from "~/store/details-panel/details-panel-action";
+ import { ContextMenu } from "~/views-components/context-menu/context-menu";
  import { FavoritePanel } from "../favorite-panel/favorite-panel";
  import { CurrentTokenDialog } from '~/views-components/current-token-dialog/current-token-dialog';
  import { Snackbar } from '~/views-components/snackbar/snackbar';
- import { favoritePanelActions } from '~/store/favorite-panel/favorite-panel-action';
- import { CreateCollectionDialog } from '~/views-components/create-collection-dialog/create-collection-dialog';
  import { CollectionPanel } from '../collection-panel/collection-panel';
- import { loadCollection, loadCollectionTags } from '~/store/collection-panel/collection-panel-action';
- import { getCollectionUrl } from '~/models/collection';
- import { UpdateCollectionDialog } from '~/views-components/update-collection-dialog/update-collection-dialog.';
- import { UpdateProjectDialog } from '~/views-components/update-project-dialog/update-project-dialog';
  import { AuthService } from "~/services/auth-service/auth-service";
  import { RenameFileDialog } from '~/views-components/rename-file-dialog/rename-file-dialog';
  import { FileRemoveDialog } from '~/views-components/file-remove-dialog/file-remove-dialog';
  import { MultipleFilesRemoveDialog } from '~/views-components/file-remove-dialog/multiple-files-remove-dialog';
- import { DialogCollectionCreateWithSelectedFile } from '~/views-components/create-collection-dialog-with-selected/create-collection-dialog-with-selected';
- import { COLLECTION_CREATE_DIALOG } from '~/views-components/dialog-create/dialog-collection-create';
- import { PROJECT_CREATE_DIALOG } from '~/views-components/dialog-create/dialog-project-create';
+ import { Routes } from '~/routes/routes';
+ import { SidePanel } from '~/views-components/side-panel/side-panel';
+ import { ProcessPanel } from '~/views/process-panel/process-panel';
+ import { Breadcrumbs } from '~/views-components/breadcrumbs/breadcrumbs';
+ 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 { UpdateCollectionDialog } from '~/views-components/dialog-forms/update-collection-dialog';
+ import { UpdateProjectDialog } from '~/views-components/dialog-forms/update-project-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 { trashPanelActions } from "~/store/trash-panel/trash-panel-action";
 +
- const DRAWER_WITDH = 240;
  const APP_BAR_HEIGHT = 100;
  
- type CssRules = 'root' | 'appBar' | 'drawerPaper' | 'content' | 'contentWrapper' | 'toolbar';
+ type CssRules = 'root' | 'appBar' | 'content' | 'contentWrapper';
  
  const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
      root: {
          position: "absolute",
          width: "100%"
      },
-     drawerPaper: {
-         position: 'relative',
-         width: DRAWER_WITDH,
-         display: 'flex',
-         flexDirection: 'column',
-     },
      contentWrapper: {
          backgroundColor: theme.palette.background.default,
          display: "flex",
          flexGrow: 1,
          position: 'relative'
      },
-     toolbar: theme.mixins.toolbar
  });
  
  interface WorkbenchDataProps {
-     projects: Array<TreeItem<ProjectResource>>;
-     currentProjectId: string;
      user?: User;
      currentToken?: string;
-     sidePanelItems: SidePanelItem[];
  }
  
  interface WorkbenchGeneralProps {
@@@ -112,10 -88,6 +91,6 @@@ interface WorkbenchActionProps 
  
  type WorkbenchProps = WorkbenchDataProps & WorkbenchGeneralProps & WorkbenchActionProps & DispatchProp<any> & WithStyles<CssRules>;
  
- interface NavBreadcrumb extends Breadcrumb {
-     itemId: string;
- }
  interface NavMenuItem extends MainAppBarMenuItem {
      action: () => void;
  }
@@@ -134,16 -106,12 +109,12 @@@ interface WorkbenchState 
  export const Workbench = withStyles(styles)(
      connect<WorkbenchDataProps>(
          (state: RootState) => ({
-             projects: state.projects.items,
-             currentProjectId: state.projects.currentItemId,
              user: state.auth.user,
              currentToken: state.auth.apiToken,
-             sidePanelItems: state.sidePanel
          })
      )(
          class extends React.Component<WorkbenchProps, WorkbenchState> {
              state = {
-                 isCreationDialogOpen: false,
                  isCurrentTokenDialogOpen: false,
                  anchorEl: null,
                  searchText: "",
              };
  
              render() {
-                 const path = getTreePath(this.props.projects, this.props.currentProjectId);
-                 const breadcrumbs = path.map(item => ({
-                     label: item.data.name,
-                     itemId: item.data.uuid,
-                     status: item.status
-                 }));
                  const { classes, user } = this.props;
                  return (
                      <div className={classes.root}>
                          <div className={classes.appBar}>
                              <MainAppBar
-                                 breadcrumbs={breadcrumbs}
+                                 breadcrumbs={Breadcrumbs}
                                  searchText={this.state.searchText}
                                  user={this.props.user}
                                  menuItems={this.state.menuItems}
                                  buildInfo={this.props.buildInfo}
                                  {...this.mainAppBarActions} />
                          </div>
-                         {user &&
-                             <Drawer
-                                 variant="permanent"
-                                 classes={{
-                                     paper: classes.drawerPaper,
-                                 }}>
-                                 <div className={classes.toolbar} />
-                                 <SidePanel
-                                     toggleOpen={this.toggleSidePanelOpen}
-                                     toggleActive={this.toggleSidePanelActive}
-                                     sidePanelItems={this.props.sidePanelItems}
-                                     onContextMenu={(event) => this.openContextMenu(event, {
-                                         uuid: this.props.authService.getUuid() || "",
-                                         name: "",
-                                         kind: ContextMenuKind.ROOT_PROJECT
-                                     })}>
-                                     <ProjectTree
-                                         projects={this.props.projects}
-                                         toggleOpen={itemId => this.props.dispatch(setProjectItem(itemId, ItemMode.OPEN))}
-                                         onContextMenu={(event, item) => this.openContextMenu(event, {
-                                             uuid: item.data.uuid,
-                                             ownerUuid: item.data.ownerUuid || this.props.authService.getUuid(),
-                                             isTrashed: item.data.isTrashed,
-                                             name: item.data.name,
-                                             kind: ContextMenuKind.PROJECT
-                                         })}
-                                         toggleActive={itemId => {
-                                             this.props.dispatch(setProjectItem(itemId, ItemMode.ACTIVE));
-                                             this.props.dispatch(loadDetails(itemId, ResourceKind.PROJECT));
-                                         }} />
-                                 </SidePanel>
-                             </Drawer>}
+                         {user && <SidePanel />}
                          <main className={classes.contentWrapper}>
                              <div className={classes.content}>
                                  <Switch>
-                                     <Route path='/' exact render={() => <Redirect to={`/projects/${this.props.authService.getUuid()}`}  />} />
-                                     <Route path="/projects/:id" render={this.renderProjectPanel} />
-                                     <Route path="/favorites" render={this.renderFavoritePanel} />
+                                     <Route path={Routes.PROJECTS} component={ProjectPanel} />
+                                     <Route path={Routes.COLLECTIONS} component={CollectionPanel} />
+                                     <Route path={Routes.FAVORITES} component={FavoritePanel} />
+                                     <Route path={Routes.PROCESSES} component={ProcessPanel} />
 +                                    <Route path="/trash" render={this.renderTrashPanel} />
-                                     <Route path="/collections/:id" render={this.renderCollectionPanel} />
                                  </Switch>
                              </div>
                              {user && <DetailsPanel />}
                          <CreateProjectDialog />
                          <CreateCollectionDialog />
                          <RenameFileDialog />
-                         <DialogCollectionCreateWithSelectedFile />
+                         <PartialCopyCollectionDialog />
+                         <FileRemoveDialog />
+                         <CopyCollectionDialog />
                          <FileRemoveDialog />
                          <MultipleFilesRemoveDialog />
                          <UpdateCollectionDialog />
+                         <FilesUploadCollectionDialog />
                          <UpdateProjectDialog />
+                         <MoveCollectionDialog />
+                         <MoveProjectDialog />
                          <CurrentTokenDialog
                              currentToken={this.props.currentToken}
                              open={this.state.isCurrentTokenDialogOpen}
                  );
              }
  
-             renderCollectionPanel = (props: RouteComponentProps<{ id: string }>) => <CollectionPanel
-                 onItemRouteChange={(collectionId) => {
-                     this.props.dispatch<any>(loadCollection(collectionId));
-                     this.props.dispatch<any>(loadCollectionTags(collectionId));
-                 }}
-                 onContextMenu={(event, item) => {
-                     this.openContextMenu(event, {
-                         uuid: item.uuid,
-                         name: item.name,
-                         description: item.description,
-                         isTrashed: item.isTrashed,
-                         kind: ContextMenuKind.COLLECTION
-                     });
-                 }}
-                 {...props} />
-             renderProjectPanel = (props: RouteComponentProps<{ id: string }>) => <ProjectPanel
-                 onItemRouteChange={itemId => this.props.dispatch(setProjectItem(itemId, ItemMode.ACTIVE))}
-                 onContextMenu={(event, item) => {
-                     let kind: ContextMenuKind;
-                     if (item.kind === ResourceKind.PROJECT) {
-                         kind = ContextMenuKind.PROJECT;
-                     } else if (item.kind === ResourceKind.COLLECTION) {
-                         kind = ContextMenuKind.COLLECTION_RESOURCE;
-                     } else {
-                         kind = ContextMenuKind.RESOURCE;
-                     }
-                     this.openContextMenu(event, {
-                         uuid: item.uuid,
-                         name: item.name,
-                         description: item.description,
-                         isTrashed: item.isTrashed,
-                         ownerUuid: item.owner || this.props.authService.getUuid(),
-                         kind
-                     });
-                 }}
-                 onProjectCreationDialogOpen={this.handleProjectCreationDialogOpen}
-                 onCollectionCreationDialogOpen={this.handleCollectionCreationDialogOpen}
-                 onItemClick={item => {
-                     this.props.dispatch(loadDetails(item.uuid, item.kind as ResourceKind));
-                 }}
-                 onItemDoubleClick={item => {
-                     switch (item.kind) {
-                         case ResourceKind.COLLECTION:
-                             this.props.dispatch(loadCollection(item.uuid));
-                             this.props.dispatch(push(getCollectionUrl(item.uuid)));
-                         default:
-                             this.props.dispatch(setProjectItem(item.uuid, ItemMode.ACTIVE));
-                             this.props.dispatch(loadDetails(item.uuid, item.kind as ResourceKind));
-                     }
-                 }}
-                 {...props} />
-             renderFavoritePanel = (props: RouteComponentProps<{ id: string }>) => <FavoritePanel
-                 onItemRouteChange={() => this.props.dispatch(favoritePanelActions.REQUEST_ITEMS())}
-                 onContextMenu={(event, item) => {
-                     const kind = item.kind === ResourceKind.PROJECT ? ContextMenuKind.PROJECT : ContextMenuKind.RESOURCE;
-                     this.openContextMenu(event, {
-                         uuid: item.uuid,
-                         name: item.name,
-                         isTrashed: item.isTrashed,
-                         kind,
-                     });
-                 }}
-                 onDialogOpen={this.handleProjectCreationDialogOpen}
-                 onItemClick={item => {
-                     this.props.dispatch(loadDetails(item.uuid, item.kind as ResourceKind));
-                 }}
-                 onItemDoubleClick={item => {
-                     switch (item.kind) {
-                         case ResourceKind.COLLECTION:
-                             this.props.dispatch(loadCollection(item.uuid));
-                             this.props.dispatch(push(getCollectionUrl(item.uuid)));
-                         default:
-                             this.props.dispatch(loadDetails(item.uuid, ResourceKind.PROJECT));
-                             this.props.dispatch(setProjectItem(item.uuid, ItemMode.ACTIVE));
-                     }
-                 }}
-                 {...props} />
-             renderTrashPanel = (props: RouteComponentProps<{ id: string }>) => <TrashPanel
-                 onItemRouteChange={() => this.props.dispatch(trashPanelActions.REQUEST_ITEMS())}
-                 onContextMenu={(event, item) => {
-                     const kind = item.kind === ResourceKind.PROJECT ? ContextMenuKind.PROJECT : ContextMenuKind.COLLECTION;
-                     this.openContextMenu(event, {
-                         uuid: item.uuid,
-                         name: item.name,
-                         isTrashed: item.isTrashed,
-                         ownerUuid: item.owner,
-                         kind,
-                     });
-                 }}
-                 onDialogOpen={this.handleProjectCreationDialogOpen}
-                 onItemClick={item => {
-                     // this.props.dispatch(loadDetails(item.uuid, item.kind as ResourceKind));
-                 }}
-                 onItemDoubleClick={item => {
-                     // switch (item.kind) {
-                     //     case ResourceKind.COLLECTION:
-                     //         this.props.dispatch(loadCollection(item.uuid));
-                     //         this.props.dispatch(push(getCollectionUrl(item.uuid)));
-                     //     default:
-                     //         this.props.dispatch(loadDetails(item.uuid, ResourceKind.PROJECT));
-                     //         this.props.dispatch(setProjectItem(item.uuid, ItemMode.ACTIVE));
-                     // }
-                 }}
-                 {...props} />
              mainAppBarActions: MainAppBarActionProps = {
-                 onBreadcrumbClick: ({ itemId }: NavBreadcrumb) => {
-                     this.props.dispatch(setProjectItem(itemId, ItemMode.BOTH));
-                     this.props.dispatch(loadDetails(itemId, ResourceKind.PROJECT));
-                 },
                  onSearch: searchText => {
                      this.setState({ searchText });
                      this.props.dispatch(push(`/search?q=${searchText}`));
                  onDetailsPanelToggle: () => {
                      this.props.dispatch(detailsPanelActions.TOGGLE_DETAILS_PANEL());
                  },
-                 onContextMenu: (event: React.MouseEvent<HTMLElement>, breadcrumb: NavBreadcrumb) => {
-                     this.openContextMenu(event, {
-                         uuid: breadcrumb.itemId,
-                         name: breadcrumb.label,
-                         kind: ContextMenuKind.PROJECT
-                     });
-                 }
              };
  
-             toggleSidePanelOpen = (itemId: string) => {
-                 this.props.dispatch(sidePanelActions.TOGGLE_SIDE_PANEL_ITEM_OPEN(itemId));
-             }
-             toggleSidePanelActive = (itemId: string) => {
-                 this.props.dispatch(projectActions.RESET_PROJECT_TREE_ACTIVITY(itemId));
-                 const panelItem = this.props.sidePanelItems.find(it => it.id === itemId);
-                 if (panelItem && panelItem.activeAction) {
-                     panelItem.activeAction(this.props.dispatch, this.props.authService.getUuid());
-                 }
-             }
-             handleProjectCreationDialogOpen = (itemUuid: string) => {
-                 this.props.dispatch(reset(PROJECT_CREATE_DIALOG));
-                 this.props.dispatch(projectActions.OPEN_PROJECT_CREATOR({ ownerUuid: itemUuid }));
-             }
-             handleCollectionCreationDialogOpen = (itemUuid: string) => {
-                 this.props.dispatch(reset(COLLECTION_CREATE_DIALOG));
-                 this.props.dispatch(collectionCreateActions.OPEN_COLLECTION_CREATOR({ ownerUuid: itemUuid }));
-             }
-             openContextMenu = (event: React.MouseEvent<HTMLElement>, resource: { name: string; uuid: string; description?: string; isTrashed?: boolean, ownerUuid?: string, kind: ContextMenuKind; }) => {
-                 event.preventDefault();
-                 this.props.dispatch(
-                     contextMenuActions.OPEN_CONTEXT_MENU({
-                         position: { x: event.clientX, y: event.clientY },
-                         resource
-                     })
-                 );
-             }
              toggleCurrentTokenModal = () => {
                  this.setState({ isCurrentTokenDialogOpen: !this.state.isCurrentTokenDialogOpen });
              }