/* eslint-disable react/no-multi-comp */
import React, {useState, useEffect} from 'react';
import ReactDOM from "react-dom"
import { useInterval } from "react-powerhooks";
import { makeStyles } from '@mui/material/styles';
import { useNavigate, Link, useParams } from "react-router-dom";
import {isMobile} from "react-device-detect";
import theme from '../theme.jsx';
import { validateJson, GetIconInfo } from "./Workflows.jsx";
import { green, yellow } from "./AngularWorkflow.jsx";
import {
Tooltip,
IconButton,
CircularProgress,
TextField,
Button,
ButtonGroup,
Paper,
Typography,
Divider,
} from '@mui/material';
import {
Preview as PreviewIcon,
ContentCopy as ContentCopyIcon,
} from '@mui/icons-material';
const hrefStyle = {
color: "white",
textDecoration: "none"
}
const bodyDivStyle = {
margin: "auto",
marginTop: 100,
width: isMobile? "100%":"500px",
position: "relative",
}
const RunWorkflow = (defaultprops) => {
const { globalUrl, isLoaded, isLoggedIn, setIsLoggedIn, setCookie, register, serverside } = defaultprops;
let navigate = useNavigate();
const [message, setMessage] = useState("");
const [workflow, setWorkflow] = React.useState({});
const [executionRequest, setExecutionRequest] = React.useState({});
const [executionArgument, setExecutionArgument] = useState("");
const [executionLoading, setExecutionLoading] = useState(false);
const [executionData, setExecutionData] = React.useState({});
const [executionRunning, setExecutionRunning] = useState(false);
const [workflowQuestion, setWorkflowQuestion] = useState("");
const [selectedOrganization, setSelectedOrganization] = React.useState(undefined);
const [apps, setApps] = React.useState([]);
const [buttonClicked, setButtonClicked] = React.useState("");
const boxStyle = {
color: "white",
paddingLeft: "30px",
paddingRight: "30px",
paddingBottom: "30px",
paddingTop: "30px",
backgroundColor: theme.palette.surfaceColor,
marginBottom: 150,
}
const params = useParams();
var props = JSON.parse(JSON.stringify(defaultprops))
props.match = {}
props.match.params = params
const defaultTitle = "Run Workflow"
if (document != undefined && document.title != defaultTitle) {
document.title = defaultTitle
}
const parsedsearch = serverside === true ? "" : window.location.search
if (serverside !== true) {
const tmpMessage = new URLSearchParams(window.location.search).get("message")
if (tmpMessage !== undefined && tmpMessage !== null && message !== tmpMessage) {
setMessage(tmpMessage)
}
}
// Used to swap from login to register. True = login, false = register
// Error messages etc
const [executionInfo, setExecutionInfo] = useState("");
const handleValidateForm = (executionArgument) => {
return true
}
const getApps = () => {
fetch(globalUrl + "/api/v1/apps", {
method: "GET",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
credentials: "include",
})
.then((response) => {
if (response.status !== 200) {
console.log("Status not 200 for apps :O!");
}
return response.json();
})
.then((responseJson) => {
setApps(responseJson)
})
.catch(error => {
console.log("App error: ", error);
})
}
const ShowExecutionResults = (props) => {
const { executionData } = props;
if (executionData === undefined || executionData === null || executionData === {}) {
return null
}
const executionMargin = 20
const defaultReturn =
No results yet
if (executionData.results === undefined || executionData.results === null) {
return defaultReturn
}
return (
Results
{executionData.results.map((data, index) => {
if (executionData.results.length !== 1 && (data.status === "SKIPPED")) {
return null;
}
// FIXME: The latter replace doens't really work if ' is used in a string
var showResult = data.result.trim();
const validate = validateJson(showResult);
const curapp = apps.find((a) => a.name === data.action.app_name && a.app_version === data.action.app_version);
const imgsize = 50;
const statusColor = data.status === "FINISHED" || data.status === "SUCCESS" ? green : data.status === "ABORTED" || data.status === "FAILURE" ? "red" : yellow;
var imgSrc = curapp === undefined ? "" : curapp.large_image;
if (
imgSrc.length === 0 &&
workflow.actions !== undefined &&
workflow.actions !== null
) {
// Look for the node in the workflow
const action = workflow.actions.find(
(action) => action.id === data.action.id
);
if (action !== undefined && action !== null) {
imgSrc = action.large_image;
}
}
var actionimg =
curapp === null ? null : (

);
if (data.action.app_name === "shuffle-subflow") {
//const parsedImage = triggers[2].large_image;
//actionimg = (
//

//);
} else if (data.action.app_name === "User Input") {
//actionimg = (
//

//);
}
if (validate.valid && typeof validate.result === "string") {
validate.result = JSON.parse(validate.result);
}
if (validate.valid && typeof validate.result === "object") {
if (
validate.result.result !== undefined &&
validate.result.result !== null
) {
try {
validate.result.result = JSON.parse(validate.result.result);
} catch (e) {
//console.log("ERROR PARSING: ", e)
}
}
}
var similarActionsView = null
if (data.similar_actions !== undefined && data.similar_actions !== null) {
var minimumMatch = 85
var matching_executions = []
if (data.similar_actions !== undefined && data.similar_actions !== null) {
for (let [k,kval] in Object.entries(data.similar_actions)){
if (data.similar_actions.hasOwnProperty(k)) {
if (data.similar_actions[k].similarity > minimumMatch) {
matching_executions.push(data.similar_actions[k].execution_id)
}
}
}
}
if (matching_executions.length !== 0) {
var parsed_url = matching_executions.join(",")
similarActionsView =
{
navigate(`?execution_highlight=${parsed_url}`)
}}
>
}
}
return (
{
}}
onMouseOut={() => {
}}
>
Status
{data.status}
)
})}
)
}
const onSubmit = (execution_id, authorization, answer) => {
stop()
setMessage("")
setExecutionLoading(true)
setExecutionData({})
setExecutionInfo("")
var data = {
"execution_argument": executionArgument
}
var url = `${globalUrl}/api/v1/workflows/${props.match.params.key}/execute`
var fetchBody = {
mode: 'cors',
credentials: 'include',
crossDomain: true,
withCredentials: true,
headers: {
'Content-Type': 'application/json; charset=utf-8',
},
}
if (answer !== undefined && execution_id !== undefined && authorization !== undefined) {
url += `?reference_execution=${execution_id}&authorization=${authorization}&answer=${answer}`
data = {}
fetchBody.method = "GET"
} else {
fetchBody.method = "POST"
fetchBody.body = JSON.stringify(data)
}
fetch(url, fetchBody)
.then((response) => {
if (response.status !== 200 && response.status !== 201) {
if (answer !== undefined && execution_id !== undefined && authorization !== undefined) {
setExecutionLoading(false)
setExecutionRunning(true);
setExecutionRequest({
"execution_id": execution_id,
"authorization": authorization,
})
start();
return
}
}
return response.json();
})
.then(responseJson => {
setExecutionLoading(false)
if (responseJson["success"] === false) {
console.log("Failed sending execution request")
} else {
console.log("Started execution")
if (answer !== undefined && answer !== null) {
console.log("Skipping start")
} else {
setExecutionRunning(true);
setExecutionRequest(responseJson)
start();
}
}
})
.catch(error => {
//setExecutionInfo("Error in workflow startup: " + error)
setExecutionLoading(false)
})
}
const getWorkflow = (workflow_id) => {
fetch(globalUrl + "/api/v1/workflows/" + workflow_id, {
method: "GET",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
credentials: "include",
})
.then((response) => {
if (response.status !== 200) {
console.log("Status not 200 for workflows :O!");
}
return response.json();
})
.then((responseJson) => {
// Not sure why this is necessary.
if (responseJson.isValid === undefined) {
responseJson.isValid = true;
}
if (responseJson.errors === undefined) {
responseJson.errors = [];
}
if (responseJson.actions === undefined || responseJson.actions === null) {
responseJson.actions = [];
}
if (responseJson.triggers === undefined || responseJson.triggers === null) {
responseJson.triggers = [];
}
handleGetOrg(responseJson.org_id)
setWorkflow(responseJson);
})
.catch((error) => {
console.log("Get workflow error: ", error.toString());
});
};
const { start, stop } = useInterval({
duration: 3000,
startImmediate: true,
callback: () => {
fetchUpdates(executionRequest.execution_id, executionRequest.authorization)
},
});
const handleUpdateResults = (responseJson, executionRequest) => {
if (responseJson === undefined || responseJson === null || responseJson.success === false) {
return
}
console.log("Got response: ", responseJson)
ReactDOM.unstable_batchedUpdates(() => {
if (JSON.stringify(responseJson) !== JSON.stringify(executionData)) {
// FIXME: If another is selected, don't edit..
// Doesn't work because this is some async garbage
if (executionData.execution_id === undefined || (responseJson.execution_id === executionData.execution_id && responseJson.results !== undefined && responseJson.results !== null)) {
if (executionData.status !== responseJson.status || executionData.result !== responseJson.result || (executionData.results !== undefined && responseJson.results !== null && executionData.results.length !== responseJson.results.length)) {
console.log("Updating data!")
setExecutionData(responseJson)
for (var key in responseJson.results) {
if (responseJson.results[key].status === "WAITING") {
console.log("Found: ", responseJson.results[key])
const validate = validateJson(responseJson.results[key].result)
console.log("Validate: ", validate)
if (validate.valid && typeof validate.result === "string") {
validate.result = JSON.parse(validate.result)
}
console.log("Newresult: ", validate.result)
if (validate.result["information"] !== undefined && validate.result["information"] !== null) {
setWorkflowQuestion(validate.result["information"])
}
break
}
}
} else {
console.log("NOT updating executiondata state.");
}
}
}
if (responseJson.status === "ABORTED" || responseJson.status === "STOPPED" || responseJson.status === "FAILURE" || responseJson.status === "WAITING") {
stop();
if (executionRunning) {
setExecutionRunning(false);
}
//getWorkflowExecution(props.match.params.key, "");
} else if (responseJson.status === "FINISHED") {
setExecutionRunning(false)
stop();
//getWorkflowExecution(props.match.params.key, "");
}
})
}
const handleGetOrg = (orgId, execution_id, authorization) => {
if (orgId.length === 0) {
return;
}
// Just use this one?
var url = execution_id !== undefined && authorization !== undefined ? `${globalUrl}/api/v1/orgs/${orgId}?reference_execution=${execution_id}&authorization=${authorization}` : `${globalUrl}/api/v1/orgs/${orgId}`;
fetch(url, {
method: "GET",
credentials: "include",
headers: {
"Content-Type": "application/json",
},
})
.then((response) => {
if (response.status === 401) {
}
return response.json();
})
.then((responseJson) => {
if (responseJson["success"] === false) {
} else {
if (responseJson.sync_features === undefined || responseJson.sync_features === null) {
}
if (document != undefined && document.title != defaultTitle) {
document.title = responseJson.name + " - " + defaultTitle
}
setSelectedOrganization(responseJson)
}
})
.catch((error) => {
console.log("Error getting org: ", error);
});
};
const fetchUpdates = (execution_id, authorization, getorg) => {
const innerRequest = {
"execution_id": execution_id,
"authorization": authorization
}
if (executionRequest.execution_id !== innerRequest.execution_id) {
setExecutionRequest(innerRequest)
}
if (execution_id === "" || authorization === "") {
setExecutionLoading(false)
setExecutionRunning(false)
stop()
return
}
fetch(globalUrl + "/api/v1/streams/results", {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
body: JSON.stringify(innerRequest),
credentials: "include",
})
.then((response) => {
if (response.status !== 200) {
console.log("Status not 200 for stream results :O!");
}
return response.json();
})
.then((responseJson) => {
if (getorg === true) {
handleGetOrg(responseJson.org_id, execution_id, authorization)
}
handleUpdateResults(responseJson, executionRequest);
})
.catch((error) => {
console.log("Execution result Error: ", error);
});
};
const answer = new URLSearchParams(window.location.search).get("answer")
const execution_id = new URLSearchParams(window.location.search).get("reference_execution")
const authorization = new URLSearchParams(window.location.search).get("authorization")
useEffect(() => {
getWorkflow(props.match.params.key)
if (execution_id !== undefined && execution_id !== null && authorization !== undefined && authorization !== null) {
console.log("Get execution: ", execution_id)
fetchUpdates(execution_id, authorization, true)
}
if (answer !== undefined && answer !== null) {
console.log("Got answer: ", answer)
}
}, [])
const buttonBackground = "linear-gradient(to right, #f86a3e, #f34079)"
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 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.contact !== undefined && selectedOrganization.contact !== null? selectedOrganization.contact : "support@shuffler.io"
const image = selectedOrganization !== undefined && selectedOrganization !== null && selectedOrganization.image !== undefined && selectedOrganization.image !== null && selectedOrganization.image !== "" ? selectedOrganization.image : theme.palette.defaultImage
console.log("IMG: ", image, "ORG: ", selectedOrganization)
if (!disabledButtons && answer !== undefined && answer !== null && organization !== "Unknown" && buttonClicked.length === 0) {
console.log("Finding button!")
// Find the button
var buttonid = ""
if (answer === "false") {
buttonid = "abort_execution"
} else if (answer === "true") {
buttonid = "continue_execution"
}
if (buttonid !== "") {
const foundButton = document.getElementById(buttonid)
console.log("Button: ", foundButton)
if (foundButton !== undefined && foundButton !== null) {
foundButton.click()
}
}
}
const basedata =
const loadedCheck = isLoaded ?
{basedata}
:
return (
{loadedCheck}
)
}
export default RunWorkflow;