1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
5 import React from 'react';
15 } from '@material-ui/core';
16 import { connect, DispatchProp } from "react-redux";
17 import { RouteComponentProps } from 'react-router';
18 import { ArvadosTheme } from 'common/custom-theme';
19 import { RootState } from 'store/store';
20 import { WorkflowIcon } from 'components/icon/icon';
22 WorkflowResource, parseWorkflowDefinition, getWorkflowInputs,
23 getWorkflowOutputs, getWorkflow
24 } from 'models/workflow';
25 import { ProcessOutputCollectionFiles } from 'views/process-panel/process-output-collection-files';
26 import { WorkflowDetailsAttributes } from 'views-components/details-panel/workflow-details';
27 import { getResource } from 'store/resources/resources';
28 import { openContextMenu, resourceUuidToContextMenuKind } from 'store/context-menu/context-menu-actions';
29 import { MPVContainer, MPVPanelContent, MPVPanelState } from 'components/multi-panel-view/multi-panel-view';
30 import { ProcessIOCard, ProcessIOCardType, ProcessIOParameter } from 'views/process-panel/process-io-card';
31 import { formatInputData, formatOutputData } from 'store/process-panel/process-panel-actions';
32 import { DetailsAttribute } from 'components/details-attribute/details-attribute';
33 import { getPropertyChip } from "views-components/resource-properties-form/property-chip";
35 type CssRules = 'root'
54 const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
62 paddingLeft: theme.spacing.unit * 2,
63 paddingRight: theme.spacing.unit * 2,
64 paddingBottom: theme.spacing.unit * 2,
74 color: theme.customs.colors.greyL
77 marginRight: theme.spacing.unit / 2,
78 marginBottom: theme.spacing.unit / 2
91 flexDirection: 'column',
94 textTransform: 'none',
99 color: theme.palette.primary.main,
105 marginLeft: theme.spacing.unit,
109 paddingTop: theme.spacing.unit,
110 paddingBottom: theme.spacing.unit,
114 paddingTop: theme.spacing.unit * 0.5,
115 color: theme.customs.colors.green700,
118 alignSelf: 'flex-start',
119 paddingTop: theme.spacing.unit * 0.5
122 marginRight: theme.spacing.unit / 2,
123 marginBottom: theme.spacing.unit / 2
127 interface RegisteredWorkflowPanelDataProps {
128 item: WorkflowResource;
129 workflowCollection: string;
130 inputParams: ProcessIOParameter[];
131 outputParams: ProcessIOParameter[];
132 gitprops: { [key: string]: string; };
135 type RegisteredWorkflowPanelProps = RegisteredWorkflowPanelDataProps & DispatchProp & WithStyles<CssRules>
137 export const RegisteredWorkflowPanel = withStyles(styles)(connect(
138 (state: RootState, props: RouteComponentProps<{ id: string }>) => {
139 const item = getResource<WorkflowResource>(props.match.params.id)(state.resources);
140 let inputParams: ProcessIOParameter[] = [];
141 let outputParams: ProcessIOParameter[] = [];
142 let workflowCollection = "";
143 const gitprops: { [key: string]: string; } = {};
146 const wfdef = parseWorkflowDefinition(item);
148 const inputs = getWorkflowInputs(wfdef);
150 inputs.forEach(elm => {
151 if (elm.default !== undefined && elm.default !== null) {
152 elm.value = elm.default;
155 inputParams = formatInputData(inputs, state.auth);
158 const outputs = getWorkflowOutputs(wfdef);
160 outputParams = formatOutputData(outputs, {}, undefined, state.auth);
163 const wf = getWorkflow(wfdef);
165 const REGEX = /keep:([0-9a-f]{32}\+\d+)\/.*/;
167 workflowCollection = wf["steps"][0].run.match(REGEX)[1];
171 for (const elm in wfdef) {
172 if (elm.startsWith("http://arvados.org/cwl#git")) {
173 gitprops[elm.substr(23)] = wfdef[elm]
178 return { item, inputParams, outputParams, workflowCollection, gitprops };
180 class extends React.Component<RegisteredWorkflowPanelProps> {
182 const { classes, item, inputParams, outputParams, workflowCollection, gitprops, dispatch } = this.props;
183 const panelsData: MPVPanelState[] = [
190 ? <MPVContainer className={classes.root} spacing={8} direction="column" justify-content="flex-start" wrap="nowrap" panelStates={panelsData}>
191 <MPVPanelContent xs="auto" data-cy='registered-workflow-info-panel'>
192 <Card className={classes.infoCard}>
194 className={classes.header}
196 content: classes.title,
197 avatar: classes.avatar,
199 avatar={<WorkflowIcon className={classes.iconHeader} />}
201 <Tooltip title={item.name} placement="bottom-start">
202 <Typography noWrap variant='h6'>
208 <Tooltip title={item.description || '(no-description)'} placement="bottom-start">
209 <Typography noWrap variant='body1' color='inherit'>
210 {item.description || '(no-description)'}
215 <Grid container justify="space-between">
217 <WorkflowDetailsAttributes workflow={item} />
220 <Grid item xs={12} md={12}>
221 <DetailsAttribute label='Properties' />
222 {Object.keys(gitprops).map(k =>
223 getPropertyChip(k, gitprops[k], undefined, classes.propertyTag))}
228 <MPVPanelContent forwardProps xs data-cy="process-inputs">
230 label={ProcessIOCardType.INPUT}
236 <MPVPanelContent forwardProps xs data-cy="process-outputs">
238 label={ProcessIOCardType.OUTPUT}
239 params={outputParams}
245 <Card className={classes.filesCard}>
246 <ProcessOutputCollectionFiles isWritable={false} currentItemUuid={workflowCollection} />
253 handleContextMenu = (event: React.MouseEvent<any>) => {
254 const { uuid, ownerUuid, name, description,
255 kind } = this.props.item;
256 const menuKind = this.props.dispatch<any>(resourceUuidToContextMenuKind(uuid));
265 // Avoid expanding/collapsing the panel
266 event.stopPropagation();
267 this.props.dispatch<any>(openContextMenu(event, resource));