[ARVADOS-WORKBENCH2] created: 1.1.4-359-gab3e261

Git user git at public.curoverse.com
Mon Jul 23 18:18:47 EDT 2018


        at  ab3e261d28ff83fa214002a372a055817a931cd1 (commit)


commit ab3e261d28ff83fa214002a372a055817a931cd1
Author: Daniel Kos <daniel.kos at contractors.roche.com>
Date:   Tue Jul 24 00:18:41 2018 +0200

    Add favorite panel
    
    Feature #13753
    
    Arvados-DCO-1.1-Signed-off-by: Daniel Kos <daniel.kos at contractors.roche.com>

diff --git a/src/components/side-panel/side-panel.tsx b/src/components/side-panel/side-panel.tsx
index 4240b1b..0d27584 100644
--- a/src/components/side-panel/side-panel.tsx
+++ b/src/components/side-panel/side-panel.tsx
@@ -58,6 +58,7 @@ export interface SidePanelItem {
     open?: boolean;
     margin?: boolean;
     openAble?: boolean;
+    path?: string;
 }
 
 interface SidePanelDataProps {
diff --git a/src/services/favorite-service/favorite-service.ts b/src/services/favorite-service/favorite-service.ts
index d075b79..fe7c787 100644
--- a/src/services/favorite-service/favorite-service.ts
+++ b/src/services/favorite-service/favorite-service.ts
@@ -10,9 +10,12 @@ import { ListArguments, ListResults } from "../../common/api/common-resource-ser
 import { OrderBuilder } from "../../common/api/order-builder";
 
 export interface FavoriteListArguments extends ListArguments {
+    limit?: number;
+    offset?: number;
     filters?: FilterBuilder<LinkResource>;
     order?: OrderBuilder<LinkResource>;
 }
+
 export class FavoriteService {
     constructor(
         private linkService: LinkService,
@@ -63,6 +66,4 @@ export class FavoriteService {
                 });
             });
     }
-
-
-}
\ No newline at end of file
+}
diff --git a/src/services/project-service/project-service.ts b/src/services/project-service/project-service.ts
index f759547..7b1640e 100644
--- a/src/services/project-service/project-service.ts
+++ b/src/services/project-service/project-service.ts
@@ -32,5 +32,4 @@ export class ProjectService extends GroupsService<ProjectResource> {
                 .create<ProjectResource>()
                 .addEqual("groupClass", GroupClass.Project));
     }
-
 }
