21225: Fix capped maxWidth preventing full width with negative margins on MPV
[arvados.git] / services / workbench2 / src / views / process-panel / process-panel-root.tsx
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 import React from "react";
6 import { StyleRulesCallback, WithStyles, withStyles } from "@material-ui/core";
7 import { ProcessIcon } from "components/icon/icon";
8 import { Process } from "store/processes/process";
9 import { SubprocessPanel } from "views/subprocess-panel/subprocess-panel";
10 import { SubprocessFilterDataProps } from "components/subprocess-filter/subprocess-filter";
11 import { MPVContainer, MPVPanelContent, MPVPanelState } from "components/multi-panel-view/multi-panel-view";
12 import { ArvadosTheme } from "common/custom-theme";
13 import { ProcessDetailsCard } from "./process-details-card";
14 import { ProcessIOCard, ProcessIOCardType, ProcessIOParameter } from "./process-io-card";
15 import { ProcessResourceCard } from "./process-resource-card";
16 import { getProcessPanelLogs, ProcessLogsPanel } from "store/process-logs-panel/process-logs-panel";
17 import { ProcessLogsCard } from "./process-log-card";
18 import { FilterOption } from "views/process-panel/process-log-form";
19 import { getInputCollectionMounts } from "store/processes/processes-actions";
20 import { WorkflowInputsData } from "models/workflow";
21 import { CommandOutputParameter } from "cwlts/mappings/v1.0/CommandOutputParameter";
22 import { AuthState } from "store/auth/auth-reducer";
23 import { ProcessCmdCard } from "./process-cmd-card";
24 import { ContainerRequestResource } from "models/container-request";
25 import { OutputDetails, NodeInstanceType } from "store/process-panel/process-panel";
26 import { NotFoundView } from 'views/not-found-panel/not-found-panel';
27
28 export interface ProcessPanelRootDataProps {
29     process?: Process;
30     subprocesses: Array<Process>;
31     filters: Array<SubprocessFilterDataProps>;
32     processLogsPanel: ProcessLogsPanel;
33     auth: AuthState;
34     inputRaw: WorkflowInputsData | null;
35     inputParams: ProcessIOParameter[] | null;
36     outputData: OutputDetails | null;
37     outputDefinitions: CommandOutputParameter[];
38     outputParams: ProcessIOParameter[] | null;
39     nodeInfo: NodeInstanceType | null;
40     usageReport: string | null;
41 }
42
43 export interface ProcessPanelRootActionProps {
44     onContextMenu: (event: React.MouseEvent<HTMLElement>, process: Process) => void;
45     onToggle: (status: string) => void;
46     cancelProcess: (uuid: string) => void;
47     startProcess: (uuid: string) => void;
48     resumeOnHoldWorkflow: (uuid: string) => void;
49     onLogFilterChange: (filter: FilterOption) => void;
50     navigateToLog: (uuid: string) => void;
51     onCopyToClipboard: (uuid: string) => void;
52     loadInputs: (containerRequest: ContainerRequestResource) => void;
53     loadOutputs: (containerRequest: ContainerRequestResource) => void;
54     loadNodeJson: (containerRequest: ContainerRequestResource) => void;
55     loadOutputDefinitions: (containerRequest: ContainerRequestResource) => void;
56     updateOutputParams: () => void;
57     pollProcessLogs: (processUuid: string) => Promise<void>;
58 }
59
60 export type ProcessPanelRootProps = ProcessPanelRootDataProps & ProcessPanelRootActionProps;
61
62 const panelsData: MPVPanelState[] = [
63     { name: "Details" },
64     { name: "Logs", visible: true },
65     { name: "Subprocesses" },
66     { name: "Outputs" },
67     { name: "Inputs" },
68     { name: "Command" },
69     { name: "Resources" },
70 ];
71
72 export const ProcessPanelRoot = ({
73     process,
74     auth,
75     processLogsPanel,
76     inputRaw,
77     inputParams,
78     outputData,
79     outputDefinitions,
80     outputParams,
81     nodeInfo,
82     usageReport,
83     loadInputs,
84     loadOutputs,
85     loadNodeJson,
86     loadOutputDefinitions,
87     updateOutputParams,
88     ...props
89 }: ProcessPanelRootProps) => {
90     const outputUuid = process?.containerRequest.outputUuid;
91     const containerRequest = process?.containerRequest;
92     const inputMounts = getInputCollectionMounts(process?.containerRequest);
93
94     React.useEffect(() => {
95         if (containerRequest) {
96             // Load inputs from mounts or props
97             loadInputs(containerRequest);
98             // Fetch raw output (loads from props or keep)
99             loadOutputs(containerRequest);
100             // Loads output definitions from mounts into store
101             loadOutputDefinitions(containerRequest);
102             // load the assigned instance type from node.json in
103             // the log collection
104             loadNodeJson(containerRequest);
105         }
106     }, [containerRequest, loadInputs, loadOutputs, loadOutputDefinitions, loadNodeJson]);
107
108     const maxHeight = "100%";
109
110     // Trigger processing output params when raw or definitions change
111     React.useEffect(() => {
112         updateOutputParams();
113     }, [outputData, outputDefinitions, updateOutputParams]);
114
115     return process ? (
116         <MPVContainer
117             spacing={8}
118             panelStates={panelsData}
119             justify-content="flex-start"
120             direction="column"
121             wrap="nowrap">
122             <MPVPanelContent
123                 forwardProps
124                 xs="auto"
125                 data-cy="process-details">
126                 <ProcessDetailsCard
127                     process={process}
128                     onContextMenu={event => props.onContextMenu(event, process)}
129                     cancelProcess={props.cancelProcess}
130                     startProcess={props.startProcess}
131                     resumeOnHoldWorkflow={props.resumeOnHoldWorkflow}
132                 />
133             </MPVPanelContent>
134             <MPVPanelContent
135                 forwardProps
136                 xs
137                 minHeight={maxHeight}
138                 maxHeight={maxHeight}
139                 data-cy="process-logs">
140                 <ProcessLogsCard
141                     onCopy={props.onCopyToClipboard}
142                     process={process}
143                     lines={getProcessPanelLogs(processLogsPanel)}
144                     selectedFilter={{
145                         label: processLogsPanel.selectedFilter,
146                         value: processLogsPanel.selectedFilter,
147                     }}
148                     filters={processLogsPanel.filters.map(filter => ({ label: filter, value: filter }))}
149                     onLogFilterChange={props.onLogFilterChange}
150                     navigateToLog={props.navigateToLog}
151                     pollProcessLogs={props.pollProcessLogs}
152                 />
153             </MPVPanelContent>
154             <MPVPanelContent
155                 forwardProps
156                 xs
157                 maxHeight={maxHeight}
158                 data-cy="process-children">
159                 <SubprocessPanel process={process} />
160             </MPVPanelContent>
161             <MPVPanelContent
162                 forwardProps
163                 xs
164                 maxHeight={maxHeight}
165                 data-cy="process-outputs">
166                 <ProcessIOCard
167                     label={ProcessIOCardType.OUTPUT}
168                     process={process}
169                     params={outputParams}
170                     raw={outputData?.raw}
171                     outputUuid={outputUuid || ""}
172                 />
173             </MPVPanelContent>
174             <MPVPanelContent
175                 forwardProps
176                 xs
177                 maxHeight={maxHeight}
178                 data-cy="process-inputs">
179                 <ProcessIOCard
180                     label={ProcessIOCardType.INPUT}
181                     process={process}
182                     params={inputParams}
183                     raw={inputRaw}
184                     mounts={inputMounts}
185                 />
186             </MPVPanelContent>
187             <MPVPanelContent
188                 forwardProps
189                 xs="auto"
190                 maxHeight={"50%"}
191                 data-cy="process-cmd">
192                 <ProcessCmdCard
193                     onCopy={props.onCopyToClipboard}
194                     process={process}
195                 />
196             </MPVPanelContent>
197             <MPVPanelContent
198                 forwardProps
199                 xs
200                 data-cy="process-resources">
201                 <ProcessResourceCard
202                     process={process}
203                     nodeInfo={nodeInfo}
204                     usageReport={usageReport}
205                 />
206             </MPVPanelContent>
207         </MPVContainer>
208     ) : (
209         <NotFoundView
210             icon={ProcessIcon}
211             messages={["Process not found"]}
212         />
213     );
214 };