Merge branch '17205-render-owner-name'
authorDaniel Kutyła <daniel.kutyla@contractors.roche.com>
Wed, 3 Feb 2021 19:47:02 +0000 (20:47 +0100)
committerDaniel Kutyła <daniel.kutyla@contractors.roche.com>
Wed, 3 Feb 2021 19:47:11 +0000 (20:47 +0100)
closes #17205

Arvados-DCO-1.1-Signed-off-by: Daniel Kutyła <daniel.kutyla@contractors.roche.com>

src/components/details-attribute/details-attribute.tsx
src/store/resources/resources-actions.ts
src/views-components/data-explorer/renderers.tsx
src/views-components/details-panel/process-details.tsx
src/views-components/details-panel/project-details.tsx
src/views/collection-panel/collection-panel.tsx

index 01276c57edbfc3d07aa2a00a75e432a3844732b0..7633b71a45685137c92ec69a32f972d4cb0c3109 100644 (file)
@@ -62,6 +62,7 @@ interface DetailsAttributeDataProps {
     onValueClick?: () => void;
     linkToUuid?: string;
     copyValue?: string;
+    uuidEnhancer?: Function;
 }
 
 type DetailsAttributeProps = DetailsAttributeDataProps & WithStyles<CssRules> & FederationConfig & DispatchProp;
