18881: Make all process status widgets consistent. Updates tests.
[arvados-workbench2.git] / src / views / process-panel / process-information-card.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     Card,
11     CardHeader,
12     IconButton,
13     CardContent,
14     Grid,
15     Typography,
16     Tooltip
17 } from '@material-ui/core';
18 import { ArvadosTheme } from 'common/custom-theme';
19 import { CloseIcon, MoreOptionsIcon, ProcessIcon } from 'components/icon/icon';
20 import { DetailsAttribute } from 'components/details-attribute/details-attribute';
21 import { Process } from 'store/processes/process';
22 import { formatDate } from 'common/formatters';
23 import classNames from 'classnames';
24 import { ContainerState } from 'models/container';
25 import { MPVPanelProps } from 'components/multi-panel-view/multi-panel-view';
26 import { ProcessRuntimeStatus } from 'views-components/process-runtime-status/process-runtime-status';
27 import { ProcessStatus } from 'views-components/data-explorer/renderers';
28
29 type CssRules = 'card' | 'iconHeader' | 'label' | 'value' | 'link' | 'content' | 'title' | 'avatar' | 'cancelButton' | 'header';
30
31 const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
32     card: {
33         height: '100%'
34     },
35     header: {
36         paddingTop: theme.spacing.unit,
37         paddingBottom: theme.spacing.unit,
38     },
39     iconHeader: {
40         fontSize: '1.875rem',
41         color: theme.customs.colors.green700,
42     },
43     avatar: {
44         alignSelf: 'flex-start',
45         paddingTop: theme.spacing.unit * 0.5
46     },
47     label: {
48         display: 'flex',
49         justifyContent: 'flex-start',
50         fontSize: '0.875rem',
51         marginRight: theme.spacing.unit * 3,
52         paddingRight: theme.spacing.unit
53     },
54     value: {
55         textTransform: 'none',
56         fontSize: '0.875rem',
57     },
58     link: {
59         fontSize: '0.875rem',
60         color: theme.palette.primary.main,
61         '&:hover': {
62             cursor: 'pointer'
63         }
64     },
65     content: {
66         paddingTop: '0px',
67         paddingLeft: theme.spacing.unit * 1,
68         paddingRight: theme.spacing.unit * 1,
69         '&:last-child': {
70             paddingBottom: theme.spacing.unit * 1,
71         }
72     },
73     title: {
74         overflow: 'hidden',
75         paddingTop: theme.spacing.unit * 0.5
76     },
77     cancelButton: {
78         paddingRight: theme.spacing.unit * 2,
79         fontSize: '14px',
80         color: theme.customs.colors.red900,
81         "&:hover": {
82             cursor: 'pointer'
83         }
84     }
85 });
86
87 export interface ProcessInformationCardDataProps {
88     process: Process;
89     onContextMenu: (event: React.MouseEvent<HTMLElement>) => void;
90     openProcessInputDialog: (uuid: string) => void;
91     navigateToOutput: (uuid: string) => void;
92     openWorkflow: (uuid: string) => void;
93     cancelProcess: (uuid: string) => void;
94 }
95
96 type ProcessInformationCardProps = ProcessInformationCardDataProps & WithStyles<CssRules, true> & MPVPanelProps;
97
98 export const ProcessInformationCard = withStyles(styles, { withTheme: true })(
99     ({ classes, process, onContextMenu, theme, openProcessInputDialog, navigateToOutput, openWorkflow, cancelProcess, doHidePanel, panelName }: ProcessInformationCardProps) => {
100         const { container } = process;
101         const startedAt = container ? formatDate(container.startedAt) : 'N/A';
102         const finishedAt = container ? formatDate(container.finishedAt) : 'N/A';
103         return <Card className={classes.card}>
104             <CardHeader
105                 className={classes.header}
106                 classes={{
107                     content: classes.title,
108                     avatar: classes.avatar
109                 }}
110                 avatar={<ProcessIcon className={classes.iconHeader} />}
111                 action={
112                     <div>
113                         {process.container && process.container.state === ContainerState.RUNNING &&
114                             <span className={classes.cancelButton} onClick={() => cancelProcess(process.containerRequest.uuid)}>Cancel</span>}
115                         <ProcessStatus uuid={process.containerRequest.uuid} />
116                         <Tooltip title="More options" disableFocusListener>
117                             <IconButton
118                                 aria-label="More options"
119                                 onClick={event => onContextMenu(event)}>
120                                 <MoreOptionsIcon />
121                             </IconButton>
122                         </Tooltip>
123                         { doHidePanel &&
124                         <Tooltip title={`Close ${panelName || 'panel'}`} disableFocusListener>
125                             <IconButton onClick={doHidePanel}><CloseIcon /></IconButton>
126                         </Tooltip> }
127                     </div>
128                 }
129                 title={ !!process.containerRequest.name &&
130                     <Typography noWrap variant='h6' color='inherit'>
131                         {process.containerRequest.name}
132                     </Typography>
133                 }
134                 subheader={
135                     <Typography noWrap variant='body1' color='inherit'>
136                         {process.containerRequest.description}
137                     </Typography>
138                 }
139             />
140             <CardContent className={classes.content}>
141                 <Grid container>
142                     <Grid item xs={12}>
143                         <ProcessRuntimeStatus runtimeStatus={process.container?.runtimeStatus} />
144                     </Grid>
145                     <Grid item xs={6}>
146                         <DetailsAttribute classLabel={classes.label} classValue={classes.value}
147                             label='Started at'
148                             value={startedAt} />
149                         <DetailsAttribute classLabel={classes.label} classValue={classes.value}
150                             label='Finished at'
151                             value={finishedAt} />
152                         {process.containerRequest.properties.workflowUuid &&
153                             <span onClick={() => openWorkflow(process.containerRequest.properties.workflowUuid)}>
154                                 <DetailsAttribute classLabel={classes.label} classValue={classNames(classes.value, classes.link)}
155                                     label='Workflow' value={process.containerRequest.properties.workflowName} />
156                             </span>}
157                     </Grid>
158                     <Grid item xs={6}>
159                         <span onClick={() => navigateToOutput(process.containerRequest.outputUuid!)}>
160                             <DetailsAttribute classLabel={classes.link} label='Outputs' />
161                         </span>
162                         <span onClick={() => openProcessInputDialog(process.containerRequest.uuid)}>
163                             <DetailsAttribute classLabel={classes.link} label='Inputs' />
164                         </span>
165                     </Grid>
166                 </Grid>
167             </CardContent>
168         </Card>;
169     }
170 );