diff --git a/src/store/favorite-panel/favorite-panel-middleware.ts b/src/store/favorite-panel/favorite-panel-middleware.ts
new file mode 100644
index 0000000..e2743c2
--- /dev/null
+++ b/src/store/favorite-panel/favorite-panel-middleware.ts
@@ -0,0 +1,132 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { Middleware } from "redux";
+import { dataExplorerActions } from "../data-explorer/data-explorer-action";
+import { favoriteService, groupsService } from "../../services/services";
+import { RootState } from "../store";
+import { getDataExplorer } from "../data-explorer/data-explorer-reducer";
+import { FilterBuilder } from "../../common/api/filter-builder";
+import { DataColumns } from "../../components/data-table/data-table";
+import { ProcessResource } from "../../models/process";
+import { OrderBuilder } from "../../common/api/order-builder";
+import { GroupContentsResource, GroupContentsResourcePrefix } from "../../services/groups-service/groups-service";
+import { SortDirection } from "../../components/data-table/data-column";
+import {
+    columns,
+    FAVORITE_PANEL_ID,
+    FavoritePanelColumnNames,
+    FavoritePanelFilter
+} from "../../views/favorite-panel/favorite-panel";
+import { FavoritePanelItem, resourceToDataItem } from "../../views/favorite-panel/favorite-panel-item";
+
+export const favoritePanelMiddleware: Middleware = store => next => {
+    next(dataExplorerActions.SET_COLUMNS({ id: FAVORITE_PANEL_ID, columns }));
+
+    return action => {
+
+        const handleProjectPanelAction = <T extends { id: string }>(handler: (data: T) => void) =>
+            (data: T) => {
+                next(action);
+                if (data.id === FAVORITE_PANEL_ID) {
+                    handler(data);
+                }
+            };
+
+        dataExplorerActions.match(action, {
+            SET_PAGE: handleProjectPanelAction(() => {
+                store.dispatch(dataExplorerActions.REQUEST_ITEMS({ id: FAVORITE_PANEL_ID }));
+            }),
+            SET_ROWS_PER_PAGE: handleProjectPanelAction(() => {
+                store.dispatch(dataExplorerActions.REQUEST_ITEMS({ id: FAVORITE_PANEL_ID }));
+            }),
+            SET_FILTERS: handleProjectPanelAction(() => {
+                store.dispatch(dataExplorerActions.RESET_PAGINATION({ id: FAVORITE_PANEL_ID }));
+                store.dispatch(dataExplorerActions.REQUEST_ITEMS({ id: FAVORITE_PANEL_ID }));
+            }),
+            TOGGLE_SORT: handleProjectPanelAction(() => {
+                store.dispatch(dataExplorerActions.REQUEST_ITEMS({ id: FAVORITE_PANEL_ID }));
+            }),
+            SET_SEARCH_VALUE: handleProjectPanelAction(() => {
+                store.dispatch(dataExplorerActions.RESET_PAGINATION({ id: FAVORITE_PANEL_ID }));
+                store.dispatch(dataExplorerActions.REQUEST_ITEMS({ id: FAVORITE_PANEL_ID }));
+            }),
+            REQUEST_ITEMS: handleProjectPanelAction(() => {
+                const state = store.getState() as RootState;
+                const dataExplorer = getDataExplorer(state.dataExplorer, FAVORITE_PANEL_ID);
+                const columns = dataExplorer.columns as DataColumns<FavoritePanelItem, FavoritePanelFilter>;
+                const typeFilters = getColumnFilters(columns, FavoritePanelColumnNames.TYPE);
+                const statusFilters = getColumnFilters(columns, FavoritePanelColumnNames.STATUS);
+                const sortColumn = dataExplorer.columns.find(({ sortDirection }) => Boolean(sortDirection && sortDirection !== "none"));
+                const sortDirection = sortColumn && sortColumn.sortDirection === SortDirection.Asc ? SortDirection.Asc : SortDirection.Desc;
+                if (typeFilters.length > 0) {
+                    favoriteService
+                        .list(state.projects.currentItemId, {
+                            limit: dataExplorer.rowsPerPage,
+                            offset: dataExplorer.page * dataExplorer.rowsPerPage,
+                            order: /*sortColumn
+                                ? sortColumn.name === FavoritePanelColumnNames.NAME
+                                    ? getOrder("name", sortDirection)
+                                    : getOrder("createdAt", sortDirection)
+                                : */OrderBuilder.create(),
+                            filters: FilterBuilder
+                                .create()
+                                .concat(FilterBuilder
+                                    .create()
+                                    .addIsA("uuid", typeFilters.map(f => f.type)))
+                                .concat(FilterBuilder
+                                    .create<ProcessResource>(GroupContentsResourcePrefix.Process)
+                                    .addIn("state", statusFilters.map(f => f.type)))
+                                .concat(getSearchFilter(dataExplorer.searchValue))
+                        })
+                        .then(response => {
+                            store.dispatch(dataExplorerActions.SET_ITEMS({
+                                id: FAVORITE_PANEL_ID,
+                                items: response.items.map(resourceToDataItem),
+                                itemsAvailable: response.itemsAvailable,
+                                page: Math.floor(response.offset / response.limit),
+                                rowsPerPage: response.limit
+                            }));
+                        });
+                } else {
+                    store.dispatch(dataExplorerActions.SET_ITEMS({
+                        id: FAVORITE_PANEL_ID,
+                        items: [],
+                        itemsAvailable: 0,
+                        page: 0,
+                        rowsPerPage: dataExplorer.rowsPerPage
+                    }));
+                }
+            }),
+            default: () => next(action)
+        });
+    };
+};
+
+const getColumnFilters = (columns: DataColumns<FavoritePanelItem, FavoritePanelFilter>, columnName: string) => {
+    const column = columns.find(c => c.name === columnName);
+    return column && column.filters ? column.filters.filter(f => f.selected) : [];
+};
+
+const getOrder = (attribute: "name" | "createdAt", direction: SortDirection) =>
+    [
+        OrderBuilder.create<GroupContentsResource>(GroupContentsResourcePrefix.Collection),
+        OrderBuilder.create<GroupContentsResource>(GroupContentsResourcePrefix.Process),
+        OrderBuilder.create<GroupContentsResource>(GroupContentsResourcePrefix.Project)
+    ].reduce((acc, b) =>
+        acc.concat(direction === SortDirection.Asc
+            ? b.addAsc(attribute)
+            : b.addDesc(attribute)), OrderBuilder.create());
+
+const getSearchFilter = (searchValue: string) =>
+    searchValue
+        ? [
+            FilterBuilder.create<GroupContentsResource>(GroupContentsResourcePrefix.Collection),
+            FilterBuilder.create<GroupContentsResource>(GroupContentsResourcePrefix.Process),
+            FilterBuilder.create<GroupContentsResource>(GroupContentsResourcePrefix.Project)]
+            .reduce((acc, b) =>
+                acc.concat(b.addILike("name", searchValue)), FilterBuilder.create())
+        : FilterBuilder.create();
+
+
diff --git a/src/store/navigation/navigation-action.ts b/src/store/navigation/navigation-action.ts
index 3920b5a..7f78243 100644
--- a/src/store/navigation/navigation-action.ts
+++ b/src/store/navigation/navigation-action.ts
@@ -58,3 +58,8 @@ export const setProjectItem = (itemId: string, itemMode: ItemMode) =>
 
         }
     };