@@ -84,23 +85,25 @@ export const DetailsAttribute = connect(mapStateToProps)(withStyles(styles)(
         }
 
         render() {
-            const { label, link, value, children, classes, classLabel,
+            const { uuidEnhancer, label, link, value, children, classes, classLabel,
                 classValue, lowercaseValue, onValueClick, linkToUuid,
                 localCluster, remoteHostsConfig, sessions, copyValue } = this.props;
             let valueNode: React.ReactNode;
 
             if (linkToUuid) {
+            const uuid = uuidEnhancer ? uuidEnhancer(linkToUuid) : linkToUuid;
                 const linkUrl = getNavUrl(linkToUuid || "", { localCluster, remoteHostsConfig, sessions });
                 if (linkUrl[0] === '/') {
-                    valueNode = <Link to={linkUrl} className={classes.link}>{linkToUuid}</Link>;
+                    valueNode = <Link to={linkUrl} className={classes.link}>{uuid}</Link>;
                 } else {
-                    valueNode = <a href={linkUrl} className={classes.link} target='_blank'>{linkToUuid}</a>;
+                    valueNode = <a href={linkUrl} className={classes.link} target='_blank'>{uuid}</a>;
                 }
             } else if (link) {
                 valueNode = <a href={link} className={classes.link} target='_blank'>{value}</a>;
             } else {
                 valueNode = value;
             }
+
             return <Typography component="div" className={classes.attribute}>
                 <Typography component="div" className={classnames([classes.label, classLabel])}>{label}</Typography>
                 <Typography
index 1de2feff8b9f8d42111090f132f7dfba4a624a0c..5465db62aef508d3a00ca42eb007a52878562ffa 100644 (file)
@@ -18,13 +18,13 @@ export type ResourcesAction = UnionOf<typeof resourcesActions>;
 
 export const updateResources = (resources: Resource[]) => resourcesActions.SET_RESOURCES(resources);
 
-export const loadResource = (uuid: string) =>
+export const loadResource = (uuid: string, showErrors?: boolean) =>
     async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
         try {
             const kind = extractUuidKind(uuid);
             const service = getResourceService(kind)(services);
             if (service) {
-                const resource = await service.get(uuid);
+                const resource = await service.get(uuid, showErrors);
                 dispatch<any>(updateResources([resource]));
                 return resource;
             }
index 6d95196d3ed1ce8eb79fa8600d8cc4c618f56c92..6cf29faecf444683540de031966273298fc19314 100644 (file)
@@ -20,13 +20,14 @@ import { WorkflowResource } from '~/models/workflow';
 import { ResourceStatus as WorkflowStatus } from '~/views/workflow-panel/workflow-panel-view';
 import { getUuidPrefix, openRunProcess } from '~/store/workflow-panel/workflow-panel-actions';
 import { openSharingDialog } from '~/store/sharing-dialog/sharing-dialog-actions';
-import { UserResource } from '~/models/user';
+import { getUserFullname, User, UserResource } from '~/models/user';
 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 { CollectionResource } from '~/models/collection';
 import { IllegalNamingWarning } from '~/components/warning/warning';
+import { loadResource } from '~/store/resources/resources-actions';
 
 const renderName = (dispatch: Dispatch, item: GroupContentsResource) =>
     <Grid container alignItems="center" wrap="nowrap" spacing={16}>
@@ -434,6 +435,35 @@ export const ResourceOwnerName = connect(
         return { owner: ownerName ? ownerName!.name : resource!.ownerUuid };
     })((props: { owner: string }) => renderOwner(props.owner));
 
+export const ResourceOwnerWithName =
+    compose(
+        connect(
+            (state: RootState, props: { uuid: string }) => {
+                let ownerName = '';
+                const resource = getResource<GroupContentsResource & UserResource>(props.uuid)(state.resources);
+
+                if (resource) {
+                    ownerName = getUserFullname(resource as User) || (resource as GroupContentsResource).name;
+                }
+
+                return { uuid: props.uuid, ownerName };
+            }),
+        withStyles({}, { withTheme: true }))
+        ((props: { uuid: string, ownerName: string, dispatch: Dispatch, theme: ArvadosTheme }) => {
+            const { uuid, ownerName, dispatch, theme } = props;
+
+            if (ownerName === '') {
+                dispatch<any>(loadResource(uuid, false));
+                return <Typography style={{ color: theme.palette.primary.main }} inline noWrap>
+                    {uuid}
+                </Typography>;
+            }
+
+            return <Typography style={{ color: theme.palette.primary.main }} inline noWrap>
+                {ownerName} ({uuid})
+            </Typography>;
+        });
+
 const renderType = (type: string) =>
     <Typography noWrap>
         {resourceLabel(type)}
@@ -446,20 +476,20 @@ export const ResourceType = connect(
     })((props: { type: string }) => renderType(props.type));
 
 export const ResourceStatus = connect((state: RootState, props: { uuid: string }) => {
-        return { resource: getResource<GroupContentsResource>(props.uuid)(state.resources) };
-    })((props: { resource: GroupContentsResource }) =>
-        (props.resource && props.resource.kind === ResourceKind.COLLECTION)
+    return { resource: getResource<GroupContentsResource>(props.uuid)(state.resources) };
+})((props: { resource: GroupContentsResource }) =>
+    (props.resource && props.resource.kind === ResourceKind.COLLECTION)
         ? <CollectionStatus uuid={props.resource.uuid} />
         : <ProcessStatus uuid={props.resource.uuid} />
-    );
+);
 
 export const CollectionStatus = connect((state: RootState, props: { uuid: string }) => {
-        return { collection: getResource<CollectionResource>(props.uuid)(state.resources) };
-    })((props: { collection: CollectionResource }) =>
-        (props.collection.uuid !== props.collection.currentVersionUuid)
+    return { collection: getResource<CollectionResource>(props.uuid)(state.resources) };
+})((props: { collection: CollectionResource }) =>
+    (props.collection.uuid !== props.collection.currentVersionUuid)
         ? <Typography>version {props.collection.version}</Typography>
         : <Typography>head version</Typography>
-    );
+);
 
 export const ProcessStatus = compose(
     connect((state: RootState, props: { uuid: string }) => {
@@ -478,7 +508,7 @@ export const ProcessStatus = compose(
 export const ProcessStartDate = connect(
     (state: RootState, props: { uuid: string }) => {
         const process = getProcess(props.uuid)(state.resources);
-        return { date: ( process && process.container ) ? process.container.startedAt : '' };
+        return { date: (process && process.container) ? process.container.startedAt : '' };
     })((props: { date: string }) => renderDate(props.date));
 
 export const renderRunTime = (time: number) =>
index aa1b3a1d73532de76b07398bc177da7f0d57ea1f..0867f92d2d38a68074e2a3aa739bd318771dd5ae 100644 (file)
@@ -10,6 +10,7 @@ import { ResourceKind } from '~/models/resource';
 import { resourceLabel } from '~/common/labels';
 import { DetailsData } from "./details-data";
 import { DetailsAttribute } from "~/components/details-attribute/details-attribute";
+import { ResourceOwnerWithName } from '../data-explorer/renderers';
 
 export class ProcessDetails extends DetailsData<ProcessResource> {
 
@@ -20,7 +21,8 @@ export class ProcessDetails extends DetailsData<ProcessResource> {
     getDetails() {
         return <div>
             <DetailsAttribute label='Type' value={resourceLabel(ResourceKind.PROCESS)} />
-            <DetailsAttribute label='Owner' linkToUuid={this.item.ownerUuid} value={this.item.ownerUuid} />
+            <DetailsAttribute label='Owner' linkToUuid={this.item.ownerUuid} value={this.item.ownerUuid}
+                uuidEnhancer={(uuid: string) => <ResourceOwnerWithName uuid={uuid} />} />
 
             <DetailsAttribute label='Status' value={this.item.state} />
             <DetailsAttribute label='Last modified' value={formatDate(this.item.modifiedAt)} />
index b901abce8ba7a9d497ab17873152fae196b4c990..61797373b69a51ddb8dcd8039cf5180610ed5749 100644 (file)
@@ -17,6 +17,7 @@ import { withStyles, StyleRulesCallback, WithStyles } from '@material-ui/core';
 import { ArvadosTheme } from '~/common/custom-theme';
 import { Dispatch } from 'redux';
 import { getPropertyChip } from '../resource-properties-form/property-chip';
+import { ResourceOwnerWithName } from '../data-explorer/renderers';
 
 export class ProjectDetails extends DetailsData<ProjectResource> {
     getIcon(className?: string) {
@@ -59,7 +60,8 @@ const ProjectDetailsComponent = connect(null, mapDispatchToProps)(
     withStyles(styles)(
         ({ classes, project, onClick }: ProjectDetailsComponentProps) => <div>
             <DetailsAttribute label='Type' value={resourceLabel(ResourceKind.PROJECT)} />
-            <DetailsAttribute label='Owner' linkToUuid={project.ownerUuid} lowercaseValue={true} />
+            <DetailsAttribute label='Owner' linkToUuid={project.ownerUuid}
+                uuidEnhancer={(uuid: string) => <ResourceOwnerWithName uuid={uuid} />} />
             <DetailsAttribute label='Last modified' value={formatDate(project.modifiedAt)} />
             <DetailsAttribute label='Created at' value={formatDate(project.createdAt)} />
             <DetailsAttribute label='Project UUID' linkToUuid={project.uuid} value={project.uuid} />
index 685bb78bda561cd90392afc9eecca7078e7b0784..7d54992ebd222eca77df3bfea7f877f258de5e1f 100644 (file)
@@ -32,6 +32,7 @@ import { getProgressIndicator } from '~/store/progress-indicator/progress-indica
 import { COLLECTION_PANEL_LOAD_FILES, loadCollectionFiles, COLLECTION_PANEL_LOAD_FILES_THRESHOLD } from '~/store/collection-panel/collection-panel-files/collection-panel-files-actions';
 import { Link } from 'react-router-dom';
 import { Link as ButtonLink } from '@material-ui/core';
+import { ResourceOwnerWithName } from '~/views-components/data-explorer/renderers';
 
 type CssRules = 'root'
     | 'button'
@@ -297,7 +298,8 @@ export const CollectionDetailsAttributes = (props: { item: CollectionResource, t
         </Grid>
         <Grid item xs={12} md={mdSize}>
             <DetailsAttribute classLabel={classes.label} classValue={classes.value}
-                label='Owner' linkToUuid={item.ownerUuid} />
+                label='Owner' linkToUuid={item.ownerUuid}
+                uuidEnhancer={(uuid: string) => <ResourceOwnerWithName uuid={uuid} />} />
         </Grid>
         <Grid item xs={12} md={mdSize}>
             <DetailsAttribute classLabel={classes.label} classValue={classes.value}
@@ -309,7 +311,7 @@ export const CollectionDetailsAttributes = (props: { item: CollectionResource, t
             <DetailsAttribute
                 classLabel={classes.label} classValue={classes.value}
                 label='Version number'
-                value={ showVersionBrowser !== undefined
+                value={showVersionBrowser !== undefined
                     ? <Tooltip title="Open version browser"><ButtonLink underline='none' className={classes.button} onClick={() => showVersionBrowser()}>
                         {<span data-cy='collection-version-number'>{item.version}</span>}
                     </ButtonLink></Tooltip>