import React, { useState, useEffect } from "react"; import { toast } from "react-toastify" import theme from '../theme.jsx'; import { useNavigate, Link, useParams } from "react-router-dom"; import AppSearchButtons from "../components/AppSearchButtons.jsx"; import { isMobile } from "react-device-detect"; import RenderCytoscape from "../components/RenderCytoscape.jsx"; import { Button, Typography, Dialog, DialogTitle, DialogContent, DialogActions, Drawer, CircularProgress, Fade, IconButton, Tooltip, } from "@mui/material"; import { Check as CheckIcon, TrendingFlat as TrendingFlatIcon, Close as CloseIcon, East as EastIcon, Interests as InterestsIcon, OpenInNew as OpenInNewIcon, } from '@mui/icons-material'; import { green, yellow, red, grey, } from "../views/AngularWorkflow.jsx" //import WorkflowTemplatePopup2 from "./WorkflowTemplatePopup2.jsx"; import WorkflowTemplatePopup2 from "./WorkflowTemplatePopup2.jsx"; import ConfigureWorkflow from "../components/ConfigureWorkflow.jsx"; import WorkflowValidationTimeline from "../components/WorkflowValidationTimeline.jsx"; import FixWorkflowValidationErrors from "../components/FixWorkflowValidationErrors.jsx"; const WorkflowTemplatePopup = (props) => { const { userdata, appFramework, globalUrl, img1, srcapp, img2, dstapp, title, description, visualOnly, apps, isLoggedIn, isHomePage, getAppFramework, showTryit, shownColor, workflowBuilt, usecaseDetails, isModalOpenDefault, setIsClicked, inputWorkflowId, inputWorkflow, } = props; const [isActive, setIsActive] = useState(workflowBuilt === true || (workflowBuilt !== undefined && workflowBuilt !== null && workflowBuilt?.length > 0) || (inputWorkflow !== undefined && inputWorkflow !== null && inputWorkflow.id !== undefined && inputWorkflow.id !== null && inputWorkflow.id !== "") ? true : false) const [isHovered, setIsHovered] = useState(false); const [modalOpen, setModalOpen] = useState(isModalOpenDefault === true ? true : false) const [errorMessage, setErrorMessage] = useState(""); const [workflowLoading, setWorkflowLoading] = useState(false) const [showLoginButton, setShowLoginButton] = useState(false); const [appAuthentication, setAppAuthentication] = React.useState(undefined); const [missingSource, setMissingSource] = React.useState(undefined) const [missingDestination, setMissingDestination] = React.useState(undefined); const [configurationFinished, setConfigurationFinished] = React.useState(false) const [appSetupDone, setAppSetupDone] = React.useState(false) const [requestSent, setRequestSent] = React.useState(false) const [showTryitOut, setShowTryitout] = React.useState(showTryit === true ? true : false) const [loadingWorkflow, setLoadingWorkflow] = React.useState(false) const [workflow, setWorkflow] = useState(inputWorkflow !== undefined && inputWorkflow !== null && inputWorkflow.id !== undefined && inputWorkflow.id !== null && inputWorkflow.id !== "" ? inputWorkflow : {}) const [_, setUpdate] = useState(0) const fetchWorkflow = (id) => { if (id === undefined || id === null || id === "") { return } if (loadingWorkflow === true) { return } setLoadingWorkflow(true) const url = `${globalUrl}/api/v1/workflows/${id}` fetch(url, { method: "GET", headers: { "Content-Type": "application/json", Accept: "application/json", }, credentials: "include", }) .then((response) => { setLoadingWorkflow(false) if (response.status !== 200) { console.log("Status not 200 for framework!"); } return response.json(); }) .then((responseJson) => { if (responseJson.success === false) { console.log("Error in workflow loading for ID ", id) } else { setWorkflow(responseJson) } }) .catch((error) => { console.log("err in framework: ", error.toString()); setLoadingWorkflow(false) }) } if (inputWorkflowId !== undefined && inputWorkflowId !== null && inputWorkflowId !== "" && workflow.id !== inputWorkflowId) { fetchWorkflow(inputWorkflowId) } const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; let navigate = useNavigate(); useEffect(() => { if (modalOpen !== true) { if (workflowLoading === true) { setWorkflowLoading(false) } //console.log("Modal is not open, so we are not doing anything.") return } if (workflowLoading !== true) { //console.log("Workflow loading is false, so we can try to get the workflow.") return } console.log("DEBUG: Skipped direct generation without Try it for now.") /* if (!srcapp.includes(":default") && !dstapp.includes(":default")) { if (appSetupDone === false && setAppSetupDone !== undefined) { setAppSetupDone(true) } getGeneratedWorkflow() } if (missingSource !== undefined && missingDestination !== undefined) { if (appSetupDone === false && setAppSetupDone !== undefined) { setAppSetupDone(true) } } if (getAppFramework !== undefined) { setTimeout(() => { getAppFramework() }, 500) } */ }, [modalOpen, missingSource, missingDestination]) useEffect(() => { //console.log("IN USEEFFECT FOR CONFIG: ", configurationFinished) if (configurationFinished === true && workflow.id !== undefined && workflow.id !== null && workflow.id !== "") { //toast.success("Generation Successful") /* setTimeout(() => { navigate("/workflows/" + workflow.id) }, 2000) */ } }, [configurationFinished, workflow]) const imageSize = 32 const defaultBorder = "1px solid rgba(255,255,255,0.6)" const imagestyleWrapper = { height: imageSize, width: imageSize, borderRadius: imageSize, border: isHomePage ? null : defaultBorder, overflow: "hidden", display: "flex", backgroundColor: theme.palette.inputColor, } const imagestyleWrapperDefault = { height: imageSize, width: imageSize, borderRadius: imageSize, border: isHomePage ? null : defaultBorder, overflow: "hidden", display: "flex", backgroundColor: theme.palette.inputColor, } const imagestyle = { height: imageSize, width: imageSize, borderRadius: imageSize, //border: isHomePage ? null : defaultBorder, overflow: "hidden", backgroundColor: theme.palette.inputColor, } const imagestyleDefault = { display: "block", marginLeft: 9, marginTop: 9, height: imageSize, width: "auto", backgroundColor: theme.palette.inputColor, } if (modalOpen === false && (title === undefined || title === null || title === "")) { if (setIsClicked !== undefined) { setIsClicked(false) } console.log("No title for workflow template popup!"); return null } const loadAppAuth = () => { // Check if it exists, and has keys // if (userdata === undefined || userdata === null || Object.keys(userdata).length === 0) { setErrorMessage("You need to be logged in to try the pre-built Workflow Templates.") setShowLoginButton(true) // Send the user to the login screen after 3 seconds setTimeout(() => { // Make it cancel if the state modalOpen changes if (modalOpen === false) { return } navigate("/login?view=" + window.location.pathname + window.location.search) }, 4500) return } fetch(`${globalUrl}/api/v1/apps/authentication`, { method: "GET", headers: { "Content-Type": "application/json", Accept: "application/json", }, credentials: "include", }) .then((response) => { if (response.status !== 200) { console.log("Status not 200 for setting app auth :O!"); } return response.json(); }) .then((responseJson) => { if (!responseJson.success) { toast("Failed to get app auth: " + responseJson.reason); return } var newauth = []; for (let authkey in responseJson.data) { if (responseJson.data[authkey].defined === false) { continue; } newauth.push(responseJson.data[authkey]); } setAppAuthentication(newauth); }) .catch((error) => { //toast(error.toString()); console.log("New auth error: ", error.toString()); }); } // Can create and set workflows const reloadWorkflow = (workflow_id) => { const new_url = `${globalUrl}/api/v1/workflows/${workflow_id}` return fetch(new_url, { 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; } //setSubmitLoading(false); return response.json(); }) .then((responseJson) => { if (responseJson.success === false) { if (responseJson.reason !== undefined) { toast("Error setting workflow: ", responseJson.reason) } else { toast("Error setting workflow.") } return } else if (responseJson.id !== undefined && responseJson.id !== null && responseJson.id !== "") { setWorkflow(responseJson) } return responseJson; }) .catch((error) => { toast("Failed reloading configured workflow: ", error.toString()); }); }; // Can create and set workflows const saveWorkflow = (workflowdata) => { const new_url = `${globalUrl}/api/v1/workflows?set_auth=true` return fetch(new_url, { method: "POST", headers: { "Content-Type": "application/json", Accept: "application/json", }, body: JSON.stringify(workflowdata), credentials: "include", }) .then((response) => { if (response.status !== 200) { console.log("Status not 200 for workflows :O!"); return; } //setSubmitLoading(false); return response.json(); }) .then((responseJson) => { if (responseJson.success === false) { if (responseJson.reason !== undefined) { toast("Error setting workflow: ", responseJson.reason) } else { toast("Error setting workflow.") } return } // In case it got a new id, this is to make sure it loads with the correct config if (responseJson.id !== undefined && responseJson.id !== null && responseJson.id !== "") { reloadWorkflow(responseJson.id) } return responseJson; }) .catch((error) => { toast("Failed generating workflow: ", error.toString()); }); }; const getGeneratedWorkflow = () => { // POST // https://shuffler.io/api/v1/workflows/merge // destination: {app_id: "b9c2feaf99b6309dabaeaa8518c61d3d", app_name: "Servicenow_API", app_version: "",…} // id: "" // middle:[] // name: "Email analysis" // source:{app_id: "accdaaf2eeba6a6ed43b2efc0112032d", app_name if (requestSent === true) { return } console.log("SRCAPP: ", srcapp, "DSTAPP: ", dstapp) if (srcapp === undefined || srcapp === null) { srcapp = "" } if ((srcapp !== undefined && srcapp !== null && srcapp.includes(":default")) || (dstapp !== undefined && dstapp !== null && dstapp.includes(":default"))) { toast("You need to select both a source and destination app before generating this workflow.") if (srcapp !== undefined && srcapp !== null && srcapp.includes(":default")) { setMissingSource({ "type": srcapp.split(":")[0], }) } if (dstapp !== undefined && dstapp !== null && dstapp.includes(":default")) { setMissingDestination({ "type": dstapp.split(":")[0], }) } return } setWorkflowLoading(true) const newsrcapp = srcapp const newdstapp = dstapp const mergedata = { name: title, id: "", source: { app_name: newsrcapp, }, middle: [], destination: { app_name: newdstapp, }, } setRequestSent(true) const url = isCloud ? `${globalUrl}/api/v1/workflows/merge` : `https://shuffler.io/api/v1/workflows/merge` fetch(url, { method: "POST", headers: { "Content-Type": "application/json", Accept: "application/json", }, credentials: "include", body: JSON.stringify(mergedata), }) .then((response) => { if (response.status !== 200) { //console.log("Status not 200 for framework!"); setRequestSent(false) } setWorkflowLoading(false) return response.json(); }) .then((responseJson) => { if (responseJson.id !== undefined && responseJson.id !== null && responseJson.id !== "" && responseJson.name !== undefined && responseJson.name !== null && responseJson.name !== "") { console.log("Success in workflow template (prebuilt): ", responseJson); setWorkflow(responseJson) // Sets it in the database properly saveWorkflow(responseJson) return } if (responseJson.success === false) { //console.log("Error in workflow template: ", responseJson.error); setRequestSent(false) const defaultMessage = "Error: Failed to generate workflow the workflow - the Shuffle team has been notified. Contact support@shuffler.io if you want manual help building this usecase." if (responseJson.reason !== undefined && responseJson.reason !== null && responseJson.reason !== "") { setErrorMessage(defaultMessage + "\n\n" + responseJson.reason) } else { setErrorMessage(defaultMessage) } setIsActive(true) //setTimeout(() => { // setModalOpen(false) //}, 5000) } else { console.log("Success in workflow template: ", responseJson); setIsActive(true) if (responseJson.workflow_id === "") { console.log("Failed to build workflow for these tools. Closing in 3 seconds.") return } fetchWorkflow(responseJson.workflow_id) } }) .catch((error) => { console.log("err in framework: ", error.toString()); setRequestSent(false) setWorkflowLoading(false) }) } if (modalOpen === true && !srcapp?.includes(":default") && !dstapp?.includes(":default")) { if (appSetupDone === false && setAppSetupDone !== undefined) { setAppSetupDone(true) } // No autoruns anymore without clicking "Try it" if (workflow.id === undefined && workflowLoading === false && errorMessage === "") { //getGeneratedWorkflow() } } const isFinished = () => { // Look for configuration fields being done in the current modal // 1. Start by finding the modal const template = document.getElementById("workflow-template") if (template === null || template == undefined) { return true } // Find item in template with id app-config const appconfig = template.getElementsByClassName("app-config") if (appconfig === null || appconfig == undefined) { return true } return false } const ModalView = () => { if (modalOpen === false) { return null } const divHeight = 500 const divWidth = 500 return ( { setModalOpen(false); if (setIsClicked !== undefined) { setIsClicked(false) } }} PaperProps={{ style: { backgroundColor: "black", color: "white", minWidth: isHomePage ? null : isMobile ? 300 : 750, maxWidth: isHomePage ? null : isMobile ? 300 : 750, paddingTop: isMobile ? null : 75, itemAlign: "center", }, }} > { setModalOpen(false); }} > Configure Workflow {title === undefined || title === null || title === "" ? null : Selected Usecase:
}
{/* Fix the timeline when errors are fixed.. how? */}
{workflowLoading === true ?
Generating Workflows...
:
{usecaseDetails === undefined || usecaseDetails === null || workflow.id !== undefined ? null : {usecaseDetails?.description} } {errorMessage !== "" ? {errorMessage !== "" ? errorMessage : ""} : null} {showLoginButton ? Sign up : !showTryitOut && !isActive ? : null}
} {!isLoggedIn ? null :
{(appSetupDone === false && missingSource !== undefined || missingDestination !== undefined) ? {"Find relevant Apps for this Usecase"} : null} {(missingSource !== undefined) ?
: null} {(missingDestination !== undefined) ?
: null}
}
) } if (isModalOpenDefault === true) { return } var parsedTitle = title !== undefined && title !== null ? title : "" const maxlength = 50 if (title !== undefined && title !== null && title.length > maxlength) { parsedTitle = title.substring(0, maxlength) + "..." } parsedTitle = parsedTitle.replaceAll("_", " ") const parsedDescription = description !== undefined && description !== null ? description.replaceAll("_", " ") : "" const boxHeight = visualOnly ? 75 : 104 const highlightColor = shownColor !== undefined && shownColor !== null && shownColor !== "" ? shownColor : "#f85a3e" var hasInterest = false if (userdata?.interests !== undefined && userdata?.interests !== null && userdata?.interests?.length > 0) { const comparisonTitle = title === undefined || title === null ? "" : title.trim().toLowerCase().replaceAll(" ", "_") for (var interestkey in userdata.interests) { if (userdata.interests[interestkey].name === undefined || userdata.interests[interestkey].name === null || userdata.interests[interestkey].name === "") { continue } if (modalOpen) { //console.log("COMPARE: ", userdata.interests[interestkey].name.trim().toLowerCase().replaceAll(" ", "_"), comparisonTitle) } if (userdata.interests[interestkey].name.trim().toLowerCase().replaceAll(" ", "_") === comparisonTitle) { if (modalOpen) { //console.log("FOUND: ", comparisonTitle) } hasInterest = true break } } } const borderStyle = isHomePage ? null : isHovered && isActive ? errorMessage !== "" ? "1px solid red" : `2px solid ${theme.palette.green}` : isHovered ? `1px solid ${highlightColor}` : "1px solid rgba(33, 33, 33, 1)" return (
{ setIsHovered(true) setShowTryitout(true) }} onMouseLeave={() => { setIsHovered(false) if (showTryit !== true) { setShowTryitout(false) } }} onClick={() => { if (visualOnly === true) { console.log("Not showing more than visuals. Workflow built: ", workflowBuilt, workflow) if (workflowBuilt !== undefined && workflowBuilt !== null && workflowBuilt?.length > 0) { window.open("/workflows/" + workflowBuilt, "_blank") } else if (workflow.id !== undefined && workflow.id !== null && workflow.id !== "") { window.open("/workflows/" + workflow.id, "_blank") } else { toast("Click 'Try this usecase' to generate workflows for this usecase.") } return } if (!isLoggedIn) { loadAppAuth() setModalOpen(true) } else if (isLoggedIn && errorMessage !== "") { toast.error("Already failed to generate a workflow for this usecase. Please try again later or contact support@shuffler.io.") setModalOpen(true) } else if (isActive) { // toast.success("Workflow already generated. Please try another workflow template!") // FIXME: Remove these? loadAppAuth() setModalOpen(true) //getGeneratedWorkflow() } else { setModalOpen(true) //setWorkflowLoading(false) } }} >
{shownColor !== undefined && shownColor !== null && shownColor !== "" ?
: null}
{img1 !== undefined && img1 !== "" && srcapp !== undefined && srcapp !== "" ?
:
}
{img2 !== undefined && img2 !== "" && dstapp !== undefined && dstapp !== "" ?
:
}
{parsedTitle}
{isActive === true && errorMessage === "" ? visualOnly === true ? : : ""} {!isActive && hasInterest === true && !visualOnly ? : null}
{showTryitOut && !isActive && !visualOnly ? : null}
) } export default WorkflowTemplatePopup