[ARVADOS-WORKBENCH2] created: 1.3.1-453-g264be0e4

Git user git at public.curoverse.com
Mon May 13 12:12:59 UTC 2019


        at  264be0e4b5adc7bf81181505c446d83628f84bfb (commit)


commit 264be0e4b5adc7bf81181505c446d83628f84bfb
Author: Pawel Kowalczyk <pawel.kowalczyk at contractors.roche.com>
Date:   Fri May 10 12:44:35 2019 +0200

    setting-breadcrumbs
    
    Feature #15020
    
    Arvados-DCO-1.1-Signed-off-by: Pawel Kowalczyk <pawel.kowalczyk at contractors.roche.com>

diff --git a/src/store/collections-content-address-panel/collections-content-address-middleware-service.ts b/src/store/collections-content-address-panel/collections-content-address-middleware-service.ts
index fcc75340..0e35d93f 100644
--- a/src/store/collections-content-address-panel/collections-content-address-middleware-service.ts
+++ b/src/store/collections-content-address-panel/collections-content-address-middleware-service.ts
@@ -20,6 +20,7 @@ import { collectionsContentAddressActions } from './collections-content-address-
 import { navigateTo } from '~/store/navigation/navigation-action';
 import { updateFavorites } from '~/store/favorites/favorites-actions';
 import { updatePublicFavorites } from '~/store/public-favorites/public-favorites-actions';
