Merge branch 'main' into 19438-resource-panel refs #19438
[arvados-workbench2.git] / src / views / process-panel / process-log-card.tsx
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 import React, { useState } from 'react';
6 import {
7     StyleRulesCallback,
8     WithStyles,
9     withStyles,
10     Card,
11     CardHeader,
12     IconButton,
13     CardContent,
14     Tooltip,
15     Grid,
16     Typography,
17 } from '@material-ui/core';
18 import { ArvadosTheme } from 'common/custom-theme';
19 import {
20     CloseIcon,
21     CollectionIcon,
22     CopyIcon,
23     LogIcon,
24     MaximizeIcon,
25     UnMaximizeIcon,
26     TextDecreaseIcon,
27     TextIncreaseIcon,
28     WordWrapOffIcon,
29     WordWrapOnIcon,
30 } from 'components/icon/icon';
31 import { Process } from 'store/processes/process';
32 import { MPVPanelProps } from 'components/multi-panel-view/multi-panel-view';
33 import {
34     FilterOption,
35     ProcessLogForm
36 } from 'views/process-panel/process-log-form';
37 import { ProcessLogCodeSnippet } from 'views/process-panel/process-log-code-snippet';
38 import { DefaultView } from 'components/default-view/default-view';
39 import { CodeSnippetDataProps } from 'components/code-snippet/code-snippet';
40 import CopyToClipboard from 'react-copy-to-clipboard';
41
42 type CssRules = 'card' | 'content' | 'title' | 'iconHeader' | 'header' | 'root' | 'logViewer' | 'logViewerContainer';
43
44 const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
45     card: {
46         height: '100%'
47     },
48     header: {
49         paddingTop: theme.spacing.unit,
50         paddingBottom: theme.spacing.unit,
51     },
52     content: {
53         padding: theme.spacing.unit * 0,
54         height: '100%',
55     },
56     logViewer: {
57         height: '100%',
58         overflowY: 'scroll', // Required for MacOS's Safari -- See #19687
59     },
60     logViewerContainer: {
61         height: '100%',
62     },
63     title: {
64         overflow: 'hidden',
65         paddingTop: theme.spacing.unit * 0.5,
66         color: theme.customs.colors.greyD
67     },
68     iconHeader: {
69         fontSize: '1.875rem',
70         color: theme.customs.colors.greyL
71     },
72     root: {
73         height: '100%',
74     },
75 });
76
77 export interface ProcessLogsCardDataProps {
78     process: Process;
79     selectedFilter: FilterOption;
80     filters: FilterOption[];
81 }
82
83 export interface ProcessLogsCardActionProps {
84     onLogFilterChange: (filter: FilterOption) => void;
85     navigateToLog: (uuid: string) => void;
86     onCopy: (text: string) => void;
87 }
88
89 type ProcessLogsCardProps = ProcessLogsCardDataProps
90     & ProcessLogsCardActionProps
91     & CodeSnippetDataProps
92     & WithStyles<CssRules>
93     & MPVPanelProps;
94
95 export const ProcessLogsCard = withStyles(styles)(
96     ({ classes, process, filters, selectedFilter, lines,
97         onLogFilterChange, navigateToLog, onCopy,
98         doHidePanel, doMaximizePanel, doUnMaximizePanel, panelMaximized, panelName }: ProcessLogsCardProps) => {
99         const [wordWrap, setWordWrap] = useState<boolean>(true);
100         const [fontSize, setFontSize] = useState<number>(3);
101         const fontBaseSize = 10;
102         const fontStepSize = 1;
103
104         return <Grid item className={classes.root} xs={12}>
105             <Card className={classes.card}>
106                 <CardHeader className={classes.header}
107                     avatar={<LogIcon className={classes.iconHeader} />}
108                     action={<Grid container direction='row' alignItems='center'>
109                         <Grid item>
110                             <ProcessLogForm selectedFilter={selectedFilter}
111                                 filters={filters} onChange={onLogFilterChange} />
112                         </Grid>
113                         <Grid item>
114                             <Tooltip title="Decrease font size" disableFocusListener>
115                                 <IconButton onClick={() => fontSize > 1 && setFontSize(fontSize-1)}>
116                                     <TextDecreaseIcon />
117                                 </IconButton>
118                             </Tooltip>
119                         </Grid>
120                         <Grid item>
121                             <Tooltip title="Increase font size" disableFocusListener>
122                                 <IconButton onClick={() => fontSize < 5 && setFontSize(fontSize+1)}>
123                                     <TextIncreaseIcon />
124                                 </IconButton>
125                             </Tooltip>
126                         </Grid>
127                         <Grid item>
128                             <Tooltip title="Copy to clipboard" disableFocusListener>
129                                 <IconButton>
130                                     <CopyToClipboard text={lines.join()} onCopy={() => onCopy("Log copied to clipboard")}>
131                                         <CopyIcon />
132                                     </CopyToClipboard>
133                                 </IconButton>
134                             </Tooltip>
135                         </Grid>
136                         <Grid item>
137                             <Tooltip title={`${wordWrap ? 'Disable' : 'Enable'} word wrapping`} disableFocusListener>
138                                 <IconButton onClick={() => setWordWrap(!wordWrap)}>
139                                     {wordWrap ? <WordWrapOffIcon /> : <WordWrapOnIcon />}
140                                 </IconButton>
141                             </Tooltip>
142                         </Grid>
143                         <Grid item>
144                             <Tooltip title="Go to Log collection" disableFocusListener>
145                                 <IconButton onClick={() => navigateToLog(process.containerRequest.logUuid!)}>
146                                     <CollectionIcon />
147                                 </IconButton>
148                             </Tooltip>
149                         </Grid>
150                         { doUnMaximizePanel && panelMaximized &&
151                         <Tooltip title={`Unmaximize ${panelName || 'panel'}`} disableFocusListener>
152                             <IconButton onClick={doUnMaximizePanel}><UnMaximizeIcon /></IconButton>
153                         </Tooltip> }
154                         { doMaximizePanel && !panelMaximized &&
155                         <Tooltip title={`Maximize ${panelName || 'panel'}`} disableFocusListener>
156                             <IconButton onClick={doMaximizePanel}><MaximizeIcon /></IconButton>
157                         </Tooltip> }
158                         { doHidePanel &&
159                         <Tooltip title={`Close ${panelName || 'panel'}`} disableFocusListener>
160                             <IconButton disabled={panelMaximized} onClick={doHidePanel}><CloseIcon /></IconButton>
161                         </Tooltip> }
162                     </Grid>}
163                     title={
164                         <Typography noWrap variant='h6' className={classes.title}>
165                             Logs
166                         </Typography>}
167                 />
168                 <CardContent className={classes.content}>
169                     {lines.length > 0
170                         ? < Grid
171                             className={classes.logViewerContainer}
172                             container
173                             spacing={24}
174                             direction='column'>
175                             <Grid className={classes.logViewer} item xs>
176                                 <ProcessLogCodeSnippet fontSize={fontBaseSize+(fontStepSize*fontSize)} wordWrap={wordWrap} lines={lines} />
177                             </Grid>
178                         </Grid>
179                         : <DefaultView
180                             icon={LogIcon}
181                             messages={['No logs yet']} />
182                     }
183                 </CardContent>
184             </Card>
185         </Grid >
186 });
187