1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
5 import React, { useState } from 'react';
6 import { CustomStyleRulesCallback } from 'common/custom-theme';
7 import { Card, CardHeader, IconButton, CardContent, Tooltip, Grid, Typography } from '@mui/material';
8 import { WithStyles } from '@mui/styles';
9 import withStyles from '@mui/styles/withStyles';
10 import { useAsyncInterval } from 'common/use-async-interval';
11 import { ArvadosTheme } from 'common/custom-theme';
23 } from 'components/icon/icon';
24 import { Process, isProcessRunning } from 'store/processes/process';
25 import { MPVPanelProps } from 'components/multi-panel-view/multi-panel-view';
29 } from 'views/process-panel/process-log-form';
30 import { ProcessLogCodeSnippet } from 'views/process-panel/process-log-code-snippet';
31 import { DefaultView } from 'components/default-view/default-view';
32 import { CodeSnippetDataProps } from 'components/code-snippet/code-snippet';
33 import CopyToClipboard from 'react-copy-to-clipboard';
35 type CssRules = 'card' | 'content' | 'title' | 'iconHeader' | 'header' | 'root' | 'logViewer' | 'logViewerContainer';
37 const styles: CustomStyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
42 paddingTop: theme.spacing(1),
43 paddingBottom: theme.spacing(1),
46 padding: theme.spacing(0),
51 overflowY: 'scroll', // Required for MacOS's Safari -- See #19687
58 paddingTop: theme.spacing(0.5),
59 color: theme.customs.colors.greyD
63 color: theme.customs.colors.greyL
70 export interface ProcessLogsCardDataProps {
72 selectedFilter: FilterOption;
73 filters: FilterOption[];
76 export interface ProcessLogsCardActionProps {
77 onLogFilterChange: (filter: FilterOption) => void;
78 navigateToLog: (uuid: string) => void;
79 onCopy: (text: string) => void;
80 pollProcessLogs: (processUuid: string) => Promise<void>;
83 type ProcessLogsCardProps = ProcessLogsCardDataProps
84 & ProcessLogsCardActionProps
85 & CodeSnippetDataProps
86 & WithStyles<CssRules>
89 export const ProcessLogsCard = withStyles(styles)(
90 ({ classes, process, filters, selectedFilter, lines,
91 onLogFilterChange, navigateToLog, onCopy, pollProcessLogs,
92 doHidePanel, doMaximizePanel, doUnMaximizePanel, panelMaximized, panelName }: ProcessLogsCardProps) => {
93 const [wordWrap, setWordWrap] = useState<boolean>(true);
94 const [fontSize, setFontSize] = useState<number>(3);
95 const fontBaseSize = 10;
96 const fontStepSize = 1;
98 useAsyncInterval(() => (
99 pollProcessLogs(process.containerRequest.uuid)
100 ), isProcessRunning(process) ? 2000 : null);
103 <Grid item className={classes.root} xs={12}>
104 <Card className={classes.card}>
105 <CardHeader className={classes.header}
106 avatar={<LogIcon className={classes.iconHeader} />}
107 action={<Grid container direction='row' alignItems='center'>
109 <ProcessLogForm selectedFilter={selectedFilter}
110 filters={filters} onChange={onLogFilterChange} />
113 <Tooltip title="Decrease font size" disableFocusListener>
114 <IconButton onClick={() => fontSize > 1 && setFontSize(fontSize-1)} size="large">
120 <Tooltip title="Increase font size" disableFocusListener>
121 <IconButton onClick={() => fontSize < 5 && setFontSize(fontSize+1)} size="large">
127 <Tooltip title="Copy link to clipboard" disableFocusListener>
128 <IconButton size="large">
129 <CopyToClipboard text={lines.join()} onCopy={() => onCopy("Log copied to clipboard")}>
136 <Tooltip title={`${wordWrap ? 'Disable' : 'Enable'} word wrapping`} disableFocusListener>
137 <IconButton onClick={() => setWordWrap(!wordWrap)} size="large">
138 {wordWrap ? <WordWrapOffIcon /> : <WordWrapOnIcon />}
143 <Tooltip title="Go to Log collection" disableFocusListener>
145 onClick={() => navigateToLog(process.containerRequest.logUuid!)}
151 { doUnMaximizePanel && panelMaximized &&
152 <Tooltip title={`Unmaximize ${panelName || 'panel'}`} disableFocusListener>
153 <IconButton onClick={doUnMaximizePanel} size="large"><UnMaximizeIcon /></IconButton>
155 { doMaximizePanel && !panelMaximized &&
156 <Tooltip title={`Maximize ${panelName || 'panel'}`} disableFocusListener>
157 <IconButton onClick={doMaximizePanel} size="large"><MaximizeIcon /></IconButton>
160 <Tooltip title={`Close ${panelName || 'panel'}`} disableFocusListener>
161 <IconButton disabled={panelMaximized} onClick={doHidePanel} size="large"><CloseIcon /></IconButton>
165 <Typography noWrap variant='h6' className={classes.title}>
169 <CardContent className={classes.content}>
172 className={classes.logViewerContainer}
176 <Grid className={classes.logViewer} item xs>
177 <ProcessLogCodeSnippet fontSize={fontBaseSize+(fontStepSize*fontSize)} wordWrap={wordWrap} lines={lines} />
182 messages={['No logs yet']} />