1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
5 import React, { useEffect, useState } from "react";
6 import { StyleRulesCallback, Tooltip, WithStyles, withStyles } from "@material-ui/core";
7 import { CProgressStacked, CProgress } from '@coreui/react';
8 import { useAsyncInterval } from "common/use-async-interval";
9 import { Process, isProcessRunning } from "store/processes/process";
10 import { connect } from "react-redux";
11 import { Dispatch } from "redux";
12 import { fetchSubprocessProgress } from "store/subprocess-panel/subprocess-panel-actions";
13 import { ProcessStatusFilter } from "store/resource-type-filters/resource-type-filters";
15 type CssRules = 'progressWrapper' | 'progressStacked' ;
17 const styles: StyleRulesCallback<CssRules> = (theme) => ({
24 border: "1px solid gray",
26 // Override stripe color to be close to white
27 "& .progress-bar-striped": {
29 "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)",
34 export interface ProgressBarDataProps {
38 export interface ProgressBarActionProps {
39 fetchSubprocessProgress: (requestingContainerUuid: string) => Promise<ProgressBarData | undefined>;
42 type ProgressBarProps = ProgressBarDataProps & ProgressBarActionProps & WithStyles<CssRules>;
44 export type ProgressBarData = {
45 [ProcessStatusFilter.COMPLETED]: number;
46 [ProcessStatusFilter.RUNNING]: number;
47 [ProcessStatusFilter.FAILED]: number;
48 [ProcessStatusFilter.QUEUED]: number;
51 const mapDispatchToProps = (dispatch: Dispatch): ProgressBarActionProps => ({
52 fetchSubprocessProgress: (requestingContainerUuid: string) => {
53 return dispatch<any>(fetchSubprocessProgress(requestingContainerUuid));
57 export const SubprocessProgressBar = connect(null, mapDispatchToProps)(withStyles(styles)(
58 ({process, classes, fetchSubprocessProgress}: ProgressBarProps) => {
60 const [progressData, setProgressData] = useState<ProgressBarData|undefined>(undefined);
61 const requestingContainerUuid = process.containerRequest.containerUuid;
62 const isRunning = isProcessRunning(process);
64 useAsyncInterval(async () => (
65 requestingContainerUuid && setProgressData(await fetchSubprocessProgress(requestingContainerUuid))
66 ), isRunning ? 5000 : null);
69 if (!isRunning && requestingContainerUuid) {
70 fetchSubprocessProgress(requestingContainerUuid)
71 .then(result => setProgressData(result));
73 }, [fetchSubprocessProgress, isRunning, requestingContainerUuid]);
75 return progressData !== undefined && getStatusTotal(progressData) > 0 ? <div className={classes.progressWrapper}>
76 <CProgressStacked className={classes.progressStacked}>
77 <Tooltip title={`${progressData[ProcessStatusFilter.COMPLETED]} Completed`}>
78 <CProgress height={10} color="success"
79 value={getStatusPercent(progressData, ProcessStatusFilter.COMPLETED)} />
81 <Tooltip title={`${progressData[ProcessStatusFilter.RUNNING]} Running`}>
82 <CProgress height={10} color="success" variant="striped"
83 value={getStatusPercent(progressData, ProcessStatusFilter.RUNNING)} />
85 <Tooltip title={`${progressData[ProcessStatusFilter.FAILED]} Failed`}>
86 <CProgress height={10} color="danger"
87 value={getStatusPercent(progressData, ProcessStatusFilter.FAILED)} />
89 <Tooltip title={`${progressData[ProcessStatusFilter.QUEUED]} Queued`}>
90 <CProgress height={10} color="secondary" variant="striped"
91 value={getStatusPercent(progressData, ProcessStatusFilter.QUEUED)} />
98 const getStatusTotal = (progressData: ProgressBarData) =>
99 (Object.keys(progressData).reduce((accumulator, key) => (accumulator += progressData[key]), 0));
102 * Gets the integer percent value for process status
104 const getStatusPercent = (progressData: ProgressBarData, status: keyof ProgressBarData) =>
105 (progressData[status] / getStatusTotal(progressData) * 100);