21225: Move progressWrapper to data explorer for conditional styles
[arvados.git] / services / workbench2 / src / components / subprocess-progress-bar / subprocess-progress-bar.tsx
index b21d879185ace8f4e2c4f66fc5da653d90438ed6..78df83f6ca86f39deeb98b39ac9050e99707d1ad 100644 (file)
@@ -6,20 +6,22 @@ import React, { useEffect, useState } from "react";
 import { StyleRulesCallback, Tooltip, WithStyles, withStyles } from "@material-ui/core";
 import { CProgressStacked, CProgress } from '@coreui/react';
 import { useAsyncInterval } from "common/use-async-interval";
-import { Process, isProcessRunning } from "store/processes/process";
+import { Process } from "store/processes/process";
 import { connect } from "react-redux";
 import { Dispatch } from "redux";
-import { fetchSubprocessProgress } from "store/subprocess-panel/subprocess-panel-actions";
-import { ProcessStatusFilter } from "store/resource-type-filters/resource-type-filters";
+import { fetchProcessProgressBarStatus } from "store/subprocess-panel/subprocess-panel-actions";
+import { ProcessStatusFilter, serializeOnlyProcessTypeFilters } from "store/resource-type-filters/resource-type-filters";
+import { ProjectResource } from "models/project";
+import { getDataExplorer } from "store/data-explorer/data-explorer-reducer";
+import { RootState } from "store/store";
+import { ProcessResource } from "models/process";
+import { getDataExplorerColumnFilters } from "store/data-explorer/data-explorer-middleware-service";
+import { ProjectPanelRunColumnNames } from "views/project-panel/project-panel-run";
+import { DataColumns } from "components/data-table/data-table";
 
