/* 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 '@material-ui/styles'; import { useNavigate, Link, useParams } from "react-router-dom"; import {isMobile} from "react-device-detect"; import theme from '../theme'; import { validateJson, GetIconInfo } from "./Workflows.jsx"; import { green, yellow } from "./AngularWorkflow.jsx"; import { Tooltip, IconButton, CircularProgress, TextField, Button, ButtonGroup, Paper, Typography, Divider, } from '@material-ui/core'; 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 boxStyle = { color: "white", paddingLeft: "30px", paddingRight: "30px", paddingBottom: "30px", paddingTop: "30px", backgroundColor: theme.palette.surfaceColor, marginBottom: 150, } 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 params = useParams(); var props = JSON.parse(JSON.stringify(defaultprops)) props.match = {} props.match.params = params 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 : ( {data.action.app_name} ); if (data.action.app_name === "shuffle-subflow") { //const parsedImage = triggers[2].large_image; //actionimg = ( // {"Shuffle //); } else if (data.action.app_name === "User Input") { //actionimg = ( // {"Shuffle //); } 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) { } 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 =
{onSubmit()}} style={{margin: "15px 15px 15px 15px"}}> {workflow.name} {organization} {contact} {message} {answer !== undefined && answer !== null ? null : Workflow: {workflow.name} } {executionData !== undefined && executionData !== null && executionData !== {} && executionData.status !== undefined && (answer === undefined || answer === null) ?
Status  {executionData.status}
: null} {workflowQuestion.length > 0 ? {workflowQuestion} : null} {answer !== undefined && answer !== null ? null : Execution Argument
{ setExecutionArgument(e.target.value) }} />
} {executionRunning ? Status: {executionData.status} : answer !== undefined && answer !== null ? {disabledButtons ? "Already answered. Nothing to do." : ""} {disabledButtons ? null : What do you want to do? }
 or 
:
} {buttonClicked !== undefined && buttonClicked !== null && buttonClicked !== "finished" && buttonClicked.length > 0 ? finalize workflow animation { console.log("Img loaded.") setTimeout(() => { console.log("Img closing.") setButtonClicked("finished") }, 1250) }} /> : null}
{executionInfo}
{answer !== undefined && answer !== null ? null : }
const loadedCheck = isLoaded ?
{basedata}
:
return (
{loadedCheck}
) } export default RunWorkflow;