+
+export const setFavoriteItem = (itemId: string, itemMode: ItemMode) =>
+    (dispatch: Dispatch, getState: () => RootState) => {
+        const a = 1;
+    };
diff --git a/src/store/side-panel/side-panel-reducer.ts b/src/store/side-panel/side-panel-reducer.ts
index 2bbd6a1..5dd5c01 100644
--- a/src/store/side-panel/side-panel-reducer.ts
+++ b/src/store/side-panel/side-panel-reducer.ts
@@ -14,11 +14,12 @@ export const sidePanelReducer = (state: SidePanelState = sidePanelData, action:
         return sidePanelData;
     } else {
         return sidePanelActions.match(action, {
-            TOGGLE_SIDE_PANEL_ITEM_OPEN: itemId => state.map(it => itemId === it.id && it.open === false ? {...it, open: true} : {...it, open: false}),
+            TOGGLE_SIDE_PANEL_ITEM_OPEN: itemId =>
+                state.map(it => ({...it, open: itemId === it.id && it.open === false})),
             TOGGLE_SIDE_PANEL_ITEM_ACTIVE: itemId => {
                 const sidePanel = _.cloneDeep(state);
                 resetSidePanelActivity(sidePanel);
-                sidePanel.map(it => {
+                sidePanel.forEach(it => {
                     if (it.id === itemId) {
                         it.active = true;
                     }
@@ -77,6 +78,7 @@ export const sidePanelData = [
         name: "Favorites",
         icon: FavoriteIcon,
         active: false,
+        path: '/favorites'
     },
     {
         id: SidePanelIdentifiers.Trash,
diff --git a/src/store/store.ts b/src/store/store.ts
index adb7ddd..fbb5ad6 100644
--- a/src/store/store.ts
+++ b/src/store/store.ts
@@ -14,6 +14,7 @@ import { dataExplorerReducer, DataExplorerState } from './data-explorer/data-exp
 import { projectPanelMiddleware } from './project-panel/project-panel-middleware';
 import { detailsPanelReducer, DetailsPanelState } from './details-panel/details-panel-reducer';
 import { contextMenuReducer, ContextMenuState } from './context-menu/context-menu-reducer';
+import { favoritePanelMiddleware } from "./favorite-panel/favorite-panel-middleware";
 
 const composeEnhancers =
     (process.env.NODE_ENV === 'development' &&
@@ -45,7 +46,8 @@ export function configureStore(history: History) {
     const middlewares: Middleware[] = [
         routerMiddleware(history),
         thunkMiddleware,
-        projectPanelMiddleware
+        projectPanelMiddleware,
+        favoritePanelMiddleware
     ];
     const enhancer = composeEnhancers(applyMiddleware(...middlewares));
     return createStore(rootReducer, enhancer);
diff --git a/src/views-components/context-menu/context-menu.tsx b/src/views-components/context-menu/context-menu.tsx
index cc2fcb3..fe6ebd8 100644
--- a/src/views-components/context-menu/context-menu.tsx
+++ b/src/views-components/context-menu/context-menu.tsx
@@ -56,5 +56,6 @@ const getMenuActionSet = (resource?: ContextMenuResource): ContextMenuActionSet
 
 export enum ContextMenuKind {
     RootProject = "RootProject",
-    Project = "Project"
+    Project = "Project",
+    Favorite = "Favorite"
 }
diff --git a/src/views/favorite-panel/favorite-panel-item.ts b/src/views/favorite-panel/favorite-panel-item.ts
new file mode 100644
index 0000000..28f7f88
--- /dev/null
+++ b/src/views/favorite-panel/favorite-panel-item.ts
@@ -0,0 +1,31 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { GroupContentsResource } from "../../services/groups-service/groups-service";
+import { ResourceKind } from "../../models/resource";
+
+export interface FavoritePanelItem {
+    uuid: string;
+    name: string;
+    kind: string;
+    url: string;
+    owner: string;
+    lastModified: string;
+    fileSize?: number;
+    status?: string;
+}
+
+
+export function resourceToDataItem(r: GroupContentsResource): FavoritePanelItem {
+    return {
+        uuid: r.uuid,
+        name: r.name,
+        kind: r.kind,
+        url: "",
+        owner: r.ownerUuid,
+        lastModified: r.modifiedAt,
+        status:  r.kind === ResourceKind.Process ? r.state : undefined
+    };
+}
+
diff --git a/src/views/favorite-panel/favorite-panel.tsx b/src/views/favorite-panel/favorite-panel.tsx
new file mode 100644
index 0000000..4c5b5bd
--- /dev/null
+++ b/src/views/favorite-panel/favorite-panel.tsx
@@ -0,0 +1,225 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as React from 'react';
+import { FavoritePanelItem } from './favorite-panel-item';
+import { Grid, Typography, Button, StyleRulesCallback, WithStyles, withStyles } from '@material-ui/core';
+import { formatDate, formatFileSize } from '../../common/formatters';
+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 { SortDirection } from '../../components/data-table/data-column';
+import { ResourceKind } from '../../models/resource';
+import { resourceLabel } from '../../common/labels';
+import { ProjectIcon, CollectionIcon, ProcessIcon, DefaultIcon } from '../../components/icon/icon';
+import { ArvadosTheme } from '../../common/custom-theme';
+
+type CssRules = "toolbar" | "button";
+
+const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
+    toolbar: {
+        paddingBottom: theme.spacing.unit * 3,
+        textAlign: "right"
+    },
+    button: {
+        marginLeft: theme.spacing.unit
+    },
+});
+
+const renderName = (item: FavoritePanelItem) =>
+    <Grid container alignItems="center" wrap="nowrap" spacing={16}>
+        <Grid item>
+            {renderIcon(item)}
+        </Grid>
+        <Grid item>
+            <Typography color="primary">
+                {item.name}
+            </Typography>
+        </Grid>
+    </Grid>;
+
+
+const renderIcon = (item: FavoritePanelItem) => {
+    switch (item.kind) {
+        case ResourceKind.Project:
+            return <ProjectIcon />;
+        case ResourceKind.Collection:
+            return <CollectionIcon />;
+        case ResourceKind.Process:
+            return <ProcessIcon />;
+        default:
+            return <DefaultIcon />;
+    }
+};
+
+const renderDate = (date: string) => {
+    return <Typography noWrap>{formatDate(date)}</Typography>;
+};
+
+const renderFileSize = (fileSize?: number) =>
+    <Typography noWrap>
+        {formatFileSize(fileSize)}
+    </Typography>;
+
+const renderOwner = (owner: string) =>
+    <Typography noWrap color="primary" >
+        {owner}
+    </Typography>;
+
+const renderType = (type: string) =>
+    <Typography noWrap>
+        {resourceLabel(type)}
+    </Typography>;
+
+const renderStatus = (item: FavoritePanelItem) =>
+    <Typography noWrap align="center" >
+        {item.status || "-"}
+    </Typography>;
+
+export enum FavoritePanelColumnNames {
+    NAME = "Name",
+    STATUS = "Status",
+    TYPE = "Type",
+    OWNER = "Owner",
+    FILE_SIZE = "File size",
+    LAST_MODIFIED = "Last modified"
+}
+
+export interface FavoritePanelFilter extends DataTableFilterItem {
+    type: ResourceKind | ContainerRequestState;
+}
+
+export const columns: DataColumns<FavoritePanelItem, FavoritePanelFilter> = [
+    {
+        name: FavoritePanelColumnNames.NAME,
+        selected: true,
+        sortDirection: SortDirection.Asc,
+        render: renderName,
+        width: "450px"
+    },
+    {
+        name: "Status",
+        selected: true,
+        filters: [
+            {
+                name: ContainerRequestState.Committed,
+                selected: true,
+                type: ContainerRequestState.Committed
+            },
+            {
+                name: ContainerRequestState.Final,
+                selected: true,
+                type: ContainerRequestState.Final
+            },
+            {
+                name: ContainerRequestState.Uncommitted,
+                selected: true,
+                type: ContainerRequestState.Uncommitted
+            }
+        ],
+        render: renderStatus,
+        width: "75px"
+    },
+    {
+        name: FavoritePanelColumnNames.TYPE,
+        selected: true,
+        filters: [
+            {
+                name: resourceLabel(ResourceKind.Collection),
+                selected: true,
+                type: ResourceKind.Collection
+            },
+            {
+                name: resourceLabel(ResourceKind.Process),
+                selected: true,
+                type: ResourceKind.Process
+            },
+            {
+                name: resourceLabel(ResourceKind.Project),
+                selected: true,
+                type: ResourceKind.Project
+            }
+        ],
+        render: item => renderType(item.kind),
+        width: "125px"
+    },
+    {
+        name: FavoritePanelColumnNames.OWNER,
+        selected: true,
+        render: item => renderOwner(item.owner),
+        width: "200px"
+    },
+    {
+        name: FavoritePanelColumnNames.FILE_SIZE,
+        selected: true,
+        render: item => renderFileSize(item.fileSize),
+        width: "50px"
+    },
+    {
+        name: FavoritePanelColumnNames.LAST_MODIFIED,
+        selected: true,
+        sortDirection: SortDirection.None,
+        render: item => renderDate(item.lastModified),
+        width: "150px"
+    }
+];
+
+export const FAVORITE_PANEL_ID = "favoritePanel";
+
+interface FavoritePanelDataProps {
+    currentItemId: string;
+}
+
+interface FavoritePanelActionProps {
+    onItemClick: (item: FavoritePanelItem) => void;
+    onContextMenu: (event: React.MouseEvent<HTMLElement>, item: FavoritePanelItem) => void;
+    onDialogOpen: (ownerUuid: string) => void;
+    onItemDoubleClick: (item: FavoritePanelItem) => void;
+    onItemRouteChange: (itemId: string) => void;
+}
+
+type FavoritePanelProps = FavoritePanelDataProps & FavoritePanelActionProps & DispatchProp
+                        & WithStyles<CssRules> & RouteComponentProps<{ id: string }>;
+
+export const FavoritePanel = withStyles(styles)(
+    connect((state: RootState) => ({ currentItemId: state.projects.currentItemId }))(
+        class extends React.Component<FavoritePanelProps> {
+            render() {
+                const { classes } = this.props;
+                return <div>
+                    <div className={classes.toolbar}>
+                        <Button color="primary" variant="raised" className={classes.button}>
+                            Create a collection
+                        </Button>
+                        <Button color="primary" variant="raised" className={classes.button}>
+                            Run a process
+                        </Button>
+                        <Button color="primary" onClick={this.handleNewProjectClick} variant="raised" className={classes.button}>
+                            New project
+                        </Button>
+                    </div>
+                    <DataExplorer
+                        id={FAVORITE_PANEL_ID}
+                        onRowClick={this.props.onItemClick}
+                        onRowDoubleClick={this.props.onItemDoubleClick}
+                        onContextMenu={this.props.onContextMenu}
+                        extractKey={(item: FavoritePanelItem) => item.uuid} />
+                </div>;
+            }
+
+            handleNewProjectClick = () => {
+                this.props.onDialogOpen(this.props.currentItemId);
+            }
+            componentWillReceiveProps({ match, currentItemId, onItemRouteChange }: FavoritePanelProps) {
+                if (match.params.id !== currentItemId) {
+                    onItemRouteChange(match.params.id);
+                }
+            }
+        }
+    )
+);
diff --git a/src/views/workbench/workbench.tsx b/src/views/workbench/workbench.tsx
index a62b713..3fec6d6 100644
--- a/src/views/workbench/workbench.tsx
+++ b/src/views/workbench/workbench.tsx
@@ -18,7 +18,7 @@ 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 { ItemMode, setFavoriteItem, setProjectItem } from "../../store/navigation/navigation-action";
 import { projectActions } from "../../store/project/project-action";
 import { ProjectPanel } from "../project-panel/project-panel";
 import { DetailsPanel } from '../../views-components/details-panel/details-panel';
@@ -28,10 +28,11 @@ import { authService } from '../../services/services';
 
 import { detailsPanelActions, loadDetails } from "../../store/details-panel/details-panel-action";
 import { contextMenuActions } from "../../store/context-menu/context-menu-actions";
-import { SidePanelIdentifiers } from '../../store/side-panel/side-panel-reducer';
+import { sidePanelData, SidePanelIdentifiers } from '../../store/side-panel/side-panel-reducer';
 import { ProjectResource } from '../../models/project';
 import { ResourceKind } from '../../models/resource';
 import { ContextMenu, ContextMenuKind } from "../../views-components/context-menu/context-menu";
+import { FavoritePanel } from "../favorite-panel/favorite-panel";
 
 const drawerWidth = 240;
 const appBarHeight = 100;
@@ -191,6 +192,7 @@ export const Workbench = withStyles(styles)(
                             <div className={classes.content}>
                                 <Switch>
                                     <Route path="/projects/:id" render={this.renderProjectPanel} />
+                                    <Route path="/favorites" render={this.renderFavoritePanel} />
                                 </Switch>
                             </div>
                             { user && <DetailsPanel /> }
@@ -214,6 +216,19 @@ export const Workbench = withStyles(styles)(
                 }}
                 {...props} />
 
+            renderFavoritePanel = (props: RouteComponentProps<{ id: string }>) => <FavoritePanel
+                onItemRouteChange={itemId => this.props.dispatch<any>(setFavoriteItem(itemId, ItemMode.ACTIVE))}
+                onContextMenu={(event, item) => this.openContextMenu(event, item.uuid, ContextMenuKind.Favorite)}
+                onDialogOpen={this.handleCreationDialogOpen}
+                onItemClick={item => {
+                    this.props.dispatch<any>(loadDetails(item.uuid, item.kind as ResourceKind));
+                }}
+                onItemDoubleClick={item => {
+                    this.props.dispatch<any>(setFavoriteItem(item.uuid, ItemMode.ACTIVE));
+                    this.props.dispatch<any>(loadDetails(item.uuid, ResourceKind.Project));
+                }}
+                {...props} />
+
             mainAppBarActions: MainAppBarActionProps = {
                 onBreadcrumbClick: ({ itemId }: NavBreadcrumb) => {
                     this.props.dispatch<any>(setProjectItem(itemId, ItemMode.BOTH));
@@ -239,7 +254,8 @@ export const Workbench = withStyles(styles)(
             toggleSidePanelActive = (itemId: string) => {
                 this.props.dispatch(sidePanelActions.TOGGLE_SIDE_PANEL_ITEM_ACTIVE(itemId));
                 this.props.dispatch(projectActions.RESET_PROJECT_TREE_ACTIVITY(itemId));
-                this.props.dispatch(push("/"));
+                const panelItem = this.props.sidePanelItems.find(it => it.id === itemId);
+                this.props.dispatch(push(panelItem && panelItem.path ? panelItem.path : "/"));
             }
 
             handleCreationDialogOpen = (itemUuid: string) => {

-----------------------------------------------------------------------


hooks/post-receive
-- 




More information about the arvados-commits mailing list