-type CssRules = 'progressWrapper' | 'progressStacked' ;
+type CssRules = 'progressStacked';
 
 const styles: StyleRulesCallback<CssRules> = (theme) => ({
-    progressWrapper: {
-        margin: "28px 0 0",
-        flexGrow: 1,
-        flexBasis: "100px",
-    },
     progressStacked: {
         border: "1px solid gray",
         height: "10px",
@@ -32,74 +34,116 @@ const styles: StyleRulesCallback<CssRules> = (theme) => ({
 });
 
 export interface ProgressBarDataProps {
-    process: Process;
+    parentResource: Process | ProjectResource | undefined;
+    dataExplorerId?: string;
+    typeFilter?: string;
 }
 
 export interface ProgressBarActionProps {
-    fetchSubprocessProgress: (requestingContainerUuid: string) => Promise<ProgressBarData | undefined>;
+    fetchProcessProgressBarStatus: (parentResource: Process | ProjectResource, typeFilter?: string) => Promise<ProgressBarStatus | undefined>;
 }
 
 type ProgressBarProps = ProgressBarDataProps & ProgressBarActionProps & WithStyles<CssRules>;
 
-export type ProgressBarData = {
+export type ProgressBarCounts = {
     [ProcessStatusFilter.COMPLETED]: number;
     [ProcessStatusFilter.RUNNING]: number;
     [ProcessStatusFilter.FAILED]: number;
     [ProcessStatusFilter.QUEUED]: number;
 };
 
+export type ProgressBarStatus = {
+    counts: ProgressBarCounts;
+    isRunning: boolean;
+};
+
+const mapStateToProps = (state: RootState, props: ProgressBarDataProps) => {
+    let typeFilter: string | undefined = undefined;
+
+    if (props.dataExplorerId) {
+        const dataExplorerState = getDataExplorer(state.dataExplorer, props.dataExplorerId);
+        const columns = dataExplorerState.columns as DataColumns<string, ProcessResource>;
+        typeFilter = serializeOnlyProcessTypeFilters(getDataExplorerColumnFilters(columns, ProjectPanelRunColumnNames.TYPE));
+    }
+
+    return { typeFilter };
+};
+
 const mapDispatchToProps = (dispatch: Dispatch): ProgressBarActionProps => ({
-    fetchSubprocessProgress: (requestingContainerUuid: string) => {
-        return dispatch<any>(fetchSubprocessProgress(requestingContainerUuid));
+    fetchProcessProgressBarStatus: (parentResource: Process | ProjectResource, typeFilter?: string) => {
+        return dispatch<any>(fetchProcessProgressBarStatus(parentResource, typeFilter));
     },
 });
 
-export const SubprocessProgressBar = connect(null, mapDispatchToProps)(withStyles(styles)(
-    ({process, classes, fetchSubprocessProgress}: ProgressBarProps) => {
+export const SubprocessProgressBar = connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(
+    ({ parentResource, typeFilter, classes, fetchProcessProgressBarStatus }: ProgressBarProps) => {
 
-        const [progressData, setProgressData] = useState<ProgressBarData|undefined>(undefined);
-        const requestingContainerUuid = process.containerRequest.containerUuid;
-        const isRunning = isProcessRunning(process);
+        const [progressCounts, setProgressData] = useState<ProgressBarCounts | undefined>(undefined);
+        const [isRunning, setIsRunning] = useState<boolean>(false);
 
-        useAsyncInterval(async () => (
-            requestingContainerUuid && setProgressData(await fetchSubprocessProgress(requestingContainerUuid))
-        ), isRunning ? 5000 : null);
+        useAsyncInterval(async () => {
+            if (parentResource) {
+                fetchProcessProgressBarStatus(parentResource, typeFilter)
+                    .then(result => {
+                        if (result) {
+                            setProgressData(result.counts);
+                            setIsRunning(result.isRunning);
+                        }
+                    });
+            }
+        }, isRunning ? 5000 : null);
 
         useEffect(() => {
-            if (!isRunning && requestingContainerUuid) {
-                fetchSubprocessProgress(requestingContainerUuid)
-                    .then(result => setProgressData(result));
+            if (!isRunning && parentResource) {
+                fetchProcessProgressBarStatus(parentResource, typeFilter)
+                    .then(result => {
+                        if (result) {
+                            setProgressData(result.counts);
+                            setIsRunning(result.isRunning);
+                        }
+                    });
+            }
+        }, [fetchProcessProgressBarStatus, isRunning, parentResource, typeFilter]);
+
+        let tooltip = "";
+        if (progressCounts) {
+            let total = 0;
+            [ProcessStatusFilter.COMPLETED,
+            ProcessStatusFilter.RUNNING,
+            ProcessStatusFilter.FAILED,
+            ProcessStatusFilter.QUEUED].forEach(psf => {
+                if (progressCounts[psf] > 0) {
+                    if (tooltip.length > 0) { tooltip += ", "; }
+                    tooltip += `${progressCounts[psf]} ${psf}`;
+                    total += progressCounts[psf];
+                }
+            });
+            if (total > 0) {
+                if (tooltip.length > 0) { tooltip += ", "; }
+                tooltip += `${total} Total`;
             }
-        }, [fetchSubprocessProgress, isRunning, requestingContainerUuid]);
+        }
 
-        return progressData !== undefined && getStatusTotal(progressData) > 0 ? <div className={classes.progressWrapper}>
+        return progressCounts !== undefined && getStatusTotal(progressCounts) > 0 ? <Tooltip title={tooltip}>
             <CProgressStacked className={classes.progressStacked}>
-                <Tooltip title={`${progressData[ProcessStatusFilter.COMPLETED]} Completed`}>
-                    <CProgress height={10} color="success"
-                        value={getStatusPercent(progressData, ProcessStatusFilter.COMPLETED)} />
-                </Tooltip>
-                <Tooltip title={`${progressData[ProcessStatusFilter.RUNNING]} Running`}>
-                    <CProgress height={10} color="success" variant="striped"
-                        value={getStatusPercent(progressData, ProcessStatusFilter.RUNNING)} />
-                </Tooltip>
-                <Tooltip title={`${progressData[ProcessStatusFilter.FAILED]} Failed`}>
-                    <CProgress height={10} color="danger"
-                        value={getStatusPercent(progressData, ProcessStatusFilter.FAILED)} />
-                </Tooltip>
-                <Tooltip title={`${progressData[ProcessStatusFilter.QUEUED]} Queued`}>
-                    <CProgress height={10} color="secondary" variant="striped"
-                        value={getStatusPercent(progressData, ProcessStatusFilter.QUEUED)} />
-                </Tooltip>
+                <CProgress height={10} color="success"
+                    value={getStatusPercent(progressCounts, ProcessStatusFilter.COMPLETED)} />
+                <CProgress height={10} color="success" variant="striped"
+                    value={getStatusPercent(progressCounts, ProcessStatusFilter.RUNNING)} />
+                <CProgress height={10} color="danger"
+                    value={getStatusPercent(progressCounts, ProcessStatusFilter.FAILED)} />
+                <CProgress height={10} color="secondary" variant="striped"
+                    value={getStatusPercent(progressCounts, ProcessStatusFilter.QUEUED)} />
             </CProgressStacked>
-        </div> : <></>;
+        </Tooltip> : <></>;
     }
 ));
 
-const getStatusTotal = (progressData: ProgressBarData) =>
-    (Object.keys(progressData).reduce((accumulator, key) => (accumulator += progressData[key]), 0));
+const getStatusTotal = (progressCounts: ProgressBarCounts) =>
+    (Object.keys(progressCounts).reduce((accumulator, key) => (accumulator += progressCounts[key]), 0));
 
 /**
  * Gets the integer percent value for process status
  */
-const getStatusPercent = (progressData: ProgressBarData, status: keyof ProgressBarData) =>
-    (progressData[status] / getStatusTotal(progressData) * 100);
+const getStatusPercent = (progressCounts: ProgressBarCounts, status: keyof ProgressBarCounts) =>
+    (progressCounts[status] / getStatusTotal(progressCounts) * 100);