Fixed user input to work better

This commit is contained in:
Frikky
2024-06-22 23:44:01 +02:00
parent 1aaaa4b43b
commit 52227397a9
4 changed files with 211 additions and 79 deletions
+1 -1
View File
@@ -1076,7 +1076,7 @@ func handleExecution(id string, workflow shuffle.Workflow, request *http.Request
// return workflowExecution, fmt.Sprintf("%s", err), nil // return workflowExecution, fmt.Sprintf("%s", err), nil
} else { } else {
log.Printf("[ERROR] Failed in prepareExecution: '%s'", err) log.Printf("[ERROR] Failed in prepareExecution: '%s'", err)
return shuffle.WorkflowExecution{}, fmt.Sprintf("Failed starting workflow: %s", err), err return shuffle.WorkflowExecution{}, fmt.Sprintf("Failed running: %s", err), err
} }
} }
+2 -2
View File
@@ -685,8 +685,8 @@ const EditWorkflow = (props) => {
<Typography variant="h6" style={{marginTop: 50, }}> <Typography variant="h6" style={{marginTop: 50, }}>
Input fields Input fields
</Typography> </Typography>
<Typography variant="body1" color="textSecondary" style={{marginBottom: 20, }}> <Typography variant="body2" color="textSecondary" style={{marginBottom: 20, }}>
Input fields are fields that will be used during the startup of the workflow. These will be formatted in JSON and is most commonly used from the <a href={`/workflows/${workflow.id}/run`} rel="noopener noreferrer" target="_blank" style={{ textDecoration: "none", color: "#f86a3e" }}>workflow run page</a>. Input fields are fields that will be used during the startup of the workflow. These will be formatted in JSON and is most commonly used from the <a href={`/workflows/${workflow.id}/run`} rel="noopener noreferrer" target="_blank" style={{ textDecoration: "none", color: "#f86a3e" }}>workflow run page</a>. If chosen in the User Input node, these will be required fields.
</Typography> </Typography>
+17 -22
View File
@@ -1073,22 +1073,20 @@ const releaseToConnectLabel = "Release to Connect"
// User Input & Subflow nodes // User Input & Subflow nodes
if (param.name === "workflow" || param.name === "subflow") { if (param.name === "workflow" || param.name === "subflow") {
const paramIndex = param.name === "workflow" ? 0 : 5 const paramIndex = param.name === "workflow" ? 0 : 5
console.log("Current vs new: ", workflow.triggers[trigger_index].parameters[paramIndex].value, subworkflow.id) if (workflow.triggers[trigger_index].parameters[paramIndex].value !== subworkflow.id) {
if (param.value === workflow.id) {
if (workflow.triggers[trigger_index].parameters[paramIndex].value !== subworkflow.id) { setSubworkflow(workflow);
if (param.value === workflow.id) { baseSubflow = workflow
setSubworkflow(workflow); } else {
baseSubflow = workflow const sub = responseJson.find((data) => data.id === param.value);
} else { if (sub !== undefined && subworkflow.id !== sub.id) {
const sub = responseJson.find((data) => data.id === param.value); baseSubflow = sub
if (sub !== undefined && subworkflow.id !== sub.id) { setSubworkflow(sub);
baseSubflow = sub }
setSubworkflow(sub); }
} }
} }
}
}
if (param.name === "startnode" && param.value !== undefined && param.value !== null) { if (param.name === "startnode" && param.value !== undefined && param.value !== null) {
@@ -3255,7 +3253,6 @@ const releaseToConnectLabel = "Release to Connect"
const getChildWorkflows = (parentWorkflowId) => { const getChildWorkflows = (parentWorkflowId) => {
if (originalWorkflow.suborg_distribution === undefined || originalWorkflow.suborg_distribution === null || originalWorkflow.suborg_distribution.length === 0) { if (originalWorkflow.suborg_distribution === undefined || originalWorkflow.suborg_distribution === null || originalWorkflow.suborg_distribution.length === 0) {
console.log("No suborg distribution")
return return
} }
@@ -14866,10 +14863,6 @@ const releaseToConnectLabel = "Release to Connect"
}) })
} }
if (workflow.triggers !== undefined && workflow.triggers !== null && workflow.triggers.length > 0 && selectedTriggerIndex >= 0 && selectedTriggerIndex < workflow.triggers.length) {
console.log(workflow.triggers[selectedTriggerIndex])
}
const UserinputSidebar = Object.getOwnPropertyNames(selectedTrigger).length === 0 || workflow.triggers[selectedTriggerIndex] === undefined || selectedTrigger.trigger_type !== "USERINPUT" ? null : const UserinputSidebar = Object.getOwnPropertyNames(selectedTrigger).length === 0 || workflow.triggers[selectedTriggerIndex] === undefined || selectedTrigger.trigger_type !== "USERINPUT" ? null :
<div style={appApiViewStyle}> <div style={appApiViewStyle}>
<h3 style={{ marginBottom: "5px" }}> <h3 style={{ marginBottom: "5px" }}>
@@ -15217,7 +15210,7 @@ const releaseToConnectLabel = "Release to Connect"
</div> </div>
<div style={{marginTop: 0, }} /> <div style={{marginTop: 0, }} />
<b>Enabled Input-Questions</b> <b>Required Input-Questions</b>
{workflow.input_questions !== undefined && workflow.input_questions !== null && workflow.input_questions.length > 0 ? {workflow.input_questions !== undefined && workflow.input_questions !== null && workflow.input_questions.length > 0 ?
<div> <div>
{workflow.input_questions.map((question, index) => { {workflow.input_questions.map((question, index) => {
@@ -17156,10 +17149,12 @@ const releaseToConnectLabel = "Release to Connect"
//defaultReturn = <UserinputSidebar /> //defaultReturn = <UserinputSidebar />
return null; return null;
} else { } else {
/*
console.log( console.log(
"Unable to handle invalid trigger type " + "Unable to handle invalid trigger type " +
selectedTrigger.trigger_type selectedTrigger.trigger_type
); );
*/
return null; return null;
} }
} else if (Object.getOwnPropertyNames(selectedEdge).length > 0) { } else if (Object.getOwnPropertyNames(selectedEdge).length > 0) {
+191 -54
View File
@@ -2,6 +2,7 @@
import React, {useState, useEffect} from 'react'; import React, {useState, useEffect} from 'react';
import ReactDOM from "react-dom" import ReactDOM from "react-dom"
import { ToastContainer, toast } from "react-toastify"
import { useInterval } from "react-powerhooks"; import { useInterval } from "react-powerhooks";
import { makeStyles } from '@mui/material/styles'; import { makeStyles } from '@mui/material/styles';
import { useNavigate, Link, useParams } from "react-router-dom"; import { useNavigate, Link, useParams } from "react-router-dom";
@@ -43,7 +44,8 @@ const bodyDivStyle = {
const RunWorkflow = (defaultprops) => { const RunWorkflow = (defaultprops) => {
const { globalUrl, isLoaded, isLoggedIn, setIsLoggedIn, setCookie, register, serverside } = defaultprops; const { globalUrl, isLoaded, isLoggedIn, setIsLoggedIn, setCookie, register, serverside } = defaultprops;
let navigate = useNavigate(); let navigate = useNavigate();
const [_, setUpdate] = useState(""); // Used to force rendring, don't remove
const [message, setMessage] = useState(""); const [message, setMessage] = useState("");
const [workflow, setWorkflow] = React.useState({}); const [workflow, setWorkflow] = React.useState({});
const [executionRequest, setExecutionRequest] = React.useState({}); const [executionRequest, setExecutionRequest] = React.useState({});
@@ -55,6 +57,7 @@ const RunWorkflow = (defaultprops) => {
const [selectedOrganization, setSelectedOrganization] = React.useState(undefined); const [selectedOrganization, setSelectedOrganization] = React.useState(undefined);
const [apps, setApps] = React.useState([]); const [apps, setApps] = React.useState([]);
const [buttonClicked, setButtonClicked] = React.useState(""); const [buttonClicked, setButtonClicked] = React.useState("");
const [foundSourcenode, setFoundSourcenode] = React.useState(undefined);
const boxStyle = { const boxStyle = {
color: "white", color: "white",
@@ -340,6 +343,8 @@ const RunWorkflow = (defaultprops) => {
event.preventDefault() event.preventDefault()
} }
console.log("ONSUBMIT: ", event, execution_id, authorization, answer)
stop() stop()
setMessage("") setMessage("")
setExecutionLoading(true) setExecutionLoading(true)
@@ -389,12 +394,27 @@ const RunWorkflow = (defaultprops) => {
url += `?reference_execution=${execution_id}&authorization=${authorization}&answer=${answer}` url += `?reference_execution=${execution_id}&authorization=${authorization}&answer=${answer}`
data = {} data = {}
fetchBody.method = "GET" fetchBody.method = "GET"
if (executionArgument !== undefined && executionArgument !== null) {
try {
if (typeof executionArgument === "string") {
url += "&note=" + executionArgument
} else {
url += "&note=" + JSON.stringify(executionArgument)
}
} catch (e) {
url += "&note=" + executionArgument
}
}
} else { } else {
fetchBody.method = "POST" fetchBody.method = "POST"
fetchBody.body = JSON.stringify(data) fetchBody.body = JSON.stringify(data)
} }
console.log("Pre request: ", url, fetchBody) // IF there is an execution argument, we should use it
console.log("FULL URL: ", url)
fetch(url, fetchBody) fetch(url, fetchBody)
.then((response) => { .then((response) => {
if (response.status !== 200 && response.status !== 201) { if (response.status !== 200 && response.status !== 201) {
@@ -408,16 +428,26 @@ const RunWorkflow = (defaultprops) => {
}) })
start(); start();
return return response.json()
} }
} }
return response.json(); return response.json()
}) })
.then(responseJson => { .then(responseJson => {
setExecutionLoading(false) setExecutionLoading(false)
if (responseJson["success"] === false) { if (responseJson.success === false) {
console.log("Failed sending execution request") console.log("Failed sending execution request")
if (responseJson.reason !== undefined && responseJson.reason !== null) {
toast.warn(responseJson.reason)
}
stop()
setMessage("")
setExecutionData({})
setExecutionInfo("")
setExecutionRunning(false)
setExecutionRequest({})
} else { } else {
console.log("Started execution") console.log("Started execution")
@@ -426,17 +456,24 @@ const RunWorkflow = (defaultprops) => {
} else { } else {
setExecutionRunning(true); setExecutionRunning(true);
setExecutionRequest(responseJson) setExecutionRequest(responseJson)
start(); start()
} }
} }
}) })
.catch(error => { .catch(error => {
//setExecutionInfo("Error in workflow startup: " + error) //setExecutionInfo("Error in workflow startup: " + error)
toast.warn("Error in workflow startup: " + error)
stop()
setMessage("")
setExecutionData({})
setExecutionInfo("")
setExecutionLoading(false) setExecutionLoading(false)
}) })
} }
const getWorkflow = (workflow_id) => { const getWorkflow = (workflow_id, selectedNode) => {
fetch(globalUrl + "/api/v1/workflows/" + workflow_id, { fetch(globalUrl + "/api/v1/workflows/" + workflow_id, {
method: "GET", method: "GET",
headers: { headers: {
@@ -480,6 +517,48 @@ const RunWorkflow = (defaultprops) => {
setExecutionArgument(newexec) setExecutionArgument(newexec)
} }
if (selectedNode !== undefined && selectedNode !== null && selectedNode.length > 0) {
var found = false
for (var actionkey in responseJson.actions) {
if (responseJson.actions[actionkey].id === selectedNode) {
found = true
setFoundSourcenode(responseJson.actions[actionkey])
break
}
}
if (!found) {
for (var triggerkey in responseJson.triggers) {
if (responseJson.triggers[triggerkey].id !== selectedNode) {
continue
}
setFoundSourcenode(responseJson.triggers[triggerkey])
if (responseJson.input_questions !== undefined && responseJson.input_questions !== null && responseJson.input_questions.length > 0 && responseJson.triggers[triggerkey].trigger_type === "USERINPUT") {
// Look for input questions param
for (var paramkey in responseJson.triggers[triggerkey].parameters) {
if (responseJson.triggers[triggerkey].parameters[paramkey].name === "input_questions") {
var relevantquestions = []
for (var questionkey in responseJson.input_questions) {
if (responseJson.triggers[triggerkey].parameters[paramkey].value.includes(responseJson.input_questions[questionkey].name)) {
relevantquestions.push(responseJson.input_questions[questionkey])
}
}
responseJson.input_questions = relevantquestions
}
}
}
break
}
}
}
handleGetOrg(responseJson.org_id) handleGetOrg(responseJson.org_id)
setWorkflow(responseJson); setWorkflow(responseJson);
}) })
@@ -489,7 +568,7 @@ const RunWorkflow = (defaultprops) => {
}; };
const { start, stop } = useInterval({ const { start, stop } = useInterval({
duration: 3000, duration: 1500,
startImmediate: true, startImmediate: true,
callback: () => { callback: () => {
fetchUpdates(executionRequest.execution_id, executionRequest.authorization) fetchUpdates(executionRequest.execution_id, executionRequest.authorization)
@@ -526,15 +605,11 @@ const RunWorkflow = (defaultprops) => {
for (var key in responseJson.results) { for (var key in responseJson.results) {
if (responseJson.results[key].status === "WAITING") { if (responseJson.results[key].status === "WAITING") {
console.log("Found: ", responseJson.results[key])
const validate = validateJson(responseJson.results[key].result) const validate = validateJson(responseJson.results[key].result)
console.log("Validate: ", validate)
if (validate.valid && typeof validate.result === "string") { if (validate.valid && typeof validate.result === "string") {
validate.result = JSON.parse(validate.result) validate.result = JSON.parse(validate.result)
} }
console.log("Newresult: ", validate.result)
if (validate.result["information"] !== undefined && validate.result["information"] !== null) { if (validate.result["information"] !== undefined && validate.result["information"] !== null) {
setWorkflowQuestion(validate.result["information"]) setWorkflowQuestion(validate.result["information"])
} }
@@ -623,15 +698,15 @@ const RunWorkflow = (defaultprops) => {
return return
} }
fetch(globalUrl + "/api/v1/streams/results", { fetch(globalUrl + "/api/v1/streams/results", {
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
Accept: "application/json", Accept: "application/json",
}, },
body: JSON.stringify(innerRequest), body: JSON.stringify(innerRequest),
credentials: "include", credentials: "include",
}) })
.then((response) => { .then((response) => {
if (response.status !== 200) { if (response.status !== 200) {
console.log("Status not 200 for stream results :O!"); console.log("Status not 200 for stream results :O!");
@@ -651,11 +726,14 @@ const RunWorkflow = (defaultprops) => {
}); });
}; };
const answer = new URLSearchParams(window.location.search).get("answer") const searchParams = new URLSearchParams(window.location.search)
const execution_id = new URLSearchParams(window.location.search).get("reference_execution") const answer = searchParams.get("answer")
const authorization = new URLSearchParams(window.location.search).get("authorization") const execution_id = searchParams.get("reference_execution")
const authorization = searchParams.get("authorization")
const sourceNode = searchParams.get("source_node")
useEffect(() => { useEffect(() => {
getWorkflow(props.match.params.key) getWorkflow(props.match.params.key, sourceNode)
if (execution_id !== undefined && execution_id !== null && authorization !== undefined && authorization !== null) { if (execution_id !== undefined && execution_id !== null && authorization !== undefined && authorization !== null) {
console.log("Get execution: ", execution_id) console.log("Get execution: ", execution_id)
fetchUpdates(execution_id, authorization, true) fetchUpdates(execution_id, authorization, true)
@@ -667,13 +745,60 @@ const RunWorkflow = (defaultprops) => {
}, []) }, [])
useEffect(() => {
if (executionData === undefined || executionData === null || executionData === {}) {
return
}
if (foundSourcenode === undefined || foundSourcenode === null || foundSourcenode === {}) {
return
}
if (foundSourcenode.trigger_type !== "USERINPUT") {
return
}
if (executionData.results === undefined || executionData.results === null || executionData.results.length === 0) {
return
}
for (var resultkey in executionData.results) {
const result = executionData.results[resultkey]
if (result.action.id !== foundSourcenode.id) {
continue
}
var parsedresult = result.result
try {
parsedresult = JSON.parse(parsedresult)
} catch (e) {
console.log("Error parsing result: ", e)
}
if (result.status !== "WAITING") {
if (parsedresult.click_info !== undefined && parsedresult.click_info !== null) {
if (parsedresult.click_info.user !== undefined && parsedresult.click_info.user !== null && parsedresult.click_info.user.length > 0) {
setMessage("Already answered by " + parsedresult.click_info.user)
}
} else {
setMessage("Answered.")
}
}
break
}
}, [executionData, foundSourcenode])
const buttonBackground = "linear-gradient(to right, #f86a3e, #f34079)" const buttonBackground = "linear-gradient(to right, #f86a3e, #f34079)"
const buttonStyle = {borderRadius: 25, height: 50, fontSize: 18, backgroundImage: handleValidateForm(executionArgument) || executionLoading ? buttonBackground : "grey", color: "white"} const buttonStyle = {borderRadius: 25, height: 50, fontSize: 18, backgroundImage: handleValidateForm(executionArgument) || executionLoading ? buttonBackground : "grey", color: "white"}
//console.log("execdata: ", executionData)
const disabledButtons = message.length > 0 || executionData.status === "FINISHED" || executionData.status === "ABORTED" const disabledButtons = message.length > 0 || executionData.status === "FINISHED" || executionData.status === "ABORTED"
//{disabledButtons ? null :
const organization = selectedOrganization !== undefined && selectedOrganization !== null ? selectedOrganization.name : "Unknown" const organization = selectedOrganization !== undefined && selectedOrganization !== null ? selectedOrganization.name : "Unknown"
const contact = selectedOrganization !== undefined && selectedOrganization !== null && selectedOrganization.org !== undefined && selectedOrganization.org !== null? selectedOrganization.org : "support@shuffler.io" const contact = selectedOrganization !== undefined && selectedOrganization !== null && selectedOrganization.org !== undefined && selectedOrganization.org !== null? selectedOrganization.org : "support@shuffler.io"
//const contact = selectedOrganization !== undefined && selectedOrganization !== null && selectedOrganization.contact !== undefined && selectedOrganization.contact !== null? selectedOrganization.contact : "support@shuffler.io" //const contact = selectedOrganization !== undefined && selectedOrganization !== null && selectedOrganization.contact !== undefined && selectedOrganization.contact !== null? selectedOrganization.contact : "support@shuffler.io"
@@ -724,49 +849,54 @@ const RunWorkflow = (defaultprops) => {
<Typography variant="h6" style={{marginBottom: 10, marginTop: 50, textAlign: "center", }}> <Typography variant="h6" style={{marginBottom: 10, marginTop: 50, textAlign: "center", }}>
{organization} {organization}
</Typography> </Typography>
<Typography variant="body1" color="textSecondary" style={{marginBottom: 15, marginTop: 0, textAlign: "center",}}>
{contact}
</Typography>
<Divider style={{marginTop: 20, marginBottom: 20, }}/> <Divider style={{marginTop: 20, marginBottom: 20, }}/>
<Typography color="textSecondary">{message}</Typography>
{disabledButtons && message.length > 0 ? null :
<Typography color="textSecondary" style={{textAlign: "center", }}>
{message}
</Typography>
}
{answer !== undefined && answer !== null ? null : {answer !== undefined && answer !== null ? null :
<Typography variant="h6" style={{marginBottom: 15, }}><b>{workflow.name}</b></Typography> <Typography variant="h6" style={{marginBottom: 15, textAlign: "center", }}><b>{workflow.name}</b></Typography>
} }
{workflowQuestion.length > 0 ? {workflowQuestion.length > 0 ?
<Typography variant="body1" style={{ marginBottom: 35, marginTop: 30, marginRight: 15, textAlign: "center", whiteSpace: "pre-line", }}> <div style={{
{workflowQuestion} backgroundColor: theme.palette.inputColor,
</Typography> padding: 20,
borderRadius: theme.palette.borderRadius,
marginBottom: 35,
marginTop: 30,
}}>
<Typography variant="body1" style={{ marginRight: 15, textAlign: "center", whiteSpace: "pre-line", }}>
{workflowQuestion}
</Typography>
</div>
: null} : null}
{workflow.input_questions !== undefined && workflow.input_questions !== null && workflow.input_questions.length > 0 ? {workflow.input_questions !== undefined && workflow.input_questions !== null && workflow.input_questions.length > 0 ?
<div style={{marginBottom: 5, }}> <div style={{marginBottom: 5, }}>
{workflow.input_questions.map((question, index) => { {workflow.input_questions.map((question, index) => {
return ( return (
<div style={{marginBottom: 5}}> <div style={{marginBottom: 5}}>
{question.name} {question.name}
<TextField <TextField
color="primary" color="primary"
style={{backgroundColor: theme.palette.inputColor, marginTop: 5, }} style={{backgroundColor: theme.palette.inputColor, marginTop: 5, }}
multiLine label={question.value}
maxRows={2} required
InputProps={{
style:{ disabled={disabledButtons}
height: "50px",
color: "white",
fontSize: "1em",
},
}}
fullWidth={true} fullWidth={true}
placeholder="" placeholder=""
id="emailfield" id="emailfield"
margin="normal" margin="normal"
variant="outlined" variant="outlined"
onChange={(e) => { onBlur={(e) => {
//setExecutionArgument(e.target.value) //setExecutionArgument(e.target.value)
executionArgument[question.value] = e.target.value executionArgument[question.value] = e.target.value
setUpdate(Math.random())
}} }}
/> />
</div> </div>
@@ -813,20 +943,27 @@ const RunWorkflow = (defaultprops) => {
</Typography> </Typography>
: null} : null}
</span> </span>
: :
answer !== undefined && answer !== null ? ((answer !== undefined && answer !== null) || (foundSourcenode !== undefined && foundSourcenode !== null)) ?
<span style={{marginTop: 20, }}> <span style={{marginTop: 20, }}>
<Typography variant="body1" style={{textAlign: "center", marginTop: 30, marginBottom: 20, }}>
{disabledButtons ? "Already answered. Nothing to do." : ""} {disabledButtons && message.length > 0 ?
</Typography> <Typography variant="body1" style={{textAlign: "center", marginTop: 30, marginBottom: 20, }}>
{message}. You may close this window.
</Typography>
:
<Typography variant="body1" style={{textAlign: "center", marginTop: 30, marginBottom: 20, }}>
{disabledButtons ? "Answered. You may close this window." : ""}
</Typography>
}
{disabledButtons ? null : {disabledButtons ? null :
<Typography variant="body2" color="textSecondary" style={{textAlign: "center", marginTop: 10, }}> <Typography variant="body2" color="textSecondary" style={{textAlign: "center", marginTop: 10, }}>
What do you want to do? What do you want to do?
</Typography> </Typography>
} }
<div fullWidth style={{width: "100%", marginTop: 10, marginBottom: 10, display: "flex", }}> <div fullWidth style={{width: "100%", marginTop: 10, marginBottom: 10, display: "flex", }}>
<Button fullWidth id="continue_execution" variant="contained" disabled={disabledButtons} color="primary" style={{border: answer === "true" ? "2px solid rgba(255,255,255,0.6)" : null, flex: 1,}} onClick={() => { <Button fullWidth id="continue_execution" variant="contained" disabled={disabledButtons} color="primary" style={{flex: 1,}} onClick={() => {
onSubmit(null, execution_id, authorization, true) onSubmit(null, execution_id, authorization, true)
setButtonClicked("FINISHED") setButtonClicked("FINISHED")
@@ -837,7 +974,7 @@ const RunWorkflow = (defaultprops) => {
<Typography variant="body1" style={{marginLeft: 3, marginRight: 3, marginTop: 3, }}> <Typography variant="body1" style={{marginLeft: 3, marginRight: 3, marginTop: 3, }}>
&nbsp;or&nbsp; &nbsp;or&nbsp;
</Typography> </Typography>
<Button fullWidth id="abort_execution" variant="contained" color="primary" disabled={disabledButtons} style={{border: answer !== "true" ? "2px solid rgba(255,255,255,0.6)" : null, flex: 1, }} onClick={() => { <Button fullWidth id="abort_execution" variant="contained" color="primary" disabled={disabledButtons} style={{ flex: 1, }} onClick={() => {
onSubmit(null, execution_id, authorization, false) onSubmit(null, execution_id, authorization, false)
setButtonClicked("ABORTED") setButtonClicked("ABORTED")
@@ -856,7 +993,7 @@ const RunWorkflow = (defaultprops) => {
</div> </div>
} }
{buttonClicked !== undefined && buttonClicked !== null && buttonClicked !== "finished" && buttonClicked.length > 0 ? {/*buttonClicked !== undefined && buttonClicked !== null && buttonClicked !== "finished" && buttonClicked.length > 0 ?
<img id="finalize_gif" src="/images/finalize.gif" alt="finalize workflow animation" style={{width: 150, marginLeft: 125, borderRadius: theme.palette.borderRadius, }} <img id="finalize_gif" src="/images/finalize.gif" alt="finalize workflow animation" style={{width: 150, marginLeft: 125, borderRadius: theme.palette.borderRadius, }}
onLoad={() => { onLoad={() => {
console.log("Img loaded.") console.log("Img loaded.")
@@ -868,7 +1005,7 @@ const RunWorkflow = (defaultprops) => {
}} }}
/> />
: null} : null*/}
<div style={{marginTop: "10px"}}> <div style={{marginTop: "10px"}}>
{executionInfo} {executionInfo}