styled panel-collapse icon Arvados-DCO-1.1-Signed-off-by: Lisa Knox <lisa.knox@curii...
[arvados.git] / src / views / process-panel / process-io-card.tsx
index 4f93f48c2e75552134514a1165eb38f11c8aad6b..8b85617d32743d1654b7aff9cd794d189a43389b 100644 (file)
@@ -74,6 +74,7 @@ import { Process } from 'store/processes/process';
 import { navigateTo } from 'store/navigation/navigation-action';
 import classNames from 'classnames';
 import { DefaultCodeSnippet } from 'components/default-code-snippet/default-code-snippet';
+import { KEEP_URL_REGEX } from 'models/resource';
 
 type CssRules =
   | "card"
@@ -127,7 +128,8 @@ const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
         paddingTop: theme.spacing.unit * 0.5
     },
     tableWrapper: {
-        height: `calc(100% - ${theme.spacing.unit * 6}px)`,
+        height: 'auto',
+        maxHeight: `calc(100% - ${theme.spacing.unit * 4.5}px)`,
         overflow: 'auto',
     },
     tableRoot: {
@@ -238,9 +240,13 @@ type ProcessIOCardProps = ProcessIOCardDataProps & ProcessIOCardActionProps & Wi
 export const ProcessIOCard = withStyles(styles)(connect(null, mapDispatchToProps)(
     ({ classes, label, params, raw, mounts, outputUuid, doHidePanel, doMaximizePanel, doUnMaximizePanel, panelMaximized, panelName, process, navigateTo }: ProcessIOCardProps) => {
         const [mainProcTabState, setMainProcTabState] = useState(0);
+        const [subProcTabState, setSubProcTabState] = useState(0);
         const handleMainProcTabChange = (event: React.MouseEvent<HTMLElement>, value: number) => {
             setMainProcTabState(value);
         }
+        const handleSubProcTabChange = (event: React.MouseEvent<HTMLElement>, value: number) => {
+            setSubProcTabState(value);
+        }
 
         const [showImagePreview, setShowImagePreview] = useState(false);
 
@@ -251,6 +257,10 @@ export const ProcessIOCard = withStyles(styles)(connect(null, mapDispatchToProps
         const hasRaw = !!(raw && Object.keys(raw).length > 0);
         const hasParams = !!(params && params.length > 0);
 
+        // Subprocess
+        const hasInputMounts = !!(label === ProcessIOCardType.INPUT && mounts && mounts.length);
+        const hasOutputCollecton = !!(label === ProcessIOCardType.OUTPUT && outputUuid);
+
         return <Card className={classes.card} data-cy="process-io-card">
             <CardHeader
                 className={classes.header}
@@ -314,25 +324,32 @@ export const ProcessIOCard = withStyles(styles)(connect(null, mapDispatchToProps
                     </>) :
                     // Subprocess
                     (<>
-                        {((mounts && mounts.length) || outputUuid) ?
+                        {loading && <Grid container item alignItems='center' justify='center'>
+                            <CircularProgress />
+                        </Grid>}
+                        {!loading && (hasInputMounts || hasOutputCollecton || hasRaw) ?
                             <>
-                                <Tabs value={0} variant="fullWidth" className={classes.symmetricTabs}>
-                                    {label === ProcessIOCardType.INPUT && <Tab label="Collections" />}
-                                    {label === ProcessIOCardType.OUTPUT && <Tab label="Collection" />}
+                                <Tabs value={subProcTabState} onChange={handleSubProcTabChange} variant="fullWidth" className={classes.symmetricTabs}>
+                                    {hasInputMounts && <Tab label="Collections" />}
+                                    {hasOutputCollecton && <Tab label="Collection" />}
+                                    <Tab label="JSON" />
                                 </Tabs>
                                 <div className={classes.tableWrapper}>
-                                    {label === ProcessIOCardType.INPUT && <ProcessInputMounts mounts={mounts || []} />}
-                                    {label === ProcessIOCardType.OUTPUT && <>
+                                    {subProcTabState === 0 && hasInputMounts && <ProcessInputMounts mounts={mounts || []} />}
+                                    {subProcTabState === 0 && hasOutputCollecton && <>
                                         {outputUuid && <Typography className={classes.collectionLink}>
                                             Output Collection: <MuiLink className={classes.keepLink} onClick={() => {navigateTo(outputUuid || "")}}>
                                             {outputUuid}
                                         </MuiLink></Typography>}
                                         <ProcessOutputCollectionFiles isWritable={false} currentItemUuid={outputUuid} />
                                     </>}
+                                    {(subProcTabState === 1 || (!hasInputMounts && !hasOutputCollecton)) && <div className={classes.tableWrapper}>
+                                        <ProcessIORaw data={raw} />
+                                    </div>}
                                 </div>
                             </> :
                             <Grid container item alignItems='center' justify='center'>
-                                <DefaultView messages={["No collection(s) found"]} />
+                                <DefaultView messages={["No data to display"]} />
                             </Grid>
                         }
                     </>)
@@ -657,6 +674,11 @@ const isFileImage = (basename?: string): boolean => {
     return basename ? (mime.getType(basename) || "").startsWith('image/') : false;
 };
 
+const isFileUrl = (location?: string): boolean => (
+    !!location && !KEEP_URL_REGEX.exec(location) &&
+    (location.startsWith("http://") || location.startsWith("https://"))
+);
+
 const normalizeDirectoryLocation = (directory: Directory): Directory => {
     if (!directory.location) {
         return directory;
@@ -680,6 +702,13 @@ const directoryToProcessIOValue = (directory: Directory, auth: AuthState, pdh?:
 const fileToProcessIOValue = (file: File, secondary: boolean, auth: AuthState, pdh: string | undefined, mainFilePdh: string): ProcessIOValue => {
     if (isExternalValue(file)) {return {display: <UnsupportedValue />}}
 
+    if (isFileUrl(file.location)) {
+        return {
+            display: <MuiLink href={file.location} target="_blank">{file.location}</MuiLink>,
+            secondary,
+        };
+    }
+
     const resourcePdh = getResourcePdhUrl(file, pdh);
     return {
         display: <KeepUrlPath auth={auth} res={file} pdh={pdh}/>,