refs #master Merge branch 'origin/master' into 14186-progress-indicator-store
authorDaniel Kos <daniel.kos@contractors.roche.com>
Tue, 18 Sep 2018 11:32:23 +0000 (13:32 +0200)
committerDaniel Kos <daniel.kos@contractors.roche.com>
Tue, 18 Sep 2018 11:32:23 +0000 (13:32 +0200)
# Conflicts:
# src/services/groups-service/groups-service.ts
# src/store/store.ts
# src/views/project-panel/project-panel.tsx
# src/views/workbench/workbench.tsx

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

1  2 
package.json
src/common/custom-theme.ts
src/services/groups-service/groups-service.ts
src/store/project-panel/project-panel-middleware-service.ts
src/store/side-panel-tree/side-panel-tree-actions.ts
src/store/store.ts
src/views/project-panel/project-panel.tsx
src/views/workbench/workbench.tsx
yarn.lock

diff --combined package.json
index dd16eea932fdce1151b53295346ed7cf0345ef0f,623e11707f1c79bb07a5947c7a1cb43427007655..620ff5a6d03a6d7d616ab9d32b0ec2f47c0530ac
      "react-router-dom": "4.3.1",
      "react-router-redux": "5.0.0-alpha.9",
      "react-scripts-ts": "2.17.0",
+     "react-splitter-layout": "3.0.1",
      "react-transition-group": "2.4.0",
      "redux": "4.0.0",
      "redux-thunk": "2.3.0",
 -    "unionize": "2.1.2"
 +    "unionize": "2.1.2",
 +    "uuid": "3.3.2"
    },
    "scripts": {
      "start": "react-scripts-ts start",
@@@ -48,7 -48,6 +49,7 @@@
      "@types/react-router-redux": "5.0.15",
      "@types/redux-devtools": "3.0.44",
      "@types/redux-form": "7.4.5",
 +    "@types/uuid": "3.4.4",
      "axios-mock-adapter": "1.15.0",
      "enzyme": "3.4.4",
      "enzyme-adapter-react-16": "1.2.0",
index be8e77ff68b37698ec8f8fd6d438f7f03a29d5b5,3d56f78b5c80c8adf285526e9dd1439fbe3937c9..ff0eb5e34ce7449f672464485b5879cc98f5d382
@@@ -10,6 -10,7 +10,7 @@@ import grey from '@material-ui/core/col
  import green from '@material-ui/core/colors/green';
  import yellow from '@material-ui/core/colors/yellow';
  import red from '@material-ui/core/colors/red';
+ import teal from '@material-ui/core/colors/teal';
  
  export interface ArvadosThemeOptions extends ThemeOptions {
      customs: any;
@@@ -26,12 -27,12 +27,10 @@@ interface Colors 
      yellow700: string;
      red900: string;
      blue500: string;
 -    grey500: string;
 -    grey700: string;
  }
  
- const red900 = red["900"];
+ const arvadosPurple = '#361336';
  const purple800 = purple["800"];
- const grey200 = grey["200"];
- const grey300 = grey["300"];
  const grey500 = grey["500"];
  const grey600 = grey["600"];
  const grey700 = grey["700"];
@@@ -45,6 -46,8 +44,6 @@@ export const themeOptions: ArvadosTheme
              yellow700: yellow["700"],
              red900: red['900'],
              blue500: blue['500'],
 -            grey500,
 -            grey700
          }
      },
      overrides: {
@@@ -55,7 -58,7 +54,7 @@@
          },
          MuiAppBar: {
              colorPrimary: {
-                 backgroundColor: purple800
+                 backgroundColor: arvadosPurple
              }
          },
          MuiTabs: {
                  color: grey600
              },
              indicator: {
-                 backgroundColor: purple800
+                 backgroundColor: arvadosPurple
              }
          },
          MuiTab: {
              selected: {
                  fontWeight: 700,
-                 color: purple800
+                 color: arvadosPurple
              }
          },
          MuiList: {
              },
              underline: {
                  '&:after': {
-                     borderBottomColor: purple800
+                     borderBottomColor: arvadosPurple
                  },
                  '&:hover:not($disabled):not($focused):not($error):before': {
                      borderBottom: '1px solid inherit'
              },
              focused: {
                  "&$focused:not($error)": {
-                     color: purple800
+                     color: arvadosPurple
                  }
              }
          }
      },
      palette: {
          primary: {
-             main: rocheBlue,
-             dark: blue.A100
+             main: teal.A700,
+             dark: blue.A100,
+             contrastText: '#fff'
          }
      }
  };
