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