diff --git a/frontend/src/components/EditWorkflow.jsx b/frontend/src/components/EditWorkflow.jsx index 5f571110..dcfa3057 100644 --- a/frontend/src/components/EditWorkflow.jsx +++ b/frontend/src/components/EditWorkflow.jsx @@ -545,6 +545,8 @@ const EditWorkflow = (props) => { }} > } label="Test" /> + } label="Staging" /> + } label="Pre-production" /> } label="Production" /> diff --git a/frontend/src/components/Navbar.jsx b/frontend/src/components/Navbar.jsx index 21321b94..04e67ac7 100644 --- a/frontend/src/components/Navbar.jsx +++ b/frontend/src/components/Navbar.jsx @@ -1363,6 +1363,7 @@ const Navbar = (props) => { sx={buttonStyles} onClick={() => { if(isCloud) { + // navigate("/new-pricing"); navigate("/pricing"); ReactGA.event({ category: "navbar", @@ -1370,7 +1371,7 @@ const Navbar = (props) => { label: "go_to_pricing", }) } else { - window.open("https://shuffler.io/pricing", '_blank'); + window.open("https://shuffler.io/pricing?env=Self-hosted", '_blank'); return; } }} diff --git a/frontend/src/components/ParsedAction.jsx b/frontend/src/components/ParsedAction.jsx index 35bc0ea4..5c35ec56 100755 --- a/frontend/src/components/ParsedAction.jsx +++ b/frontend/src/components/ParsedAction.jsx @@ -179,6 +179,7 @@ const ParsedAction = (props) => { suborgWorkflows, originalWorkflow, + runFromHere, } = props; let navigate = useNavigate() @@ -233,11 +234,16 @@ const ParsedAction = (props) => { // auth, required, optional var changed = false if (selectedActionParameters === undefined || selectedActionParameters === null || selectedActionParameters.length === 0) { + console.log("Returning because no params") return } if (selectedApp !== undefined && selectedApp !== null && selectedApp.generated !== true) { - return + + if (isAgent || isIntegration) { + } else { + return + } } // Fixing required fields with a shitty structure :) @@ -346,13 +352,23 @@ const ParsedAction = (props) => { // 3. generated fields & all else - const newparams = auth + var newparams = auth .concat(bodyfield) .concat(required) .concat(generated_optional) .concat(special_optional) .concat(optional) + const dedupedParams = [] + for (var paramKey in newparams) { + const param = newparams[paramKey] + if (dedupedParams.find(item => item.name === param.name) === undefined) { + dedupedParams.push(param) + } + } + + newparams = dedupedParams + var newkeyorder = [] for (let paramkey in newparams) { //console.log("Param: ", newparams[paramkey]) @@ -1814,6 +1830,29 @@ const ParsedAction = (props) => { + + {(selectedAction?.generated === true && selectedAction?.app_version === "1.0.0") || (selectedAction?.app_name === "Shuffle Tools" && selectedAction?.app_version !== "1.2.0") ? diff --git a/frontend/src/theme.jsx b/frontend/src/theme.jsx index 4897e2ca..1621dc42 100644 --- a/frontend/src/theme.jsx +++ b/frontend/src/theme.jsx @@ -27,7 +27,7 @@ const theme = createTheme(adaptV4Theme({ distributionColor: "#40E0D0", green: "#5cc879", - borderRadius: 10, + borderRadius: 8, defaultBorder: "1px solid rgba(255,255,255,0.3)", //jsonTheme: "brewer", @@ -112,39 +112,39 @@ const theme = createTheme(adaptV4Theme({ MuiCssBaseline: { styleOverrides: ` @font-face { - font-family: 'Roboto'; + font-family: 'Inter'; font-style: normal; font-display: swap; font-weight: 300; - src: local('Roboto Light'), local('Roboto-Light'); + src: local('Inter Light'), local('Inter-Light'); } @font-face { - font-family: 'Roboto'; + font-family: 'Inter'; font-style: normal; font-display: swap; font-weight: 400; - src: local('Roboto'), local('Roboto-Regular'); + src: local('Inter Regular'), local('Inter-Regular'); } @font-face { - font-family: 'Roboto'; + font-family: 'Inter'; font-style: normal; font-display: swap; font-weight: 500; - src: local('Roboto Medium'), local('Roboto-Medium'); + src: local('Inter Medium'), local('Inter-Medium'); } @font-face { - font-family: 'Roboto'; + font-family: 'Inter'; font-style: normal; font-display: swap; font-weight: 600; - src: local('Roboto SemiBold'), local('Roboto-SemiBold'); + src: local('Inter SemiBold'), local('Inter-SemiBold'); } @font-face { - font-family: 'Roboto'; + font-family: 'Inter'; font-style: normal; font-display: swap; font-weight: 700; - src: local('Roboto Bold'), local('Roboto-Bold'); + src: local('Inter Bold'), local('Inter-Bold'); } `, }, diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index 1f32c6ec..3394dfd9 100755 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -652,6 +652,31 @@ const AngularWorkflow = (defaultprops) => { ], "multiselect": true, }, + { + "name": "memory", + "value": "", + "required": true, + "description": "Whether to store the conversation in memory", + "options": [ + "Nothing", + "Shuffle Datastore", + ], + "multiselect": false, + "disabled": true, + }, + { + "name": "knowledge", + "value": "", + "required": true, + "description": "The knowledge we should inject into the context window", + "options": [ + "Nothing", + "Shuffle Files", + ], + "multiselect": false, + "disabled": true, + }, + ] }], large_image: theme.palette.singulBlackWhite, @@ -772,7 +797,7 @@ const AngularWorkflow = (defaultprops) => { const [loadedApps, setLoadedApps] = React.useState([]) - const loadAppConfig = (appId, select) => { + const loadAppConfig = (appId, select, skipAppLoad) => { if (appId === undefined || appId === null || appId.length === 0) { console.log("No appId to load") return @@ -868,7 +893,7 @@ const AngularWorkflow = (defaultprops) => { setSelectedApp(foundapp) } - if (apps === undefined || apps === null || apps.length === 0) { + if ((apps === undefined || apps === null || apps.length === 0) && skipAppLoad !== true) { console.log("No apps to update :(") getApps() return @@ -2785,6 +2810,94 @@ const AngularWorkflow = (defaultprops) => { return true; }; + const runFromHere = (curAction) => { + if (curAction.app_id === undefined || curAction.app_id === null || curAction.app_id.length === 0) { + toast.error("No app id found for action. Please contact support@shuffler.io if this persists") + return + } + + if (workflow.id !== undefined && workflow.id !== null && workflow.id.length > 0) { + curAction.source_workflow = workflow.id + } + + // Based on the previous execution id + console.log("WORKFLOW EXEC: ", workflowExecutions) + // Look for the "execution_id" parameter + const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search; + const execFound = new URLSearchParams(cursearch).get("execution_id"); + if (execFound !== undefined && execFound !== null && execFound.length > 0) { + toast.info("Rerunning based on previously watched execution id") + curAction.source_execution = execFound + } else if (workflowExecutions !== undefined && workflowExecutions !== null && workflowExecutions.length > 0) { + curAction.source_execution = workflowExecutions[0].execution_id + } else { + toast.error("No previous execution found. Please run the workflow first.") + return + } + + setExecutionRunning(true) + setExecutionRequestStarted(true) + var headers = { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + } + + if (workflow.org_id !== undefined && workflow.org_id !== null && workflow.org_id.length > 0) { + headers["Org-Id"] = workflow.org_id + } + + // Rerun makes it return execution_id + authorization + const appRunUrl = `${globalUrl}/api/v1/apps/${curAction.app_id}/run?rerun=true` + fetch(appRunUrl, { + method: 'POST', + headers: headers, + body: JSON.stringify(curAction), + credentials: "include", + }) + .then((response) => { + setExecutionRunning(false) + setExecutionRequestStarted(false) + + if (response.status !== 200) { + console.log("Status not 200 for stream results :O!") + } + + return response.json() + }) + .then((responseJson) => { + setExecutionRequestStarted(false) + + if (responseJson?.success === false) { + setExecutionRunning(false) + + if (responseJson?.reason !== undefined && responseJson?.reason !== null && responseJson?.reason.length > 0) { + toast.error(responseJson.reason) + } else { + toast.error("Failed to run the action. Please try again or contact support@shuffler.io") + } + + return + } else if (responseJson?.success === true && responseJson?.execution_id !== undefined && responseJson?.execution_id !== null && responseJson?.execution_id.length > 0) { + navigate(`?execution_id=${responseJson.execution_id}`) + setExecutionRequest({ + execution_id: responseJson.execution_id, + authorization: responseJson.authorization, + }) + + setExecutionData({}) + setExecutionModalOpen(true) + setExecutionModalView(1) + start() + } + }) + .catch((error) => { + toast.error("Failed to run the action. "+error.toString()) + + setExecutionRunning(false) + setExecutionRequestStarted(false) + }) + } + const executeWorkflow = (executionArgument, startNode, hasSaved, skip_popup) => { if (hasSaved === false) { @@ -3203,6 +3316,13 @@ const AngularWorkflow = (defaultprops) => { return } + for (var key in responseJson) { + const curapp = responseJson[key] + if (curapp?.actions === undefined || curapp?.actions === null || curapp?.actions?.length === 0 || curapp?.actions?.length === 1) { + loadAppConfig(curapp?.id, false, true) + } + } + // Find app with ID "794e51c3c1a8b24b89ccc573a3defc47" (gmail) to force-break it, // Find app with ID "3e2bdf9d5069fe3f4746c29d68785a6a" (shuffle tools) to force-break it, // as to ensure the autocorrect works. @@ -13580,14 +13700,18 @@ const AngularWorkflow = (defaultprops) => { setSourceValue({ ...sourceValue, value: value - }); + }) + + setUpdate(Math.random()) } else if (fieldType === "destination") { setDestinationValue({ ...destinationValue, value: value - }); - } - }; + }) + + setUpdate(Math.random()) + } + } const conditionsModal = ( @@ -14763,6 +14887,7 @@ const AngularWorkflow = (defaultprops) => { ] const handleSubflowParamChange = (triggerId, triggerField, newData) => { + var updateFail = "" if (workflow !== undefined && workflow !== null) { // Find the trigger with matching id @@ -14778,9 +14903,20 @@ const AngularWorkflow = (defaultprops) => { setWorkflow(workflow); setSelectedTriggerValue(newData) setLastSaved(false); - } - } - } + setUpdate(Math.random()) + } else { + updateFail = "Parameter is undefined or null" + } + } else { + updateFail = "Trigger is undefined or null" + } + } else { + updateFail = "Workflow is undefined or null" + } + + if (updateFail !== "") { + toast.error(updateFail + " - Failed to update subflow parameter value. Please try again.") + } } const SubflowSidebar = Object.getOwnPropertyNames(selectedTrigger).length === 0 || workflow.triggers[selectedTriggerIndex] === undefined || selectedTrigger.trigger_type !== "SUBFLOW" ? null : @@ -20801,9 +20937,9 @@ const AngularWorkflow = (defaultprops) => { onClose={() => { setExecutionModalOpen(false) - const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search; - const newitem = removeParam("execution_id", cursearch); - navigate(curpath + newitem) + //const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search; + //const newitem = removeParam("execution_id", cursearch); + //navigate(curpath + newitem) }} style={{ resize: "both", @@ -21256,7 +21392,11 @@ const AngularWorkflow = (defaultprops) => {

Details

+ Rerun workflow. Uses same startnode as the original. Runs from scratch. + + } placement="top" style={{ zIndex: 50000 }} > @@ -21558,7 +21698,7 @@ const AngularWorkflow = (defaultprops) => { : null} - {userdata.support === true && executionData.workflow !== undefined && executionData.workflow !== null && executionData.status !== "EXECUTING" ? + {userdata.support === true && executionData.workflow !== undefined && executionData.workflow !== null && executionData.status !== "EXECUTING" && executionData.status !== "ABORTED" ?
{ suborgWorkflows={suborgWorkflows} originalWorkflow={originalWorkflow} + runFromHere={runFromHere} />
@@ -24731,6 +24872,8 @@ const AngularWorkflow = (defaultprops) => { } const handleActionParamChange = (actionId, fieldName, newData) => { + var updateFail = "" + if (workflow !== undefined) { // Find the action with matching id const actionIndex = workflow?.actions.findIndex(action => action.id === actionId); @@ -24745,9 +24888,20 @@ const AngularWorkflow = (defaultprops) => { // Update workflow state to trigger re-render setWorkflow({...workflow}); setLastSaved(false); - } - } - } + setUpdate(Math.random()) + } else { + updateFail = "Parameter is undefined or null" + } + } else { + updateFail = "Trigger is undefined or null" + } + } else { + updateFail = "Workflow is undefined or null" + } + + if (updateFail !== "") { + toast.error(updateFail + " - Failed to update subflow parameter value. Please try again.") + } } /* var foundusecase = {} diff --git a/frontend/src/views/AppExplorer.jsx b/frontend/src/views/AppExplorer.jsx index d37709ee..b19e5cdd 100644 --- a/frontend/src/views/AppExplorer.jsx +++ b/frontend/src/views/AppExplorer.jsx @@ -1088,14 +1088,13 @@ const AppExplorer = (props) => { setNewWorkflowTags(newWorkflowTags); } - // This is annoying (: - var securitySchemes = data.components.securityDefinitions; + var securitySchemes = data?.components?.securityDefinitions; if (securitySchemes === undefined) { - securitySchemes = data.securitySchemes; + securitySchemes = data?.securitySchemes; } if (securitySchemes === undefined) { - securitySchemes = data.components.securitySchemes; + securitySchemes = data?.components?.securitySchemes; } const allowedfunctions = [ @@ -1681,7 +1680,7 @@ const AppExplorer = (props) => { setExecutionResult({ valid: false, result: - "Couldn't finish execution (2). Please fill all the required fields, and validate the execution.", + "Couldn't finish execution OR no result was returned (2). Please fill all the required fields, and validate the execution.", }); } diff --git a/frontend/src/views/Docs.jsx b/frontend/src/views/Docs.jsx index 8b56b2ce..90def218 100755 --- a/frontend/src/views/Docs.jsx +++ b/frontend/src/views/Docs.jsx @@ -343,10 +343,15 @@ const Docs = (defaultprops) => { return } + console.log("PROPKEY: ", propkey) if (location.pathname.includes("/docs/")) { if (propkey === "cookie_policy" || propkey === "compliance" || propkey === "privacy_policy" || propkey === "terms_of_service") { navigate(`/legal/${propkey}`) } + + if (propkey === "app_creation") { + navigate('/docs/apps#app-creation-introduction') + } } }, [location]); @@ -932,7 +937,7 @@ const Docs = (defaultprops) => { } if (responseJson.reason !== undefined && responseJson.reason !== null && responseJson.reason.includes("404: Not Found") && !isArticlePage) { - navigate("/docs") + //navigate("/docs") return } diff --git a/frontend/src/views/SettingsPage.jsx b/frontend/src/views/SettingsPage.jsx index a507857f..c19cd987 100755 --- a/frontend/src/views/SettingsPage.jsx +++ b/frontend/src/views/SettingsPage.jsx @@ -527,6 +527,8 @@ const Settings = (props) => { }; const generateApikey = () => { + toast.info("Generating new API key. This may take a bit."); + fetch(globalUrl + "/api/v1/generateapikey", { method: "GET", headers: { @@ -538,7 +540,7 @@ const Settings = (props) => { .then((response) => { if (response.status !== 200) { console.log("Status not 200 for WORKFLOW EXECUTION :O!"); - } + } return response.json(); }) @@ -836,7 +838,12 @@ const Settings = (props) => { variant="outlined" />