21720: removed unused accordion styles
[arvados.git] / services / workbench2 / src / components / subprocess-progress-bar / subprocess-progress-bar.tsx
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 import React, { useEffect, useState } from "react";
6 import { CustomStyleRulesCallback } from 'common/custom-theme';
7 import { Tooltip } from "@mui/material";
8 import { WithStyles } from '@mui/styles';
9 import withStyles from '@mui/styles/withStyles';
10 import { CProgressStacked, CProgress } from '@coreui/react';
11 import { useAsyncInterval } from "common/use-async-interval";
12 import { Process, isProcessRunning } from "store/processes/process";
13 import { connect } from "react-redux";
14 import { Dispatch } from "redux";
15 import { fetchSubprocessProgress } from "store/subprocess-panel/subprocess-panel-actions";
16 import { ProcessStatusFilter } from "store/resource-type-filters/resource-type-filters";
17
18 type CssRules = 'progressWrapper' | 'progressStacked';
19
20 const styles: CustomStyleRulesCallback<CssRules> = (theme) => ({
21     progressWrapper: {
22         margin: "28px 0 0",
23         flexGrow: 1,
24         flexBasis: "100px",
25     },
26     progressStacked: {
27         border: "1px solid gray",
28         height: "10px",
29         // Override stripe color to be close to white
30         "& .progress-bar-striped": {
31             backgroundImage:
32                 "linear-gradient(45deg,rgba(255,255,255,.80) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.80) 50%,rgba(255,255,255,.80) 75%,transparent 75%,transparent)",
33         },
34     },
35 });
36
37 export interface ProgressBarDataProps {
38     process: Process;
39 }
40
41 export interface ProgressBarActionProps {
42     fetchSubprocessProgress: (requestingContainerUuid: string) => Promise<ProgressBarData | undefined>;
43 }
44
45 type ProgressBarProps = ProgressBarDataProps & ProgressBarActionProps & WithStyles<CssRules>;
46
47 export type ProgressBarData = {
48     [ProcessStatusFilter.COMPLETED]: number;
49     [ProcessStatusFilter.RUNNING]: number;
50     [ProcessStatusFilter.FAILED]: number;
51     [ProcessStatusFilter.QUEUED]: number;
52 };
53
54 const mapDispatchToProps = (dispatch: Dispatch): ProgressBarActionProps => ({
55     fetchSubprocessProgress: (requestingContainerUuid: string) => {
56         return dispatch<any>(fetchSubprocessProgress(requestingContainerUuid));
57     },
58 });
59
60 export const SubprocessProgressBar = connect(null, mapDispatchToProps)(withStyles(styles)(
61     ({ process, classes, fetchSubprocessProgress }: ProgressBarProps) => {
62
63         const [progressData, setProgressData] = useState<ProgressBarData | undefined>(undefined);
64         const requestingContainerUuid = process.containerRequest.containerUuid;
65         const isRunning = isProcessRunning(process);
66
67         useAsyncInterval(async () => (
68             requestingContainerUuid && setProgressData(await fetchSubprocessProgress(requestingContainerUuid))
69         ), isRunning ? 5000 : null);
70
71         useEffect(() => {
72             if (!isRunning && requestingContainerUuid) {
73                 fetchSubprocessProgress(requestingContainerUuid)
74                     .then(result => setProgressData(result));
75             }
76         }, [fetchSubprocessProgress, isRunning, requestingContainerUuid]);
77
78         let tooltip = "";
79         if (progressData) {
80             let total = 0;
81             [ProcessStatusFilter.COMPLETED,
82             ProcessStatusFilter.RUNNING,
83             ProcessStatusFilter.FAILED,
84             ProcessStatusFilter.QUEUED].forEach(psf => {
85                 if (progressData[psf] > 0) {
86                     if (tooltip.length > 0) { tooltip += ", "; }
87                     tooltip += `${progressData[psf]} ${psf}`;
88                     total += progressData[psf];
89                 }
90             });
91             if (total > 0) {
92                 if (tooltip.length > 0) { tooltip += ", "; }
93                 tooltip += `${total} Total`;
94             }
95         }
96
97         return progressData !== undefined && getStatusTotal(progressData) > 0 ? <div className={classes.progressWrapper}>
98             <Tooltip title={tooltip}>
99                 <CProgressStacked className={classes.progressStacked}>
100                     <CProgress height={10} color="success"
101                         value={getStatusPercent(progressData, ProcessStatusFilter.COMPLETED)} />
102                     <CProgress height={10} color="success" variant="striped"
103                         value={getStatusPercent(progressData, ProcessStatusFilter.RUNNING)} />
104                     <CProgress height={10} color="danger"
105                         value={getStatusPercent(progressData, ProcessStatusFilter.FAILED)} />
106                     <CProgress height={10} color="secondary" variant="striped"
107                         value={getStatusPercent(progressData, ProcessStatusFilter.QUEUED)} />
108                 </CProgressStacked>
109             </Tooltip>
110         </div> : <></>;
111     }
112 ));
113
114 const getStatusTotal = (progressData: ProgressBarData) =>
115     (Object.keys(progressData).reduce((accumulator, key) => (accumulator += progressData[key]), 0));
116
117 /**
118  * Gets the integer percent value for process status
119  */
120 const getStatusPercent = (progressData: ProgressBarData, status: keyof ProgressBarData) =>
121     (progressData[status] / getStatusTotal(progressData) * 100);