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