+import { setBreadcrumbs } from '../breadcrumbs/breadcrumbs-actions';
 
 export class CollectionsWithSameContentAddressMiddlewareService extends DataExplorerMiddlewareService {
     constructor(private services: ServiceRepository, id: string) {
@@ -45,6 +46,7 @@ export class CollectionsWithSameContentAddressMiddlewareService extends DataExpl
             }
             try {
                 api.dispatch(progressIndicatorActions.START_WORKING(this.getId()));
+                const userUuid = api.getState().auth.user!.uuid;
                 const pathname = api.getState().router.location!.pathname;
                 const contentAddress = pathname.split('/')[2];
                 const response = await this.services.collectionService.list({
@@ -55,6 +57,7 @@ export class CollectionsWithSameContentAddressMiddlewareService extends DataExpl
                         .addILike("name", dataExplorer.searchValue)
                         .getFilters()
                 });
+                api.dispatch<any>(setBreadcrumbs([{ label: 'Projects', uuid: userUuid }]));
                 api.dispatch<any>(updateFavorites(response.items.map(item => item.uuid)));
                 api.dispatch<any>(updatePublicFavorites(response.items.map(item => item.uuid)));
                 if (response.itemsAvailable === 1) {

commit 9791e4a669e0573f155a9785de550e9cb8520296
Author: Pawel Kowalczyk <pawel.kowalczyk at contractors.roche.com>
Date:   Fri May 10 12:33:47 2019 +0200

    fixed-bug-when-navigating-not-rfom-collections
    
    Feature #15020
    
    Arvados-DCO-1.1-Signed-off-by: Pawel Kowalczyk <pawel.kowalczyk at contractors.roche.com>

diff --git a/src/components/details-attribute/details-attribute.tsx b/src/components/details-attribute/details-attribute.tsx
index b8955d4e..3586d22d 100644
--- a/src/components/details-attribute/details-attribute.tsx
+++ b/src/components/details-attribute/details-attribute.tsx
@@ -60,7 +60,7 @@ export const DetailsAttribute = withStyles(styles)(
         <Typography component="div" className={classes.attribute}>
             <Typography component="span" className={classnames([classes.label, classLabel])}>{label}</Typography>
             {link && <a href={link} className={classes.link} target='_blank'>{value}</a>}
-            {linkInsideCard && <Link to={linkInsideCard} className={classes.link}>{value}</Link>}
+            {linkInsideCard && <Link to={`/collections/${linkInsideCard}`} className={classes.link}>{value}</Link>}
             {!link && !linkInsideCard && <Typography
                 onClick={onValueClick}
                 component="span"

commit a6ca8c85942e16d50fe32cbba5f5b1e32a480ec5
Author: Pawel Kowalczyk <pawel.kowalczyk at contractors.roche.com>
Date:   Fri May 10 12:22:20 2019 +0200

    title-for-data-table
    
    Feature #15020
    
    Arvados-DCO-1.1-Signed-off-by: Pawel Kowalczyk <pawel.kowalczyk at contractors.roche.com>

diff --git a/src/components/data-explorer/data-explorer.tsx b/src/components/data-explorer/data-explorer.tsx
index 7c1f9045..5f019577 100644
--- a/src/components/data-explorer/data-explorer.tsx
+++ b/src/components/data-explorer/data-explorer.tsx
@@ -14,7 +14,7 @@ import { DataTableFilters } from '~/components/data-table-filters/data-table-fil
 import { MoreOptionsIcon } from '~/components/icon/icon';
 import { PaperProps } from '@material-ui/core/Paper';
 
-type CssRules = 'searchBox' | "toolbar" | "footer" | "root" | 'moreOptionsButton';
+type CssRules = 'searchBox' | "toolbar" | "footer" | "root" | 'moreOptionsButton' | 'title';
 
 const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
     searchBox: {
@@ -31,6 +31,11 @@ const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
     },
     moreOptionsButton: {
         padding: 0
+    },
+    title: {
+        paddingLeft: theme.spacing.unit * 3,
+        paddingTop: theme.spacing.unit * 3,
+        fontSize: '18px'
     }
 });
 
@@ -52,6 +57,7 @@ interface DataExplorerDataProps<T> {
     hideSearchInput?: boolean;
     paperKey?: string;
     currentItemUuid: string;
+    title?: string;
 }
 
 interface DataExplorerActionProps<T> {
@@ -84,9 +90,10 @@ export const DataExplorer = withStyles(styles)(
                 rowsPerPage, rowsPerPageOptions, onColumnToggle, searchValue, onSearch,
                 items, itemsAvailable, onRowClick, onRowDoubleClick, classes,
                 dataTableDefaultView, hideColumnSelector, actions, paperProps, hideSearchInput,
-                paperKey, fetchMode, currentItemUuid
+                paperKey, fetchMode, currentItemUuid, title
             } = this.props;
             return <Paper className={classes.root} {...paperProps} key={paperKey}>
+                {title ? <div className={classes.title}>Content Address: {title}</div> : null}
                 {(!hideColumnSelector || !hideSearchInput) && <Toolbar className={classes.toolbar}>
                     <Grid container justify="space-between" wrap="nowrap" alignItems="center">
                         <div className={classes.searchBox}>
diff --git a/src/views/collection-content-address-panel/collection-content-address-panel.tsx b/src/views/collection-content-address-panel/collection-content-address-panel.tsx
index f2c6df34..dfe1b5bf 100644
--- a/src/views/collection-content-address-panel/collection-content-address-panel.tsx
+++ b/src/views/collection-content-address-panel/collection-content-address-panel.tsx
@@ -121,9 +121,15 @@ const mapDispatchToProps = (dispatch: Dispatch): CollectionContentAddressPanelAc
     }
 });
 
+interface CollectionContentAddressDataProps {
+    match: {
+        params: { id: string }
+    };
+}
+
 export const CollectionsContentAddressPanel = withStyles(styles)(
     connect(null, mapDispatchToProps)(
-        class extends React.Component<CollectionContentAddressPanelActionProps & WithStyles<CssRules>> {
+        class extends React.Component<CollectionContentAddressPanelActionProps & CollectionContentAddressDataProps & WithStyles<CssRules>> {
             render() {
                 return <Grid item xs={12}>
                     {/* <Link to={`/collections/${collectionUuid}`} className={this.props.classes.backLink}>
@@ -136,6 +142,7 @@ export const CollectionsContentAddressPanel = withStyles(styles)(
                         onRowDoubleClick={this.props.onItemDoubleClick}
                         onContextMenu={this.props.onContextMenu}
                         contextMenuColumn={true}
+                        title={this.props.match.params.id}
                         dataTableDefaultView={
                             <DataTableDefaultView
                                 icon={CollectionIcon}

commit 600473ba57f6a25281d1d2094eab2ad51bfd9a4b
Author: Pawel Kowalczyk <pawel.kowalczyk at contractors.roche.com>
Date:   Fri May 10 11:55:58 2019 +0200

    search-for-data-table-and-small-improvements
    
    Feature #15020
    
    Arvados-DCO-1.1-Signed-off-by: Pawel Kowalczyk <pawel.kowalczyk at contractors.roche.com>

diff --git a/src/store/collections-content-address-panel/collections-content-address-middleware-service.ts b/src/store/collections-content-address-panel/collections-content-address-middleware-service.ts
index c1893f6d..fcc75340 100644
--- a/src/store/collections-content-address-panel/collections-content-address-middleware-service.ts
+++ b/src/store/collections-content-address-panel/collections-content-address-middleware-service.ts
@@ -18,6 +18,8 @@ import { GroupContentsResource, GroupContentsResourcePrefix } from '~/services/g
 import { progressIndicatorActions } from '~/store/progress-indicator/progress-indicator-actions';
 import { collectionsContentAddressActions } from './collections-content-address-panel-actions';
 import { navigateTo } from '~/store/navigation/navigation-action';
+import { updateFavorites } from '~/store/favorites/favorites-actions';
+import { updatePublicFavorites } from '~/store/public-favorites/public-favorites-actions';
 
 export class CollectionsWithSameContentAddressMiddlewareService extends DataExplorerMiddlewareService {
     constructor(private services: ServiceRepository, id: string) {
@@ -50,8 +52,11 @@ export class CollectionsWithSameContentAddressMiddlewareService extends DataExpl
                     offset: dataExplorer.page * dataExplorer.rowsPerPage,
                     filters: new FilterBuilder()
                         .addEqual('portableDataHash', contentAddress)
+                        .addILike("name", dataExplorer.searchValue)
                         .getFilters()
                 });
+                api.dispatch<any>(updateFavorites(response.items.map(item => item.uuid)));
+                api.dispatch<any>(updatePublicFavorites(response.items.map(item => item.uuid)));
                 if (response.itemsAvailable === 1) {
                     api.dispatch<any>(navigateTo(response.items[0].uuid));
                     api.dispatch(progressIndicatorActions.PERSIST_STOP_WORKING(this.getId()));
diff --git a/src/views/collection-content-address-panel/collection-content-address-panel.tsx b/src/views/collection-content-address-panel/collection-content-address-panel.tsx
index 5ca392fe..f2c6df34 100644
--- a/src/views/collection-content-address-panel/collection-content-address-panel.tsx
+++ b/src/views/collection-content-address-panel/collection-content-address-panel.tsx
@@ -62,7 +62,7 @@ const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
 
 enum CollectionContentAddressPanelColumnNames {
     COLLECTION_WITH_THIS_ADDRESS = "Collection with this address",
-    OWNER = "Owner",
+    LOCATION = "Location",
     LAST_MODIFIED = "Last modified"
 }
 
@@ -76,7 +76,7 @@ export const collectionContentAddressPanelColumns: DataColumns<string> = [
         render: uuid => <ResourceName uuid={uuid} />
     },
     {
-        name: CollectionContentAddressPanelColumnNames.OWNER,
+        name: CollectionContentAddressPanelColumnNames.LOCATION,
         selected: true,
         configurable: true,
         filters: createTree(),
@@ -92,13 +92,13 @@ export const collectionContentAddressPanelColumns: DataColumns<string> = [
     }
 ];
 
-export interface CollectionContentAddressMainCardActionProps {
+export interface CollectionContentAddressPanelActionProps {
     onContextMenu: (event: React.MouseEvent<any>, uuid: string) => void;
     onItemClick: (item: string) => void;
     onItemDoubleClick: (item: string) => void;
 }
 
-const mapDispatchToProps = (dispatch: Dispatch): CollectionContentAddressMainCardActionProps => ({
+const mapDispatchToProps = (dispatch: Dispatch): CollectionContentAddressPanelActionProps => ({
     onContextMenu: (event, resourceUuid) => {
         const isAdmin = dispatch<any>(getIsAdmin());
         const kind = resourceKindToContextMenuKind(resourceUuid, isAdmin);
@@ -123,12 +123,12 @@ const mapDispatchToProps = (dispatch: Dispatch): CollectionContentAddressMainCar
 
 export const CollectionsContentAddressPanel = withStyles(styles)(
     connect(null, mapDispatchToProps)(
-        class extends React.Component<CollectionContentAddressMainCardActionProps & WithStyles<CssRules>> {
+        class extends React.Component<CollectionContentAddressPanelActionProps & WithStyles<CssRules>> {
             render() {
                 return <Grid item xs={12}>
-                    {/* <Link to={`/collections/${this.props.collection.uuid}`} className={this.props.classes.backLink}>
+                    {/* <Link to={`/collections/${collectionUuid}`} className={this.props.classes.backLink}>
                         <BackIcon className={this.props.classes.backIcon} />
-                        Back test
+                        Back
                     </Link> */}
                     <DataExplorer
                         id={COLLECTIONS_CONTENT_ADDRESS_PANEL_ID}

commit 98d1b780b15d9995ec5ef6cae25c0663702ede4f
Author: Pawel Kowalczyk <pawel.kowalczyk at contractors.roche.com>
Date:   Wed May 8 14:14:35 2019 +0200

    data-table-columns-change
    
    Feature #15020
    
    Arvados-DCO-1.1-Signed-off-by: Pawel Kowalczyk <pawel.kowalczyk at contractors.roche.com>

diff --git a/src/store/workbench/workbench-actions.ts b/src/store/workbench/workbench-actions.ts
index 25852cbf..2363b579 100644
--- a/src/store/workbench/workbench-actions.ts
+++ b/src/store/workbench/workbench-actions.ts
@@ -94,6 +94,7 @@ import { DataTableFetchMode } from "~/components/data-table/data-table";
 import { loadPublicFavoritePanel, publicFavoritePanelActions } from '~/store/public-favorites-panel/public-favorites-action';
 import { publicFavoritePanelColumns } from '~/views/public-favorites-panel/public-favorites-panel';
 import { loadCollectionsContentAddressPanel, collectionsContentAddressActions } from '~/store/collections-content-address-panel/collections-content-address-panel-actions';
+import { collectionContentAddressPanelColumns } from '~/views/collection-content-address-panel/collection-content-address-panel';
 
 export const WORKBENCH_LOADING_SCREEN = 'workbenchLoadingScreen';
 
@@ -133,7 +134,7 @@ export const loadWorkbench = () =>
             dispatch(linkPanelActions.SET_COLUMNS({ columns: linkPanelColumns }));
             dispatch(computeNodesActions.SET_COLUMNS({ columns: computeNodePanelColumns }));
             dispatch(apiClientAuthorizationsActions.SET_COLUMNS({ columns: apiClientAuthorizationPanelColumns }));
-            dispatch(collectionsContentAddressActions.SET_COLUMNS({ columns: projectPanelColumns }));
+            dispatch(collectionsContentAddressActions.SET_COLUMNS({ columns: collectionContentAddressPanelColumns }));
 
             dispatch<any>(initSidePanelTree());
             if (router.location) {
diff --git a/src/views-components/data-explorer/renderers.tsx b/src/views-components/data-explorer/renderers.tsx
index 5dc1e02a..52760cb4 100644
--- a/src/views-components/data-explorer/renderers.tsx
+++ b/src/views-components/data-explorer/renderers.tsx
@@ -26,6 +26,7 @@ import { toggleIsActive, toggleIsAdmin } from '~/store/users/users-actions';
 import { LinkResource } from '~/models/link';
 import { navigateTo } from '~/store/navigation/navigation-action';
 import { withResourceData } from '~/views-components/data-explorer/with-resources';
+import { extractUuidKind } from '~/models/resource';
 
 const renderName = (item: { name: string; uuid: string, kind: string }) =>
     <Grid container alignItems="center" wrap="nowrap" spacing={16}>
diff --git a/src/views/collection-content-address-panel/collection-content-address-panel.tsx b/src/views/collection-content-address-panel/collection-content-address-panel.tsx
index 009e16c7..5ca392fe 100644
--- a/src/views/collection-content-address-panel/collection-content-address-panel.tsx
+++ b/src/views/collection-content-address-panel/collection-content-address-panel.tsx
@@ -10,7 +10,6 @@ import {
 import { CollectionIcon } from '~/components/icon/icon';
 import { ArvadosTheme } from '~/common/custom-theme';
 import { BackIcon } from '~/components/icon/icon';
-import { CollectionResource } from '~/models/collection';
 import { DataTableDefaultView } from '~/components/data-table-default-view/data-table-default-view';
 import { COLLECTIONS_CONTENT_ADDRESS_PANEL_ID } from '~/store/collections-content-address-panel/collections-content-address-panel-actions';
 import { DataExplorer } from "~/views-components/data-explorer/data-explorer";
@@ -19,8 +18,12 @@ import { getIsAdmin } from '~/store/public-favorites/public-favorites-actions';
 import { resourceKindToContextMenuKind, openContextMenu } from '~/store/context-menu/context-menu-actions';
 import { ResourceKind } from '~/models/resource';
 import { loadDetailsPanel } from '~/store/details-panel/details-panel-action';
-import { connect, DispatchProp } from 'react-redux';
+import { connect } from 'react-redux';
 import { navigateTo } from '~/store/navigation/navigation-action';
+import { DataColumns } from '~/components/data-table/data-table';
+import { SortDirection } from '~/components/data-table/data-column';
+import { createTree } from '~/models/tree';
+import { ResourceName, ResourceOwner, ResourceLastModifiedDate } from '~/views-components/data-explorer/renderers';
 
 type CssRules = 'backLink' | 'backIcon' | 'card' | 'title' | 'iconHeader' | 'link';
 
@@ -57,6 +60,37 @@ const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
     }
 });
 
+enum CollectionContentAddressPanelColumnNames {
+    COLLECTION_WITH_THIS_ADDRESS = "Collection with this address",
+    OWNER = "Owner",
+    LAST_MODIFIED = "Last modified"
+}
+
+export const collectionContentAddressPanelColumns: DataColumns<string> = [
+    {
+        name: CollectionContentAddressPanelColumnNames.COLLECTION_WITH_THIS_ADDRESS,
+        selected: true,
+        configurable: true,
+        sortDirection: SortDirection.NONE,
+        filters: createTree(),
+        render: uuid => <ResourceName uuid={uuid} />
+    },
+    {
+        name: CollectionContentAddressPanelColumnNames.OWNER,
+        selected: true,
+        configurable: true,
+        filters: createTree(),
+        render: uuid => <ResourceOwner uuid={uuid} />
+    },
+    {
+        name: CollectionContentAddressPanelColumnNames.LAST_MODIFIED,
+        selected: true,
+        configurable: true,
+        sortDirection: SortDirection.DESC,
+        filters: createTree(),
+        render: uuid => <ResourceLastModifiedDate uuid={uuid} />
+    }
+];
 
 export interface CollectionContentAddressMainCardActionProps {
     onContextMenu: (event: React.MouseEvent<any>, uuid: string) => void;

commit 59995a3eed71636c7b3be3729a41d8a90ee2486c
Author: Pawel Kowalczyk <pawel.kowalczyk at contractors.roche.com>
Date:   Wed May 8 13:34:07 2019 +0200

    navigate-to-collection-when-there-is-only-one-collection-with-given-content-address
    
    Feature #15020
    
    Arvados-DCO-1.1-Signed-off-by: Pawel Kowalczyk <pawel.kowalczyk at contractors.roche.com>

diff --git a/src/store/collections-content-address-panel/collections-content-address-middleware-service.ts b/src/store/collections-content-address-panel/collections-content-address-middleware-service.ts
index 53c45d57..c1893f6d 100644
--- a/src/store/collections-content-address-panel/collections-content-address-middleware-service.ts
+++ b/src/store/collections-content-address-panel/collections-content-address-middleware-service.ts
@@ -17,6 +17,7 @@ import { FavoritePanelColumnNames } from '~/views/favorite-panel/favorite-panel'
 import { GroupContentsResource, GroupContentsResourcePrefix } from '~/services/groups-service/groups-service';
 import { progressIndicatorActions } from '~/store/progress-indicator/progress-indicator-actions';
 import { collectionsContentAddressActions } from './collections-content-address-panel-actions';
+import { navigateTo } from '~/store/navigation/navigation-action';
 
 export class CollectionsWithSameContentAddressMiddlewareService extends DataExplorerMiddlewareService {
     constructor(private services: ServiceRepository, id: string) {
@@ -51,14 +52,19 @@ export class CollectionsWithSameContentAddressMiddlewareService extends DataExpl
                         .addEqual('portableDataHash', contentAddress)
                         .getFilters()
                 });
-                api.dispatch(progressIndicatorActions.PERSIST_STOP_WORKING(this.getId()));
-                api.dispatch(resourcesActions.SET_RESOURCES(response.items));
-                api.dispatch(collectionsContentAddressActions.SET_ITEMS({
-                    items: response.items.map((resource: any) => resource.uuid),
-                    itemsAvailable: response.itemsAvailable,
-                    page: Math.floor(response.offset / response.limit),
-                    rowsPerPage: response.limit
-                }));
+                if (response.itemsAvailable === 1) {
+                    api.dispatch<any>(navigateTo(response.items[0].uuid));
+                    api.dispatch(progressIndicatorActions.PERSIST_STOP_WORKING(this.getId()));
+                } else {
+                    api.dispatch(progressIndicatorActions.PERSIST_STOP_WORKING(this.getId()));
+                    api.dispatch(resourcesActions.SET_RESOURCES(response.items));
+                    api.dispatch(collectionsContentAddressActions.SET_ITEMS({
+                        items: response.items.map((resource: any) => resource.uuid),
+                        itemsAvailable: response.itemsAvailable,
+                        page: Math.floor(response.offset / response.limit),
+                        rowsPerPage: response.limit
+                    }));
+                }
             } catch (e) {
                 api.dispatch(progressIndicatorActions.PERSIST_STOP_WORKING(this.getId()));
                 api.dispatch(collectionsContentAddressActions.SET_ITEMS({

commit 2d9dcf3d61d410328e081b5b00c7175c7eb1d82b
Author: Pawel Kowalczyk <pawel.kowalczyk at contractors.roche.com>
Date:   Wed May 8 13:27:55 2019 +0200

    routing+init-data-explorer
    
    Feature #15020
    
    Arvados-DCO-1.1-Signed-off-by: Pawel Kowalczyk <pawel.kowalczyk at contractors.roche.com>

diff --git a/src/components/details-attribute/details-attribute.tsx b/src/components/details-attribute/details-attribute.tsx
index d255d14b..b8955d4e 100644
--- a/src/components/details-attribute/details-attribute.tsx
+++ b/src/components/details-attribute/details-attribute.tsx
@@ -7,6 +7,7 @@ import Typography from '@material-ui/core/Typography';
 import { StyleRulesCallback, WithStyles, withStyles } from '@material-ui/core/styles';
 import { ArvadosTheme } from '~/common/custom-theme';
 import * as classnames from "classnames";
+import { Link } from 'react-router-dom';
 
 type CssRules = 'attribute' | 'label' | 'value' | 'lowercaseValue' | 'link';
 
@@ -49,23 +50,25 @@ interface DetailsAttributeDataProps {
     link?: string;
     children?: React.ReactNode;
     onValueClick?: () => void;
+    linkInsideCard?: string;
 }
 
 type DetailsAttributeProps = DetailsAttributeDataProps & WithStyles<CssRules>;
 
 export const DetailsAttribute = withStyles(styles)(
-    ({ label, link, value, children, classes, classLabel, classValue, lowercaseValue, onValueClick }: DetailsAttributeProps) =>
+    ({ label, link, value, children, classes, classLabel, classValue, lowercaseValue, onValueClick, linkInsideCard }: DetailsAttributeProps) =>
         <Typography component="div" className={classes.attribute}>
             <Typography component="span" className={classnames([classes.label, classLabel])}>{label}</Typography>
-            { link
-                ? <a href={link} className={classes.link} target='_blank'>{value}</a>
-                : <Typography
-                    onClick={onValueClick}
-                    component="span"
-                    className={classnames([classes.value, classValue, { [classes.lowercaseValue]: lowercaseValue }])}
-                >
-                    {value}
-                    {children}
-                </Typography> }
+            {link && <a href={link} className={classes.link} target='_blank'>{value}</a>}
+            {linkInsideCard && <Link to={linkInsideCard} className={classes.link}>{value}</Link>}
+            {!link && !linkInsideCard && <Typography
+                onClick={onValueClick}
+                component="span"
+                className={classnames([classes.value, classValue, { [classes.lowercaseValue]: lowercaseValue }])}>
+                {value}
+                {children}
+            </Typography>
+            }
+
         </Typography>
 );
diff --git a/src/routes/route-change-handlers.ts b/src/routes/route-change-handlers.ts
index 2811f95a..50ba319e 100644
--- a/src/routes/route-change-handlers.ts
+++ b/src/routes/route-change-handlers.ts
@@ -23,7 +23,7 @@ const handleLocationChange = (store: RootStore) => ({ pathname }: Location) => {
     const projectMatch = Routes.matchProjectRoute(pathname);
     const collectionMatch = Routes.matchCollectionRoute(pathname);
     const favoriteMatch = Routes.matchFavoritesRoute(pathname);
-    const publicFavoritesMatch = Routes.matchPublicFavorites(pathname);
+    const publicFavoritesMatch = Routes.matchPublicFavoritesRoute(pathname);
     const trashMatch = Routes.matchTrashRoute(pathname);
     const processMatch = Routes.matchProcessRoute(pathname);
     const processLogMatch = Routes.matchProcessLogRoute(pathname);
@@ -45,6 +45,7 @@ const handleLocationChange = (store: RootStore) => ({ pathname }: Location) => {
     const groupsMatch = Routes.matchGroupsRoute(pathname);
     const groupDetailsMatch = Routes.matchGroupDetailsRoute(pathname);
     const linksMatch = Routes.matchLinksRoute(pathname);
+    const collectionsContentAddressMatch = Routes.matchCollectionsContentAddressRoute(pathname);
 
     store.dispatch(dialogActions.CLOSE_ALL_DIALOGS());
     store.dispatch(contextMenuActions.CLOSE_CONTEXT_MENU());
@@ -102,5 +103,7 @@ const handleLocationChange = (store: RootStore) => ({ pathname }: Location) => {
         store.dispatch(WorkbenchActions.loadGroupDetailsPanel(groupDetailsMatch.params.id));
     } else if (linksMatch) {
         store.dispatch(WorkbenchActions.loadLinks);
+    } else if (collectionsContentAddressMatch) {
+        store.dispatch(WorkbenchActions.loadCollectionContentAddress);
     }
 };
\ No newline at end of file
diff --git a/src/routes/routes.ts b/src/routes/routes.ts
index 3fd6670d..dd09d8ec 100644
--- a/src/routes/routes.ts
+++ b/src/routes/routes.ts
@@ -34,7 +34,8 @@ export const Routes = {
     GROUPS: '/groups',
     GROUP_DETAILS: `/group/:id(${RESOURCE_UUID_PATTERN})`,
     LINKS: '/links',
-    PUBLIC_FAVORITES: '/public-favorites'
+    PUBLIC_FAVORITES: '/public-favorites',
+    COLLECTIONS_CONTENT_ADDRESS: '/collections/:id',
 };
 
 export const getResourceUrl = (uuid: string) => {
@@ -136,5 +137,8 @@ export const matchGroupDetailsRoute = (route: string) =>
 export const matchLinksRoute = (route: string) =>
     matchPath(route, { path: Routes.LINKS });
 
-export const matchPublicFavorites = (route: string) =>
+export const matchPublicFavoritesRoute = (route: string) =>
     matchPath(route, { path: Routes.PUBLIC_FAVORITES });
+
+export const matchCollectionsContentAddressRoute = (route: string) =>
+    matchPath(route, { path: Routes.COLLECTIONS_CONTENT_ADDRESS });
diff --git a/src/store/collections-content-address-panel/collections-content-address-middleware-service.ts b/src/store/collections-content-address-panel/collections-content-address-middleware-service.ts
new file mode 100644
index 00000000..53c45d57
--- /dev/null
+++ b/src/store/collections-content-address-panel/collections-content-address-middleware-service.ts
@@ -0,0 +1,86 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { ServiceRepository } from '~/services/services';
+import { MiddlewareAPI, Dispatch } from 'redux';
+import { DataExplorerMiddlewareService } from '~/store/data-explorer/data-explorer-middleware-service';
+import { RootState } from '~/store/store';
+import { snackbarActions, SnackbarKind } from '~/store/snackbar/snackbar-actions';
+import { getDataExplorer } from '~/store/data-explorer/data-explorer-reducer';
+import { resourcesActions } from '~/store/resources/resources-actions';
+import { FilterBuilder } from '~/services/api/filter-builder';
+import { SortDirection } from '~/components/data-table/data-column';
+import { OrderDirection, OrderBuilder } from '~/services/api/order-builder';
+import { getSortColumn } from "~/store/data-explorer/data-explorer-reducer";
+import { FavoritePanelColumnNames } from '~/views/favorite-panel/favorite-panel';
+import { GroupContentsResource, GroupContentsResourcePrefix } from '~/services/groups-service/groups-service';
+import { progressIndicatorActions } from '~/store/progress-indicator/progress-indicator-actions';
+import { collectionsContentAddressActions } from './collections-content-address-panel-actions';
+
+export class CollectionsWithSameContentAddressMiddlewareService extends DataExplorerMiddlewareService {
+    constructor(private services: ServiceRepository, id: string) {
+        super(id);
+    }
+
+    async requestItems(api: MiddlewareAPI<Dispatch, RootState>) {
+        const dataExplorer = getDataExplorer(api.getState().dataExplorer, this.getId());
+        if (!dataExplorer) {
+            api.dispatch(collectionPanelDataExplorerIsNotSet());
+        } else {
+            const sortColumn = getSortColumn(dataExplorer);
+
+            const contentOrder = new OrderBuilder<GroupContentsResource>();
+
+            if (sortColumn && sortColumn.name === FavoritePanelColumnNames.NAME) {
+                const direction = sortColumn.sortDirection === SortDirection.ASC
+                    ? OrderDirection.ASC
+                    : OrderDirection.DESC;
+
+                contentOrder
+                    .addOrder(direction, "name", GroupContentsResourcePrefix.COLLECTION);
+            }
+            try {
+                api.dispatch(progressIndicatorActions.START_WORKING(this.getId()));
+                const pathname = api.getState().router.location!.pathname;
+                const contentAddress = pathname.split('/')[2];
+                const response = await this.services.collectionService.list({
+                    limit: dataExplorer.rowsPerPage,
+                    offset: dataExplorer.page * dataExplorer.rowsPerPage,
+                    filters: new FilterBuilder()
+                        .addEqual('portableDataHash', contentAddress)
+                        .getFilters()
+                });
+                api.dispatch(progressIndicatorActions.PERSIST_STOP_WORKING(this.getId()));
+                api.dispatch(resourcesActions.SET_RESOURCES(response.items));
+                api.dispatch(collectionsContentAddressActions.SET_ITEMS({
+                    items: response.items.map((resource: any) => resource.uuid),
+                    itemsAvailable: response.itemsAvailable,
+                    page: Math.floor(response.offset / response.limit),
+                    rowsPerPage: response.limit
+                }));
+            } catch (e) {
+                api.dispatch(progressIndicatorActions.PERSIST_STOP_WORKING(this.getId()));
+                api.dispatch(collectionsContentAddressActions.SET_ITEMS({
+                    items: [],
+                    itemsAvailable: 0,
+                    page: 0,
+                    rowsPerPage: dataExplorer.rowsPerPage
+                }));
+                api.dispatch(couldNotFetchCollections());
+            }
+        }
+    }
+}
+
+const collectionPanelDataExplorerIsNotSet = () =>
+    snackbarActions.OPEN_SNACKBAR({
+        message: 'Collection panel is not ready.',
+        kind: SnackbarKind.ERROR
+    });
+
+const couldNotFetchCollections = () =>
+    snackbarActions.OPEN_SNACKBAR({
+        message: 'Could not fetch collection with this content address.',
+        kind: SnackbarKind.ERROR
+    });
\ No newline at end of file
diff --git a/src/store/collections-content-address-panel/collections-content-address-panel-actions.ts b/src/store/collections-content-address-panel/collections-content-address-panel-actions.ts
new file mode 100644
index 00000000..11f1a8cc
--- /dev/null
+++ b/src/store/collections-content-address-panel/collections-content-address-panel-actions.ts
@@ -0,0 +1,15 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { Dispatch } from "redux";
+import { bindDataExplorerActions } from '~/store/data-explorer/data-explorer-action';
+
+export const COLLECTIONS_CONTENT_ADDRESS_PANEL_ID = 'collectionsContentAddressPanel';
+
+export const collectionsContentAddressActions = bindDataExplorerActions(COLLECTIONS_CONTENT_ADDRESS_PANEL_ID);
+
+export const loadCollectionsContentAddressPanel = () =>
+    (dispatch: Dispatch) => {
+        dispatch(collectionsContentAddressActions.REQUEST_ITEMS());
+    };
diff --git a/src/store/navigation/navigation-action.ts b/src/store/navigation/navigation-action.ts
index af7a0c03..d7ad0178 100644
--- a/src/store/navigation/navigation-action.ts
+++ b/src/store/navigation/navigation-action.ts
@@ -98,3 +98,5 @@ export const navigateToGroups = push(Routes.GROUPS);
 export const navigateToGroupDetails = compose(push, getGroupUrl);
 
 export const navigateToLinks = push(Routes.LINKS);
+
+export const navigateToCollectionsContentAddress = push(Routes.COLLECTIONS_CONTENT_ADDRESS);
diff --git a/src/store/store.ts b/src/store/store.ts
index 9d7dcdd4..eff2454a 100644
--- a/src/store/store.ts
+++ b/src/store/store.ts
@@ -62,6 +62,8 @@ import { ApiClientAuthorizationMiddlewareService } from '~/store/api-client-auth
 import { PublicFavoritesMiddlewareService } from '~/store/public-favorites-panel/public-favorites-middleware-service';
 import { PUBLIC_FAVORITE_PANEL_ID } from '~/store/public-favorites-panel/public-favorites-action';
 import { publicFavoritesReducer } from '~/store/public-favorites/public-favorites-reducer';
+import { CollectionsWithSameContentAddressMiddlewareService } from '~/store/collections-content-address-panel/collections-content-address-middleware-service';
+import { COLLECTIONS_CONTENT_ADDRESS_PANEL_ID } from '~/store/collections-content-address-panel/collections-content-address-panel-actions';
 
 const composeEnhancers =
     (process.env.NODE_ENV === 'development' &&
@@ -115,6 +117,10 @@ export function configureStore(history: History, services: ServiceRepository): R
     const publicFavoritesMiddleware = dataExplorerMiddleware(
         new PublicFavoritesMiddlewareService(services, PUBLIC_FAVORITE_PANEL_ID)
     );
+    const collectionsContentAddress = dataExplorerMiddleware(
+        new CollectionsWithSameContentAddressMiddlewareService(services, COLLECTIONS_CONTENT_ADDRESS_PANEL_ID)
+    );
+
     const middlewares: Middleware[] = [
         routerMiddleware(history),
         thunkMiddleware.withExtraArgument(services),
@@ -130,7 +136,8 @@ export function configureStore(history: History, services: ServiceRepository): R
         linkPanelMiddleware,
         computeNodeMiddleware,
         apiClientAuthorizationMiddlewareService,
-        publicFavoritesMiddleware
+        publicFavoritesMiddleware,
+        collectionsContentAddress
     ];
     const enhancer = composeEnhancers(applyMiddleware(...middlewares));
     return createStore(rootReducer, enhancer);
diff --git a/src/store/workbench/workbench-actions.ts b/src/store/workbench/workbench-actions.ts
index 378bb8de..25852cbf 100644
--- a/src/store/workbench/workbench-actions.ts
+++ b/src/store/workbench/workbench-actions.ts
@@ -93,6 +93,7 @@ import { groupDetailsPanelColumns } from '~/views/group-details-panel/group-deta
 import { DataTableFetchMode } from "~/components/data-table/data-table";
 import { loadPublicFavoritePanel, publicFavoritePanelActions } from '~/store/public-favorites-panel/public-favorites-action';
 import { publicFavoritePanelColumns } from '~/views/public-favorites-panel/public-favorites-panel';
+import { loadCollectionsContentAddressPanel, collectionsContentAddressActions } from '~/store/collections-content-address-panel/collections-content-address-panel-actions';
 
 export const WORKBENCH_LOADING_SCREEN = 'workbenchLoadingScreen';
 
@@ -132,6 +133,7 @@ export const loadWorkbench = () =>
             dispatch(linkPanelActions.SET_COLUMNS({ columns: linkPanelColumns }));
             dispatch(computeNodesActions.SET_COLUMNS({ columns: computeNodePanelColumns }));
             dispatch(apiClientAuthorizationsActions.SET_COLUMNS({ columns: apiClientAuthorizationPanelColumns }));
+            dispatch(collectionsContentAddressActions.SET_COLUMNS({ columns: projectPanelColumns }));
 
             dispatch<any>(initSidePanelTree());
             if (router.location) {
@@ -153,6 +155,11 @@ export const loadFavorites = () =>
             dispatch<any>(setSidePanelBreadcrumbs(SidePanelTreeCategory.FAVORITES));
         });
 
+export const loadCollectionContentAddress = handleFirstTimeLoad(
+    async (dispatch: Dispatch<any>) => {
+        await dispatch(loadCollectionsContentAddressPanel());
+    });
+
 export const loadTrash = () =>
     handleFirstTimeLoad(
         (dispatch: Dispatch) => {
@@ -255,7 +262,7 @@ export const loadCollection = (uuid: string) =>
                         dispatch(collectionPanelActions.SET_COLLECTION(collection as CollectionResource));
                         dispatch(updateResources([collection]));
                         await dispatch(activateSidePanelTreeItem(collection.ownerUuid));
-                        dispatch(setSidePanelBreadcrumbs(collection.ownerUuid));              
+                        dispatch(setSidePanelBreadcrumbs(collection.ownerUuid));
                         dispatch(loadCollectionPanel(collection.uuid));
                     },
                     SHARED: collection => {
diff --git a/src/views-components/details-panel/collection-details.tsx b/src/views-components/details-panel/collection-details.tsx
index 01b867b3..b47e9edf 100644
--- a/src/views-components/details-panel/collection-details.tsx
+++ b/src/views-components/details-panel/collection-details.tsx
@@ -25,8 +25,8 @@ export class CollectionDetails extends DetailsData<CollectionResource> {
             <DetailsAttribute label='Last modified' value={formatDate(this.item.modifiedAt)} />
             <DetailsAttribute label='Created at' value={formatDate(this.item.createdAt)} />
             {/* Links but we dont have view */}
-            <DetailsAttribute label='Collection UUID' link={this.item.uuid} value={this.item.uuid} />
-            <DetailsAttribute label='Content address' link={this.numberOfCollectionsByPDH === 1 ? this.item.uuid : this.item.portableDataHash} value={this.item.portableDataHash} />
+            <DetailsAttribute label='Collection UUID' linkInsideCard={this.item.uuid} value={this.item.uuid} />
+            <DetailsAttribute label='Content address' linkInsideCard={this.numberOfCollectionsByPDH === 1 ? this.item.uuid : this.item.portableDataHash} value={this.item.portableDataHash} />
             {/* Missing attrs */}
             <DetailsAttribute label='Number of files' value={this.data && this.data.fileCount} />
             <DetailsAttribute label='Content size' value={formatFileSize(this.data && this.data.fileSize)} />
diff --git a/src/views/collection-content-address-panel/collection-content-address-panel.tsx b/src/views/collection-content-address-panel/collection-content-address-panel.tsx
new file mode 100644
index 00000000..009e16c7
--- /dev/null
+++ b/src/views/collection-content-address-panel/collection-content-address-panel.tsx
@@ -0,0 +1,114 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as React from 'react';
+import { Link } from 'react-router-dom';
+import {
+    StyleRulesCallback, WithStyles, withStyles, Grid
+} from '@material-ui/core';
+import { CollectionIcon } from '~/components/icon/icon';
+import { ArvadosTheme } from '~/common/custom-theme';
+import { BackIcon } from '~/components/icon/icon';
+import { CollectionResource } from '~/models/collection';
+import { DataTableDefaultView } from '~/components/data-table-default-view/data-table-default-view';
+import { COLLECTIONS_CONTENT_ADDRESS_PANEL_ID } from '~/store/collections-content-address-panel/collections-content-address-panel-actions';
+import { DataExplorer } from "~/views-components/data-explorer/data-explorer";
+import { Dispatch } from 'redux';
+import { getIsAdmin } from '~/store/public-favorites/public-favorites-actions';
+import { resourceKindToContextMenuKind, openContextMenu } from '~/store/context-menu/context-menu-actions';
+import { ResourceKind } from '~/models/resource';
+import { loadDetailsPanel } from '~/store/details-panel/details-panel-action';
+import { connect, DispatchProp } from 'react-redux';
+import { navigateTo } from '~/store/navigation/navigation-action';
+
+type CssRules = 'backLink' | 'backIcon' | 'card' | 'title' | 'iconHeader' | 'link';
+
+const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
+    backLink: {
+        fontSize: '1rem',
+        fontWeight: 600,
+        display: 'flex',
+        alignItems: 'center',
+        textDecoration: 'none',
+        padding: theme.spacing.unit,
+        color: theme.palette.grey["700"],
+    },
+    backIcon: {
+        marginRight: theme.spacing.unit
+    },
+    card: {
+        width: '100%'
+    },
+    title: {
+        color: theme.palette.grey["700"]
+    },
+    iconHeader: {
+        fontSize: '1.875rem',
+        color: theme.customs.colors.green700
+    },
+    link: {
+        fontSize: '0.875rem',
+        color: theme.palette.primary.main,
+        textAlign: 'right',
+        '&:hover': {
+            cursor: 'pointer'
+        }
+    }
+});
+
+
+export interface CollectionContentAddressMainCardActionProps {
+    onContextMenu: (event: React.MouseEvent<any>, uuid: string) => void;
+    onItemClick: (item: string) => void;
+    onItemDoubleClick: (item: string) => void;
+}
+
+const mapDispatchToProps = (dispatch: Dispatch): CollectionContentAddressMainCardActionProps => ({
+    onContextMenu: (event, resourceUuid) => {
+        const isAdmin = dispatch<any>(getIsAdmin());
+        const kind = resourceKindToContextMenuKind(resourceUuid, isAdmin);
+        if (kind) {
+            dispatch<any>(openContextMenu(event, {
+                name: '',
+                uuid: resourceUuid,
+                ownerUuid: '',
+                kind: ResourceKind.NONE,
+                menuKind: kind
+            }));
+        }
+        dispatch<any>(loadDetailsPanel(resourceUuid));
+    },
+    onItemClick: (uuid: string) => {
+        dispatch<any>(loadDetailsPanel(uuid));
+    },
+    onItemDoubleClick: uuid => {
+        dispatch<any>(navigateTo(uuid));
+    }
+});
+
+export const CollectionsContentAddressPanel = withStyles(styles)(
+    connect(null, mapDispatchToProps)(
+        class extends React.Component<CollectionContentAddressMainCardActionProps & WithStyles<CssRules>> {
+            render() {
+                return <Grid item xs={12}>
+                    {/* <Link to={`/collections/${this.props.collection.uuid}`} className={this.props.classes.backLink}>
+                        <BackIcon className={this.props.classes.backIcon} />
+                        Back test
+                    </Link> */}
+                    <DataExplorer
+                        id={COLLECTIONS_CONTENT_ADDRESS_PANEL_ID}
+                        onRowClick={this.props.onItemClick}
+                        onRowDoubleClick={this.props.onItemDoubleClick}
+                        onContextMenu={this.props.onContextMenu}
+                        contextMenuColumn={true}
+                        dataTableDefaultView={
+                            <DataTableDefaultView
+                                icon={CollectionIcon}
+                                messages={['Collections with this content address not found.']} />
+                        } />;
+                    </Grid >;
+            }
+        }
+    )
+);
\ No newline at end of file
diff --git a/src/views/favorite-panel/favorite-panel.tsx b/src/views/favorite-panel/favorite-panel.tsx
index 8498b787..e7234009 100644
--- a/src/views/favorite-panel/favorite-panel.tsx
+++ b/src/views/favorite-panel/favorite-panel.tsx
@@ -22,7 +22,6 @@ import {
     ResourceType
 } from '~/views-components/data-explorer/renderers';
 import { FavoriteIcon } from '~/components/icon/icon';
-import { Dispatch } from 'redux';
 import { openContextMenu, resourceKindToContextMenuKind } from '~/store/context-menu/context-menu-actions';
 import { loadDetailsPanel } from '~/store/details-panel/details-panel-action';
 import { navigateTo } from '~/store/navigation/navigation-action';
diff --git a/src/views/workbench/workbench.tsx b/src/views/workbench/workbench.tsx
index a31c7d25..e3668373 100644
--- a/src/views/workbench/workbench.tsx
+++ b/src/views/workbench/workbench.tsx
@@ -92,6 +92,7 @@ import { GroupMemberAttributesDialog } from '~/views-components/groups-dialog/me
 import { AddGroupMembersDialog } from '~/views-components/dialog-forms/add-group-member-dialog';
 import { PartialCopyToCollectionDialog } from '~/views-components/dialog-forms/partial-copy-to-collection-dialog';
 import { PublicFavoritePanel } from '~/views/public-favorites-panel/public-favorites-panel';
+import { CollectionsContentAddressPanel } from '~/views/collection-content-address-panel/collection-content-address-panel';
 
 type CssRules = 'root' | 'container' | 'splitter' | 'asidePanel' | 'contentWrapper' | 'content';
 
@@ -176,6 +177,7 @@ export const WorkbenchPanel =
                                 <Route path={Routes.GROUP_DETAILS} component={GroupDetailsPanel} />
                                 <Route path={Routes.LINKS} component={LinkPanel} />
                                 <Route path={Routes.PUBLIC_FAVORITES} component={PublicFavoritePanel} />
+                                <Route path={Routes.COLLECTIONS_CONTENT_ADDRESS} component={CollectionsContentAddressPanel}/>
                             </Switch>
                         </Grid>
                     </Grid>

commit ada23a4054724d0d6be4ee88efb22daecd86001f
Author: Pawel Kowalczyk <pawel.kowalczyk at contractors.roche.com>
Date:   Mon May 6 12:06:59 2019 +0200

    check-for-collections-with-same-content-adress
    
    Feature #15020
    
    Arvados-DCO-1.1-Signed-off-by: Pawel Kowalczyk <pawel.kowalczyk at contractors.roche.com>

diff --git a/src/store/collection-panel/collection-panel-action.ts b/src/store/collection-panel/collection-panel-action.ts
index cd426471..76a40dcb 100644
--- a/src/store/collection-panel/collection-panel-action.ts
+++ b/src/store/collection-panel/collection-panel-action.ts
@@ -7,7 +7,7 @@ import { loadCollectionFiles } from "./collection-panel-files/collection-panel-f
 import { CollectionResource } from '~/models/collection';
 import { collectionPanelFilesAction } from "./collection-panel-files/collection-panel-files-actions";
 import { createTree } from "~/models/tree";
-import { RootState } from "../store";
+import { RootState } from "~/store/store";
 import { ServiceRepository } from "~/services/services";
 import { TagProperty } from "~/models/tag";
 import { snackbarActions } from "../snackbar/snackbar-actions";
@@ -15,9 +15,12 @@ import { resourcesActions } from "~/store/resources/resources-actions";
 import { unionize, ofType, UnionOf } from '~/common/unionize';
 import { SnackbarKind } from '~/store/snackbar/snackbar-actions';
 import { navigateTo } from '~/store/navigation/navigation-action';
+import { FilterBuilder } from "~/services/api/filter-builder";
+import { loadDetailsPanel } from '~/store/details-panel/details-panel-action';
 
 export const collectionPanelActions = unionize({
     SET_COLLECTION: ofType<CollectionResource>(),
+    SET_NUMBER_OF_COLLECTIONS_WITH_SAME_PDH: ofType<number>(),
     LOAD_COLLECTION: ofType<{ uuid: string }>(),
     LOAD_COLLECTION_SUCCESS: ofType<{ item: CollectionResource }>()
 });
@@ -31,6 +34,13 @@ export const loadCollectionPanel = (uuid: string) =>
         dispatch(collectionPanelActions.LOAD_COLLECTION({ uuid }));
         dispatch(collectionPanelFilesAction.SET_COLLECTION_FILES({ files: createTree() }));
         const collection = await services.collectionService.get(uuid);
+        const collectionsByPDH = await services.collectionService.list({
+            filters: new FilterBuilder()
+                .addEqual('portableDataHash', collection.portableDataHash)
+                .getFilters()
+        });
+        dispatch(collectionPanelActions.SET_NUMBER_OF_COLLECTIONS_WITH_SAME_PDH(collectionsByPDH.itemsAvailable));
+        dispatch(loadDetailsPanel(collection.uuid));
         dispatch(collectionPanelActions.LOAD_COLLECTION_SUCCESS({ item: collection }));
         dispatch(resourcesActions.SET_RESOURCES([collection]));
         dispatch<any>(loadCollectionFiles(collection.uuid));
diff --git a/src/store/collection-panel/collection-panel-files/collection-panel-files-actions.ts b/src/store/collection-panel/collection-panel-files/collection-panel-files-actions.ts
index b75de94a..534d70d4 100644
--- a/src/store/collection-panel/collection-panel-files/collection-panel-files-actions.ts
+++ b/src/store/collection-panel/collection-panel-files/collection-panel-files-actions.ts
@@ -77,8 +77,8 @@ export const openFileRemoveDialog = (filePath: string) =>
                 ? 'Are you sure you want to remove this directory?'
                 : 'Are you sure you want to remove this file?';
             const info = isDirectory
-                ? 'Removing files will change content adress.'
-                : 'Removing a file will change content adress.';
+                ? 'Removing files will change content address.'
+                : 'Removing a file will change content address.';
 
             dispatch(dialogActions.OPEN_DIALOG({
                 id: FILE_REMOVE_DIALOG,
@@ -101,7 +101,7 @@ export const openMultipleFilesRemoveDialog = () =>
         data: {
             title: 'Removing files',
             text: 'Are you sure you want to remove selected files?',
-            info: 'Removing files will change content adress.',
+            info: 'Removing files will change content address.',
             confirmButtonLabel: 'Remove'
         }
     });
diff --git a/src/store/collection-panel/collection-panel-reducer.ts b/src/store/collection-panel/collection-panel-reducer.ts
index 55829cb5..4207a393 100644
--- a/src/store/collection-panel/collection-panel-reducer.ts
+++ b/src/store/collection-panel/collection-panel-reducer.ts
@@ -7,10 +7,12 @@ import { CollectionResource } from "~/models/collection";
 
 export interface CollectionPanelState {
     item: CollectionResource | null;
+    numberOfCollectionsWithSamePDH: number;
 }
 
 const initialState = {
-    item: null
+    item: null,
+    numberOfCollectionsWithSamePDH: 0
 };
 
 export const collectionPanelReducer = (state: CollectionPanelState = initialState, action: CollectionPanelAction) =>
@@ -18,4 +20,5 @@ export const collectionPanelReducer = (state: CollectionPanelState = initialStat
         default: () => state,
         SET_COLLECTION: (item) => ({ ...state, item }),
         LOAD_COLLECTION_SUCCESS: ({ item }) => ({ ...state, item }),
+        SET_NUMBER_OF_COLLECTIONS_WITH_SAME_PDH: (num) => ({ ...state, numberOfCollectionsWithSamePDH: num }),
     });
diff --git a/src/store/workbench/workbench-actions.ts b/src/store/workbench/workbench-actions.ts
index adf3fa15..378bb8de 100644
--- a/src/store/workbench/workbench-actions.ts
+++ b/src/store/workbench/workbench-actions.ts
@@ -68,8 +68,7 @@ import { FilterBuilder } from '~/services/api/filter-builder';
 import { GroupContentsResource } from '~/services/groups-service/groups-service';
 import { MatchCases, ofType, unionize, UnionOf } from '~/common/unionize';
 import { loadRunProcessPanel } from '~/store/run-process-panel/run-process-panel-actions';
-import { loadCollectionFiles } from '~/store/collection-panel/collection-panel-files/collection-panel-files-actions';
-import { collectionPanelActions } from "~/store/collection-panel/collection-panel-action";
+import { collectionPanelActions, loadCollectionPanel } from "~/store/collection-panel/collection-panel-action";
 import { CollectionResource } from "~/models/collection";
 import {
     loadSearchResultsPanel,
@@ -256,22 +255,22 @@ export const loadCollection = (uuid: string) =>
                         dispatch(collectionPanelActions.SET_COLLECTION(collection as CollectionResource));
                         dispatch(updateResources([collection]));
                         await dispatch(activateSidePanelTreeItem(collection.ownerUuid));
-                        dispatch(setSidePanelBreadcrumbs(collection.ownerUuid));
-                        dispatch(loadCollectionFiles(collection.uuid));
+                        dispatch(setSidePanelBreadcrumbs(collection.ownerUuid));              
+                        dispatch(loadCollectionPanel(collection.uuid));
                     },
                     SHARED: collection => {
                         dispatch(collectionPanelActions.SET_COLLECTION(collection as CollectionResource));
                         dispatch(updateResources([collection]));
                         dispatch<any>(setSharedWithMeBreadcrumbs(collection.ownerUuid));
                         dispatch(activateSidePanelTreeItem(collection.ownerUuid));
-                        dispatch(loadCollectionFiles(collection.uuid));
+                        dispatch(loadCollectionPanel(collection.uuid));
                     },
                     TRASHED: collection => {
                         dispatch(collectionPanelActions.SET_COLLECTION(collection as CollectionResource));
                         dispatch(updateResources([collection]));
                         dispatch(setTrashBreadcrumbs(''));
                         dispatch(activateSidePanelTreeItem(SidePanelTreeCategory.TRASH));
-                        dispatch(loadCollectionFiles(collection.uuid));
+                        dispatch(loadCollectionPanel(collection.uuid));
                     },
 
                 });
diff --git a/src/views-components/details-panel/collection-details.tsx b/src/views-components/details-panel/collection-details.tsx
index 98fa3886..01b867b3 100644
--- a/src/views-components/details-panel/collection-details.tsx
+++ b/src/views-components/details-panel/collection-details.tsx
@@ -26,7 +26,7 @@ export class CollectionDetails extends DetailsData<CollectionResource> {
             <DetailsAttribute label='Created at' value={formatDate(this.item.createdAt)} />
             {/* Links but we dont have view */}
             <DetailsAttribute label='Collection UUID' link={this.item.uuid} value={this.item.uuid} />
-            <DetailsAttribute label='Content address' link={this.item.portableDataHash} value={this.item.portableDataHash} />
+            <DetailsAttribute label='Content address' link={this.numberOfCollectionsByPDH === 1 ? this.item.uuid : this.item.portableDataHash} value={this.item.portableDataHash} />
             {/* Missing attrs */}
             <DetailsAttribute label='Number of files' value={this.data && this.data.fileCount} />
             <DetailsAttribute label='Content size' value={formatFileSize(this.data && this.data.fileSize)} />
diff --git a/src/views-components/details-panel/details-data.tsx b/src/views-components/details-panel/details-data.tsx
index 45afb02b..1a67a55d 100644
--- a/src/views-components/details-panel/details-data.tsx
+++ b/src/views-components/details-panel/details-data.tsx
@@ -7,7 +7,7 @@ import { DetailsResource } from "~/models/details";
 import { ResourceData } from "~/store/resources-data/resources-data-reducer";
 
 export abstract class DetailsData<T extends DetailsResource = DetailsResource> {
-    constructor(protected item: T, protected data?: ResourceData) {}
+    constructor(protected item: T, protected data?: ResourceData, protected numberOfCollectionsByPDH?: number) {}
 
     getTitle(): string {
         return this.item.name || 'Projects';
diff --git a/src/views-components/details-panel/details-panel.tsx b/src/views-components/details-panel/details-panel.tsx
index 2a30ae47..5c8ebe74 100644
--- a/src/views-components/details-panel/details-panel.tsx
+++ b/src/views-components/details-panel/details-panel.tsx
@@ -62,13 +62,13 @@ const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
 
 const EMPTY_RESOURCE: EmptyResource = { kind: undefined, name: 'Projects' };
 
-const getItem = (res: DetailsResource, resourceData?: ResourceData): DetailsData => {
+const getItem = (res: DetailsResource, resourceData?: ResourceData, numberOfCollectionsByPDH?: number): DetailsData => {
     if ('kind' in res) {
         switch (res.kind) {
             case ResourceKind.PROJECT:
                 return new ProjectDetails(res);
             case ResourceKind.COLLECTION:
-                return new CollectionDetails(res, resourceData);
+                return new CollectionDetails(res, resourceData, numberOfCollectionsByPDH);
             case ResourceKind.PROCESS:
                 return new ProcessDetails(res);
             default:
@@ -79,13 +79,14 @@ const getItem = (res: DetailsResource, resourceData?: ResourceData): DetailsData
     }
 };
 
-const mapStateToProps = ({ detailsPanel, resources, resourcesData, collectionPanelFiles }: RootState) => {
+const mapStateToProps = ({ detailsPanel, resources, resourcesData, collectionPanelFiles, collectionPanel }: RootState) => {
     const resource = getResource(detailsPanel.resourceUuid)(resources) as DetailsResource | undefined;
     const file = getNode(detailsPanel.resourceUuid)(collectionPanelFiles);
     const resourceData = getResourceData(detailsPanel.resourceUuid)(resourcesData);
+    const numberOfCollectionsByPDH = collectionPanel.numberOfCollectionsWithSamePDH;
     return {
         isOpened: detailsPanel.isOpened,
-        item: getItem(resource || (file && file.value) || EMPTY_RESOURCE, resourceData)
+        item: getItem(resource || (file && file.value) || EMPTY_RESOURCE, resourceData, numberOfCollectionsByPDH),
     };
 };
 

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


hooks/post-receive
-- 




More information about the arvados-commits mailing list