Merge branch '21933-deps-upgrade'
[arvados.git] / services / workbench2 / src / views / workflow-panel / registered-workflow-panel.tsx
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 import React from 'react';
6 import {
7     StyleRulesCallback,
8     WithStyles,
9     withStyles,
10     Tooltip,
11     Typography,
12     Card,
13     CardHeader,
14     CardContent,
15     IconButton
16 } from '@material-ui/core';
17 import { connect, DispatchProp } from "react-redux";
18 import { RouteComponentProps } from 'react-router';
19 import { ArvadosTheme } from 'common/custom-theme';
20 import { RootState } from 'store/store';
21 import { WorkflowIcon, MoreVerticalIcon } from 'components/icon/icon';
22 import { WorkflowResource } from 'models/workflow';
23 import { ProcessOutputCollectionFiles } from 'views/process-panel/process-output-collection-files';
24 import { WorkflowDetailsAttributes, RegisteredWorkflowPanelDataProps, getRegisteredWorkflowPanelData } from 'views-components/details-panel/workflow-details';
25 import { getResource } from 'store/resources/resources';
26 import { openContextMenu, resourceUuidToContextMenuKind } from 'store/context-menu/context-menu-actions';
27 import { MPVContainer, MPVPanelContent, MPVPanelState } from 'components/multi-panel-view/multi-panel-view';
28 import { ProcessIOCard, ProcessIOCardType } from 'views/process-panel/process-io-card';
29 import { NotFoundView } from 'views/not-found-panel/not-found-panel';
30 import { WorkflowProcessesPanel } from './workflow-processes-panel';
31
32 type CssRules =
33     | 'button'
34     | 'infoCard'
35     | 'propertiesCard'
36     | 'filesCard'
37     | 'iconHeader'
38     | 'tag'
39     | 'label'
40     | 'value'
41     | 'link'
42     | 'centeredLabel'
43     | 'warningLabel'
44     | 'collectionName'
45     | 'readOnlyIcon'
46     | 'header'
47     | 'title'
48     | 'avatar'
49     | 'content';
50
51 const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
52     button: {
53         cursor: 'pointer'
54     },
55     infoCard: {
56     },
57     propertiesCard: {
58         padding: 0,
59     },
60     filesCard: {
61         padding: 0,
62     },
63     iconHeader: {
64         fontSize: '1.875rem',
65         color: theme.customs.colors.greyL
66     },
67     tag: {
68         marginRight: theme.spacing.unit / 2,
69         marginBottom: theme.spacing.unit / 2
70     },
71     label: {
72         fontSize: '0.875rem',
73     },
74     centeredLabel: {
75         fontSize: '0.875rem',
76         textAlign: 'center'
77     },
78     warningLabel: {
79         fontStyle: 'italic'
80     },
81     collectionName: {
82         flexDirection: 'column',
83     },
84     value: {
85         textTransform: 'none',
86         fontSize: '0.875rem'
87     },
88     link: {
89         fontSize: '0.875rem',
90         color: theme.palette.primary.main,
91         '&:hover': {
92             cursor: 'pointer'
93         }
94     },
95     readOnlyIcon: {
96         marginLeft: theme.spacing.unit,
97         fontSize: 'small',
98     },
99     header: {
100         paddingTop: theme.spacing.unit,
101         paddingBottom: theme.spacing.unit,
102     },
103     title: {
104         overflow: 'hidden',
105         paddingTop: theme.spacing.unit * 0.5,
106         color: theme.customs.colors.green700,
107     },
108     avatar: {
109         alignSelf: 'flex-start',
110         paddingTop: theme.spacing.unit * 0.5
111     },
112     content: {
113         padding: theme.spacing.unit * 1.0,
114         paddingTop: theme.spacing.unit * 0.5,
115         '&:last-child': {
116             paddingBottom: theme.spacing.unit * 1,
117         }
118     }
119 });
120
121 type RegisteredWorkflowPanelProps = RegisteredWorkflowPanelDataProps & DispatchProp & WithStyles<CssRules>
122
123 export const RegisteredWorkflowPanel = withStyles(styles)(connect(
124     (state: RootState, props: RouteComponentProps<{ id: string }>) => {
125         const item = getResource<WorkflowResource>(props.match.params.id)(state.resources);
126         if (item) {
127             return getRegisteredWorkflowPanelData(item, state.auth);
128         }
129         return { item, inputParams: [], outputParams: [], workflowCollection: "", gitprops: {} };
130     })(
131         class extends React.Component<RegisteredWorkflowPanelProps> {
132             render() {
133                 const { classes, item, inputParams, outputParams, workflowCollection } = this.props;
134                 const panelsData: MPVPanelState[] = [
135                     { name: "Details" },
136                     { name: "Runs" },
137                     { name: "Outputs" },
138                     { name: "Inputs" },
139                     { name: "Definition" },
140                 ];
141                 return item
142                     ? <MPVContainer spacing={8} direction="column" justify-content="flex-start" wrap="nowrap" panelStates={panelsData}>
143                         <MPVPanelContent xs="auto" data-cy='registered-workflow-info-panel'>
144                             <Card className={classes.infoCard}>
145                                 <CardHeader
146                                     className={classes.header}
147                                     classes={{
148                                         content: classes.title,
149                                         avatar: classes.avatar,
150                                     }}
151                                     avatar={<WorkflowIcon className={classes.iconHeader} />}
152                                     title={
153                                         <Tooltip title={item.name} placement="bottom-start">
154                                             <Typography noWrap variant='h6'>
155                                                 {item.name}
156                                             </Typography>
157                                         </Tooltip>
158                                     }
159                                     subheader={
160                                         <Tooltip title={item.description || '(no-description)'} placement="bottom-start">
161                                             <Typography noWrap variant='body1' color='inherit'>
162                                                 {item.description || '(no-description)'}
163                                             </Typography>
164                                         </Tooltip>}
165                                     action={
166                                         <Tooltip title="More options" disableFocusListener>
167                                             <IconButton
168                                                 aria-label="More options"
169                                                 onClick={event => this.handleContextMenu(event)}>
170                                                 <MoreVerticalIcon />
171                                             </IconButton>
172                                         </Tooltip>}
173
174                                 />
175
176                                 <CardContent className={classes.content}>
177                                     <WorkflowDetailsAttributes workflow={item} />
178                                 </CardContent>
179                             </Card>
180                         </MPVPanelContent>
181                         <MPVPanelContent forwardProps xs maxHeight="100%">
182                             <WorkflowProcessesPanel />
183                         </MPVPanelContent>
184                         <MPVPanelContent forwardProps xs data-cy="process-outputs" maxHeight="100%">
185                             <ProcessIOCard
186                                 label={ProcessIOCardType.OUTPUT}
187                                 params={outputParams}
188                                 raw={{}}
189                                 forceShowParams={true}
190                             />
191                         </MPVPanelContent>
192                         <MPVPanelContent forwardProps xs data-cy="process-inputs" maxHeight="100%">
193                             <ProcessIOCard
194                                 label={ProcessIOCardType.INPUT}
195                                 params={inputParams}
196                                 raw={{}}
197                                 forceShowParams={true}
198                             />
199                         </MPVPanelContent>
200                         <MPVPanelContent xs maxHeight="100%">
201                             <Card className={classes.filesCard}>
202                                 <CardHeader title="Workflow Definition" />
203                                 <ProcessOutputCollectionFiles isWritable={false} currentItemUuid={workflowCollection} />
204                             </Card>
205                         </MPVPanelContent>
206                     </MPVContainer>
207                     :
208                     <NotFoundView
209                         icon={WorkflowIcon}
210                         messages={["Workflow not found"]}
211                     />
212             }
213
214             handleContextMenu = (event: React.MouseEvent<any>) => {
215                 const { uuid, ownerUuid, name, description,
216                     kind } = this.props.item;
217                 const menuKind = this.props.dispatch<any>(resourceUuidToContextMenuKind(uuid));
218                 const resource = {
219                     uuid,
220                     ownerUuid,
221                     name,
222                     description,
223                     kind,
224                     menuKind,
225                 };
226                 // Avoid expanding/collapsing the panel
227                 event.stopPropagation();
228                 this.props.dispatch<any>(openContextMenu(event, resource));
229             }
230         }
231     )
232 );