index c2b559b78b164b1139aa0df920c255c6991b584a,299e6808546b224cb2e1b39f18bb3aeb73061b40..e705b6e5377541f5eaa245d2cf7afbc0b1c40dcf
@@@ -3,14 -3,14 +3,15 @@@
  // SPDX-License-Identifier: AGPL-3.0
  
  import * as _ from "lodash";
- import { CommonResourceService, ListResults } from "~/services/common-service/common-resource-service";
+ import { CommonResourceService, ListResults, ListArguments } from '~/services/common-service/common-resource-service';
  import { AxiosInstance } from "axios";
  import { CollectionResource } from "~/models/collection";
  import { ProjectResource } from "~/models/project";
  import { ProcessResource } from "~/models/process";
  import { TrashableResource } from "~/models/resource";
  import { TrashableResourceService } from "~/services/common-service/trashable-resource-service";
 -import { GroupResource } from '~/models/group';
 +import { ApiActions } from "~/services/api/api-actions";
++import { GroupResource } from "~/models/group";
  
  export interface ContentsArguments {
      limit?: number;
      includeTrash?: boolean;
  }
  
+ export interface SharedArguments extends ListArguments {
+     include?: string;
+ }
  export type GroupContentsResource =
      CollectionResource |
      ProjectResource |
      ProcessResource;
  
- export class GroupsService<T extends TrashableResource = TrashableResource> extends TrashableResourceService<T> {
+ export class GroupsService<T extends GroupResource = GroupResource> extends TrashableResourceService<T> {
  
 -    constructor(serverApi: AxiosInstance) {
 -        super(serverApi, "groups");
 +    constructor(serverApi: AxiosInstance, actions: ApiActions) {
 +        super(serverApi, "groups", actions);
      }
  
      contents(uuid: string, args: ContentsArguments = {}): Promise<ListResults<GroupContentsResource>> {
              filters: filters ? `[${filters}]` : undefined,
              order: order ? order : undefined
          };
 -        return this.serverApi
 -            .get(this.resourceType + `${uuid}/contents`, {
 -                params: CommonResourceService.mapKeys(_.snakeCase)(params)
 -            })
 -            .then(CommonResourceService.mapResponseKeys);
 +        return CommonResourceService.defaultResponse(
 +            this.serverApi
 +                .get(this.resourceType + `${uuid}/contents`, {
 +                    params: CommonResourceService.mapKeys(_.snakeCase)(params)
 +                }),
 +            this.actions
 +        );
      }
 -        return this.serverApi
 -            .get(this.resourceType + 'shared', { params })
 -            .then(CommonResourceService.mapResponseKeys);
+     shared(params: SharedArguments = {}): Promise<ListResults<GroupContentsResource>> {
++        return CommonResourceService.defaultResponse(
++            this.serverApi
++                .get(this.resourceType + 'shared', { params }),
++            this.actions
++        );
+     }
  }
  
  export enum GroupContentsResourcePrefix {
index 84eaa856ece812b602e86d450c20a48b1164ef06,519943c1f547a1b7c13c18f880dca8368dc4a635..09e76ae28b99ef30697228df98f97b78531559f7
@@@ -17,8 -17,7 +17,8 @@@ import { Dispatch, MiddlewareAPI } fro
  import { ProjectResource } from "~/models/project";
  import { updateResources } from "~/store/resources/resources-actions";
  import { getProperty } from "~/store/properties/properties";
 -import { snackbarActions } from '../snackbar/snackbar-actions';
 +import { snackbarActions, SnackbarKind } from '../snackbar/snackbar-actions';
 +import { progressIndicatorActions } from '~/store/progress-indicator/progress-indicator-actions.ts';
  import { DataExplorer, getDataExplorer } from '../data-explorer/data-explorer-reducer';
  import { ListResults } from '~/services/common-service/common-resource-service';
  import { loadContainers } from '../processes/processes-actions';
@@@ -39,21 -38,12 +39,21 @@@ export class ProjectPanelMiddlewareServ
              api.dispatch(projectPanelDataExplorerIsNotSet());
          } else {
              try {
 +                api.dispatch(progressIndicatorActions.START_WORKING(this.getId()));
                  const response = await this.services.groupsService.contents(projectUuid, getParams(dataExplorer));
 +                api.dispatch(progressIndicatorActions.PERSIST_STOP_WORKING(this.getId()));
                  api.dispatch<any>(updateFavorites(response.items.map(item => item.uuid)));
                  api.dispatch(updateResources(response.items));
                  await api.dispatch<any>(loadMissingProcessesInformation(response.items));
                  api.dispatch(setItems(response));
              } catch (e) {
 +                api.dispatch(progressIndicatorActions.PERSIST_STOP_WORKING(this.getId()));
 +                api.dispatch(projectPanelActions.SET_ITEMS({
 +                    items: [],
 +                    itemsAvailable: 0,
 +                    page: 0,
 +                    rowsPerPage: dataExplorer.rowsPerPage
 +                }));
                  api.dispatch(couldNotFetchProjectContents());
              }
          }
@@@ -76,19 -66,19 +76,19 @@@ export const loadMissingProcessesInform
          }
      };
  
- const setItems = (listResults: ListResults<GroupContentsResource>) =>
export const setItems = (listResults: ListResults<GroupContentsResource>) =>
      projectPanelActions.SET_ITEMS({
          ...listResultsToDataExplorerItemsMeta(listResults),
          items: listResults.items.map(resource => resource.uuid),
      });
  
- const getParams = (dataExplorer: DataExplorer) => ({
export const getParams = (dataExplorer: DataExplorer) => ({
      ...dataExplorerToListParams(dataExplorer),
      order: getOrder(dataExplorer),
      filters: getFilters(dataExplorer),
  });
  
- const getFilters = (dataExplorer: DataExplorer) => {
export const getFilters = (dataExplorer: DataExplorer) => {
      const columns = dataExplorer.columns as DataColumns<string, ProjectPanelFilter>;
      const typeFilters = getDataExplorerColumnFilters(columns, ProjectPanelColumnNames.TYPE);
      const statusFilters = getDataExplorerColumnFilters(columns, ProjectPanelColumnNames.STATUS);
          .getFilters();
  };
  
- const getOrder = (dataExplorer: DataExplorer) => {
export const getOrder = (dataExplorer: DataExplorer) => {
      const sortColumn = dataExplorer.columns.find(c => c.sortDirection !== SortDirection.NONE);
      const order = new OrderBuilder<ProjectResource>();
      if (sortColumn) {
@@@ -126,8 -116,7 +126,8 @@@ const projectPanelCurrentUuidIsNotSet 
  
  const couldNotFetchProjectContents = () =>
      snackbarActions.OPEN_SNACKBAR({
 -        message: 'Could not fetch project contents.'
 +        message: 'Could not fetch project contents.',
 +        kind: SnackbarKind.ERROR
      });
  
  const projectPanelDataExplorerIsNotSet = () =>
index 561df1d7ed5a2f4e6dd8391a953c0a90222ea106,073de22c4ea4b9fa30aae1b15fc2311dfc706fe0..23c5ea2217972d07765cc720b0e3253f4ac50b2a
@@@ -13,7 -13,6 +13,7 @@@ import { getTreePicker, TreePicker } fr
  import { TreeItemStatus } from "~/components/tree/tree";
  import { getNodeAncestors, getNodeValue, getNodeAncestorsIds, getNode } from '~/models/tree';
  import { ProjectResource } from '~/models/project';
 +import { progressIndicatorActions } from '../progress-indicator/progress-indicator-actions';
  
  export enum SidePanelTreeCategory {
      PROJECTS = 'Projects',
@@@ -148,14 -147,14 +148,14 @@@ export const expandSidePanelTreeItem = 
          }
      };
  
- const getSidePanelTreeNode = (nodeId: string) => (treePicker: TreePicker) => {
export const getSidePanelTreeNode = (nodeId: string) => (treePicker: TreePicker) => {
      const sidePanelTree = getTreePicker(SIDE_PANEL_TREE)(treePicker);
      return sidePanelTree
          ? getNodeValue(nodeId)(sidePanelTree)
          : undefined;
  };
  
- const getSidePanelTreeNodeAncestorsIds = (nodeId: string) => (treePicker: TreePicker) => {
export const getSidePanelTreeNodeAncestorsIds = (nodeId: string) => (treePicker: TreePicker) => {
      const sidePanelTree = getTreePicker(SIDE_PANEL_TREE)(treePicker);
      return sidePanelTree
          ? getNodeAncestorsIds(nodeId)(sidePanelTree)
diff --combined src/store/store.ts
index 205a21e16c8caf97897fc9e665e426b5f6e5f927,43ab2310f754c91d63a58f25fd7ef3bfcbe7af90..012b747425b72e714472a5b2a0cfe89d03dc2546
@@@ -32,7 -32,8 +32,9 @@@ import { TrashPanelMiddlewareService } 
  import { TRASH_PANEL_ID } from "~/store/trash-panel/trash-panel-action";
  import { processLogsPanelReducer } from './process-logs-panel/process-logs-panel-reducer';
  import { processPanelReducer } from '~/store/process-panel/process-panel-reducer';
+ import { SHARED_WITH_ME_PANEL_ID } from '~/store/shared-with-me-panel/shared-with-me-panel-actions';
+ import { SharedWithMeMiddlewareService } from './shared-with-me-panel/shared-with-me-middleware-service';
 +import { progressIndicatorReducer } from './progress-indicator/progress-indicator-reducer';
  
  const composeEnhancers =
      (process.env.NODE_ENV === 'development' &&
@@@ -55,13 -56,17 +57,17 @@@ export function configureStore(history
      const trashPanelMiddleware = dataExplorerMiddleware(
          new TrashPanelMiddlewareService(services, TRASH_PANEL_ID)
      );
+     const sharedWithMePanelMiddleware = dataExplorerMiddleware(
+         new SharedWithMeMiddlewareService(services, SHARED_WITH_ME_PANEL_ID)
+     );
  
      const middlewares: Middleware[] = [
          routerMiddleware(history),
          thunkMiddleware.withExtraArgument(services),
          projectPanelMiddleware,
          favoritePanelMiddleware,
-         trashPanelMiddleware
+         trashPanelMiddleware,
+         sharedWithMePanelMiddleware,
      ];
      const enhancer = composeEnhancers(applyMiddleware(...middlewares));
      return createStore(rootReducer, enhancer);
@@@ -84,6 -89,5 +90,6 @@@ const createRootReducer = (services: Se
      snackbar: snackbarReducer,
      treePicker: treePickerReducer,
      fileUploader: fileUploaderReducer,
 -    processPanel: processPanelReducer
 +    processPanel: processPanelReducer,
 +    progressIndicator: progressIndicatorReducer
  });
index 61f6766c443115daa18948f98855f7a452abf7da,25579396167eef30d75abbe2d80ca71937754202..2b2be2e8905ec5d111faf8c121da72dddba94371
@@@ -3,7 -3,6 +3,6 @@@
  // SPDX-License-Identifier: AGPL-3.0
  
  import * as React from 'react';
- import { Button, 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';
@@@ -14,7 -13,6 +13,6 @@@ import { ContainerRequestState } from '
  import { SortDirection } from '~/components/data-table/data-column';
  import { ResourceKind, Resource } from '~/models/resource';
  import { resourceLabel } from '~/common/labels';
- import { ArvadosTheme } from '~/common/custom-theme';
  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';
@@@ -25,24 -23,9 +23,23 @@@ import { ProjectResource } from '~/mode
  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';
--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';
++import { StyleRulesCallback, WithStyles } from "@material-ui/core";
++import { ArvadosTheme } from "~/common/custom-theme";
++import withStyles from "@material-ui/core/styles/withStyles";
 +
 +type CssRules = 'root' | "button";
 +
 +const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
 +    root: {
 +        position: 'relative',
 +        width: '100%',
 +        height: '100%'
 +    },
 +    button: {
 +        marginLeft: theme.spacing.unit
 +    },
 +});
  
  export enum ProjectPanelColumnNames {
      NAME = "Name",
@@@ -127,33 -110,30 +124,33 @@@ interface ProjectPanelDataProps 
      resources: ResourcesState;
  }
  
 -type ProjectPanelProps = ProjectPanelDataProps & DispatchProp & RouteComponentProps<{ id: string }>;
 +type ProjectPanelProps = ProjectPanelDataProps & DispatchProp
 +    & WithStyles<CssRules> & RouteComponentProps<{ id: string }>;
  
 -export const ProjectPanel = connect((state: RootState) => ({
 +export const ProjectPanel = withStyles(styles)(
 +    connect((state: RootState) => ({
          currentItemId: getProperty(PROJECT_PANEL_CURRENT_UUID)(state.properties),
          resources: state.resources
      }))(
          class extends React.Component<ProjectPanelProps> {
              render() {
 -                return this.hasAnyItems()
 -                    ? <DataExplorer
 +                const { classes } = this.props;
 +                return <div className={classes.root}>
 +                    <DataExplorer
                          id={PROJECT_PANEL_ID}
                          onRowClick={this.handleRowClick}
                          onRowDoubleClick={this.handleRowDoubleClick}
                          onContextMenu={this.handleContextMenu}
                          contextMenuColumn={true}
 -                        dataTableDefaultView={<DataTableDefaultView icon={ProjectIcon}/>} />
 -                    : <PanelDefaultView
 -                        icon={ProjectIcon}
 -                        messages={['Your project is empty.', 'Please create a project or create a collection and upload a data.']} />;
 -            }
 -
 -            hasAnyItems = () => {
 -                const resources = filterResources(this.isCurrentItemChild)(this.props.resources);
 -                return resources.length > 0;
 +                        dataTableDefaultView={
 +                            <DataTableDefaultView
 +                                icon={ProjectIcon}
 +                                messages={[
 +                                    'Your project is empty.',
 +                                    'Please create a project or create a collection and upload a data.'
 +                                ]}/>
 +                        }/>
 +                </div>;
              }
  
              isCurrentItemChild = (resource: Resource) => {
              }
  
          }
 -    );
 +    )
 +);
index c22dde25f2a80be37775c27f19e701f3ad43a028,1d7d47d09ee89f8085312cf2b0a1b804f823e07c..ad1a266881993b8f5e38eb568ac0f8f2c175ef3d
@@@ -41,21 -41,29 +41,31 @@@ import { FilesUploadCollectionDialog } 
  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 { Grid } from '@material-ui/core';
 +import { Grid, LinearProgress } from '@material-ui/core';
+ import { SharedWithMePanel } from '../shared-with-me-panel/shared-with-me-panel';
 -import { ProcessCommandDialog } from '~/views-components/process-command-dialog/process-command-dialog';
+ import SplitterLayout from 'react-splitter-layout';
 +import { ProcessCommandDialog } from '~/views-components/process-command-dialog/process-command-dialog';
 +import { isSystemWorking } from "~/store/progress-indicator/progress-indicator-reducer";
  
- type CssRules = 'root' | 'asidePanel' | 'contentWrapper' | 'content' | 'appBar';
+ type CssRules = 'root' | 'container' | 'splitter' | 'asidePanel' | 'contentWrapper' | 'content' | 'appBar';
  
  const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
      root: {
          overflow: 'hidden',
          width: '100vw',
 -        height: '100vh'
 +        height: '100vh',
 +        paddingTop: theme.spacing.unit * 8
      },
+     container: {
+         position: 'relative'
+     },
+     splitter: {
+         '& > .layout-splitter': {
+             width: '2px'
+         }
+     },
      asidePanel: {
-         maxWidth: '240px',
+         height: '100%',
          background: theme.palette.background.default
      },
      contentWrapper: {
@@@ -64,7 -72,6 +74,6 @@@
      },
      content: {
          minWidth: 0,
-         overflow: 'auto',
          paddingLeft: theme.spacing.unit * 3,
          paddingRight: theme.spacing.unit * 3,
      },
@@@ -76,7 -83,6 +85,7 @@@
  interface WorkbenchDataProps {
      user?: User;
      currentToken?: string;
 +    working: boolean;
  }
  
  interface WorkbenchGeneralProps {
@@@ -95,7 -101,6 +104,7 @@@ export const Workbench = withStyles(sty
          (state: RootState) => ({
              user: state.auth.user,
              currentToken: state.auth.apiToken,
 +            working: isSystemWorking(state.progressIndicator)
          })
      )(
          class extends React.Component<WorkbenchProps, WorkbenchState> {
              render() {
                  const { classes } = this.props;
                  return <>
 +                    <MainAppBar
 +                        searchText={this.state.searchText}
 +                        user={this.props.user}
 +                        onSearch={this.onSearch}
 +                        buildInfo={this.props.buildInfo}>
 +                        {this.props.working ? <LinearProgress color="secondary" /> : null}
 +                    </MainAppBar>
                      <Grid container direction="column" className={classes.root}>
 -                        <Grid className={classes.appBar}>
 -                            <MainAppBar
 -                                searchText={this.state.searchText}
 -                                user={this.props.user}
 -                                onSearch={this.onSearch}
 -                                buildInfo={this.props.buildInfo} />
 -                        </Grid>
                          {this.props.user &&
                              <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={classes.contentWrapper}>
-                                     <Grid item>
-                                         <MainContentBar />
-                                     </Grid>
-                                     <Grid item xs className={classes.content}>
-                                         <Switch>
-                                             <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={Routes.TRASH} component={TrashPanel} />
-                                             <Route path={Routes.PROCESS_LOGS} component={ProcessLogPanel} />
-                                         </Switch>
-                                     </Grid>
+                                 <Grid container item className={classes.container}>
+                                 <SplitterLayout customClassName={classes.splitter} percentage={true}
+                                     primaryIndex={0} primaryMinSize={20} secondaryInitialSize={80} secondaryMinSize={40}>
+                                         <Grid container item xs component='aside' direction='column' className={classes.asidePanel}>
+                                             <SidePanel />
+                                         </Grid>
+                                         <Grid container item xs component="main" direction="column" className={classes.contentWrapper}>
+                                             <Grid item>
+                                                 <MainContentBar />
+                                             </Grid>
+                                             <Grid item xs className={classes.content}>
+                                                 <Switch>
+                                                     <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={Routes.TRASH} component={TrashPanel} />
+                                                     <Route path={Routes.PROCESS_LOGS} component={ProcessLogPanel} />
+                                                     <Route path={Routes.SHARED_WITH_ME} component={SharedWithMePanel} />
+                                                 </Switch>
+                                             </Grid>
+                                         </Grid>
+                                     </SplitterLayout>
                                  </Grid>
                                  <Grid item>
                                      <DetailsPanel />
                                  </Grid>
-                             </Grid>}
+                             </Grid>
+                         }
                      </Grid>
                      <ContextMenu />
                      <CopyCollectionDialog />
diff --combined yarn.lock
index 45c879fd84f5f8d224c08ea3ca7f78f739707c57,3765778d616a8698c561eb92a8e8e9339c3f1482..30e94bdefb4c9be9d37fc394908c15653130d55f
+++ b/yarn.lock
    dependencies:
      "@types/enzyme" "*"
  
- "@types/enzyme@*":
-   version "3.1.12"
-   resolved "https://registry.yarnpkg.com/@types/enzyme/-/enzyme-3.1.12.tgz#293bb07c1ef5932d37add3879e72e0f5bc614f3c"
-   dependencies:
-     "@types/cheerio" "*"
-     "@types/react" "*"
- "@types/enzyme@3.1.13":
+ "@types/enzyme@*", "@types/enzyme@3.1.13":
    version "3.1.13"
    resolved "https://registry.yarnpkg.com/@types/enzyme/-/enzyme-3.1.13.tgz#4bbc5c81fa40c9fc7efee25c4a23cb37119a33ea"
    dependencies:
    version "4.14.116"
    resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.116.tgz#5ccf215653e3e8c786a58390751033a9adca0eb9"
  
- "@types/node@*":
-   version "10.5.2"
-   resolved "https://registry.yarnpkg.com/@types/node/-/node-10.5.2.tgz#f19f05314d5421fe37e74153254201a7bf00a707"
- "@types/node@10.7.1":
+ "@types/node@*", "@types/node@10.7.1":
    version "10.7.1"
    resolved "https://registry.yarnpkg.com/@types/node/-/node-10.7.1.tgz#b704d7c259aa40ee052eec678758a68d07132a2e"
  
      "@types/react" "*"
      redux "^3.6.0 || ^4.0.0"
  
 +"@types/uuid@3.4.4":
 +  version "3.4.4"
 +  resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-3.4.4.tgz#7af69360fa65ef0decb41fd150bf4ca5c0cefdf5"
 +  dependencies:
 +    "@types/node" "*"
 +
  abab@^1.0.4:
    version "1.0.4"
    resolved "https://registry.yarnpkg.com/abab/-/abab-1.0.4.tgz#5faad9c2c07f60dd76770f71cf025b62a63cfd4e"
@@@ -1707,14 -1690,10 +1696,10 @@@ color-convert@^1.3.0, color-convert@^1.
    dependencies:
      color-name "1.1.1"
  
- color-name@1.1.1:
+ color-name@1.1.1, color-name@^1.0.0:
    version "1.1.1"
    resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.1.tgz#4b1415304cf50028ea81643643bd82ea05803689"
  
- color-name@^1.0.0:
-   version "1.1.3"
-   resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
  color-string@^0.3.0:
    version "0.3.0"
    resolved "https://registry.yarnpkg.com/color-string/-/color-string-0.3.0.tgz#27d46fb67025c5c2fa25993bfbf579e47841b991"
@@@ -2363,20 -2342,13 +2348,13 @@@ domutils@1.1
    dependencies:
      domelementtype "1"
  
- domutils@1.5.1:
+ domutils@1.5.1, domutils@^1.5.1:
    version "1.5.1"
    resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf"
    dependencies:
      dom-serializer "0"
      domelementtype "1"
  
- domutils@^1.5.1:
-   version "1.7.0"
-   resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a"
-   dependencies:
-     dom-serializer "0"
-     domelementtype "1"
  dot-prop@^4.1.0:
    version "4.2.0"
    resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.0.tgz#1f19e0c2e1aa0e32797c49799f2837ac6af69c57"
@@@ -2838,14 -2810,10 +2816,10 @@@ extract-text-webpack-plugin@3.0.2
      schema-utils "^0.3.0"
      webpack-sources "^1.0.1"
  
- extsprintf@1.3.0:
+ extsprintf@1.3.0, extsprintf@^1.2.0:
    version "1.3.0"
    resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05"
  
- extsprintf@^1.2.0:
-   version "1.4.0"
-   resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f"
  fast-deep-equal@^1.0.0:
    version "1.1.0"
    resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614"
@@@ -4940,7 -4908,7 +4914,7 @@@ minimatch@^3.0.2, minimatch@^3.0.3, min
    dependencies:
      brace-expansion "^1.1.7"
  
- minimist@0.0.8:
+ minimist@0.0.8, minimist@~0.0.1:
    version "0.0.8"
    resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
  
@@@ -4948,10 -4916,6 +4922,6 @@@ minimist@^1.1.0, minimist@^1.1.1, minim
    version "1.2.0"
    resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
  
- minimist@~0.0.1:
-   version "0.0.10"
-   resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf"
  minipass@^2.2.1, minipass@^2.3.3:
    version "2.3.3"
    resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.3.tgz#a7dcc8b7b833f5d368759cce544dccb55f50f233"
@@@ -5351,18 -5315,12 +5321,12 @@@ onetime@^2.0.0
    dependencies:
      mimic-fn "^1.0.0"
  
- opn@5.2.0:
+ opn@5.2.0, opn@^5.1.0:
    version "5.2.0"
    resolved "https://registry.yarnpkg.com/opn/-/opn-5.2.0.tgz#71fdf934d6827d676cecbea1531f95d354641225"
    dependencies:
      is-wsl "^1.1.0"
  
- opn@^5.1.0:
-   version "5.3.0"
-   resolved "https://registry.yarnpkg.com/opn/-/opn-5.3.0.tgz#64871565c863875f052cfdf53d3e3cb5adb53b1c"
-   dependencies:
-     is-wsl "^1.1.0"
  optimist@^0.6.1:
    version "0.6.1"
    resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686"
@@@ -6042,14 -6000,10 +6006,10 @@@ q@^1.1.2
    version "1.5.1"
    resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
  
- qs@6.5.1:
+ qs@6.5.1, qs@~6.5.1:
    version "6.5.1"
    resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8"
  
- qs@~6.5.1:
-   version "6.5.2"
-   resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
  query-string@^4.1.0:
    version "4.3.4"
    resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.3.4.tgz#bbb693b9ca915c232515b228b1a02b609043dbeb"
@@@ -6187,11 -6141,7 +6147,7 @@@ react-event-listener@^0.6.2
      prop-types "^15.6.0"
      warning "^4.0.1"
  
- react-is@^16.4.1:
-   version "16.4.1"
-   resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.4.1.tgz#d624c4650d2c65dbd52c72622bbf389435d9776e"
- react-is@^16.4.2:
+ react-is@^16.4.1, react-is@^16.4.2:
    version "16.4.2"
    resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.4.2.tgz#84891b56c2b6d9efdee577cc83501dfc5ecead88"
  
@@@ -6304,6 -6254,10 +6260,10 @@@ react-scripts-ts@2.17.0
    optionalDependencies:
      fsevents "^1.1.3"
  
+ react-splitter-layout@3.0.1:
+   version "3.0.1"
+   resolved "https://registry.yarnpkg.com/react-splitter-layout/-/react-splitter-layout-3.0.1.tgz#c2e00e69b35d240ab7a44f395d41803c5f4b70ef"
  react-test-renderer@^16.0.0-0:
    version "16.4.1"
    resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.4.1.tgz#f2fb30c2c7b517db6e5b10ed20bb6b0a7ccd8d70"
@@@ -6671,18 -6625,12 +6631,12 @@@ resolve@1.1.7
    version "1.1.7"
    resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b"
  
- resolve@1.6.0:
+ resolve@1.6.0, resolve@^1.1.7, resolve@^1.3.2:
    version "1.6.0"
    resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.6.0.tgz#0fbd21278b27b4004481c395349e7aba60a9ff5c"
    dependencies:
      path-parse "^1.0.5"
  
- resolve@^1.1.7, resolve@^1.3.2:
-   version "1.8.1"
-   resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.8.1.tgz#82f1ec19a423ac1fbd080b0bab06ba36e84a7a26"
-   dependencies:
-     path-parse "^1.0.5"
  restore-cursor@^2.0.0:
    version "2.0.0"
    resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf"
@@@ -7130,11 -7078,7 +7084,7 @@@ static-extend@^0.1.1
      define-property "^0.2.5"
      object-copy "^0.1.0"
  
- "statuses@>= 1.3.1 < 2", "statuses@>= 1.4.0 < 2":
-   version "1.5.0"
-   resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
- statuses@~1.4.0:
+ "statuses@>= 1.3.1 < 2", "statuses@>= 1.4.0 < 2", statuses@~1.4.0:
    version "1.4.0"
    resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087"
  
@@@ -7814,14 -7758,14 +7764,14 @@@ utils-merge@1.0.1
    version "1.0.1"
    resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
  
 +uuid@3.3.2, uuid@^3.1.0:
 +  version "3.3.2"
 +  resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131"
 +
  uuid@^2.0.2:
    version "2.0.3"
    resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a"
  
 -uuid@^3.1.0:
 -  version "3.3.2"
 -  resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131"
 -
  validate-npm-package-license@^3.0.1:
    version "3.0.3"
    resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz#81643bcbef1bdfecd4623793dc4648948ba98338"
@@@ -8004,14 -7948,10 +7954,10 @@@ whatwg-encoding@^1.0.1, whatwg-encoding
    dependencies:
      iconv-lite "0.4.19"
  
- whatwg-fetch@2.0.3:
+ whatwg-fetch@2.0.3, whatwg-fetch@>=0.10.0:
    version "2.0.3"
    resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz#9c84ec2dcf68187ff00bc64e1274b442176e1c84"
  
- whatwg-fetch@>=0.10.0:
-   version "2.0.4"
-   resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz#dde6a5df315f9d39991aa17621853d720b85566f"
  whatwg-mimetype@^2.0.0, whatwg-mimetype@^2.1.0:
    version "2.1.0"
    resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.1.0.tgz#f0f21d76cbba72362eb609dbed2a30cd17fcc7d4"
@@@ -8058,14 -7998,10 +8004,10 @@@ window-size@0.1.0
    version "0.1.0"
    resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d"
  
- wordwrap@0.0.2:
+ wordwrap@0.0.2, wordwrap@~0.0.2:
    version "0.0.2"
    resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f"
  
- wordwrap@~0.0.2:
-   version "0.0.3"
-   resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107"
  wordwrap@~1.0.0:
    version "1.0.0"
    resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"