diff --git a/frontend/src/components/Billing.jsx b/frontend/src/components/Billing.jsx index 44e36334..ef09b6f4 100644 --- a/frontend/src/components/Billing.jsx +++ b/frontend/src/components/Billing.jsx @@ -891,7 +891,7 @@ const Billing = memo((props) => { : null} - {showSupport ? + {/* {showSupport ? - : null} + : null} */} ) } @@ -2021,7 +2021,7 @@ const Billing = memo((props) => {
- {isCloud && + {/* {isCloud && selectedOrganization.subscriptions !== undefined && selectedOrganization.subscriptions !== null && selectedOrganization.subscriptions.length > 0 && @@ -2043,7 +2043,7 @@ const Billing = memo((props) => { /> ) }) - : null} + : null} */}
{isCloud && billingInfo.subscription !== undefined && billingInfo.subscription !== null ? isChildOrg ? null : diff --git a/frontend/src/components/CacheView.jsx b/frontend/src/components/CacheView.jsx index 34b4608f..f4b8b9cb 100644 --- a/frontend/src/components/CacheView.jsx +++ b/frontend/src/components/CacheView.jsx @@ -151,13 +151,16 @@ const CacheView = memo((props) => { if (fileCategories.length === 1 && fileCategories[0] === "default") { var newcategories = ["default"] for (var key in responseJson.keys) { - if (responseJson.keys[key].category !== undefined && responseJson.keys[key].category !== null && responseJson.keys[key].category !== "" && !fileCategories.includes(responseJson.keys[key].category)) { - newcategories.push(responseJson.keys[key].category); + var category = responseJson.keys[key].category + if (category !== undefined && category !== null && category !== ""){ + category = category.replaceAll(" ", "_") + + if (!newcategories.includes(category)) { + newcategories.push(category) + } } } - console.log("CATEGORIES: ", newcategories) - setFileCategories(newcategories) } } @@ -357,6 +360,7 @@ const CacheView = memo((props) => { { editCache ? "Edit Key" : "Add Key"}{selectedCategory === "" || selectedCategory === "default" ? "" : ` in category '${selectedCategory}'`} +
Key @@ -1041,13 +1045,13 @@ const CacheView = memo((props) => { { deleteCache(orgId, data.key); //deleteFile(orgId); diff --git a/frontend/src/components/LeftSideBar.jsx b/frontend/src/components/LeftSideBar.jsx index baf60f06..92aeabe4 100644 --- a/frontend/src/components/LeftSideBar.jsx +++ b/frontend/src/components/LeftSideBar.jsx @@ -774,8 +774,8 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => { }, [window?.location?.pathname]); const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent); - - + const showPartnerLogo = userdata?.org_status?.includes("integration_partner") && userdata?.active_org?.image !== undefined && userdata?.active_org?.image !== null && userdata?.active_org?.image.length > 0 + return (
{ } }} > - + Shuffle Logo @@ -1611,7 +1613,7 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => { }} > - {expandLeftNav && + {userdata?.licensed !== true && !userdata?.org_status?.includes("integration_partner") && expandLeftNav &&
- {subscription.active === true && !isScale && } + {subscription.active === true && !isScale && }
{top_text === "Base Cloud Access" && userdata.has_card_available === true && !isScale ? { color="primary" /> : null} - + {top_text} @@ -396,7 +453,7 @@ const LicencePopup = (props) => { }} /> : null} - {isCloud && highlight === true && top_text !== "Base Cloud Access" ? + {isCloud && highlight === true && top_text !== "Starter Plan" ? { {subscription.currency_text}{subscription.price} - / {subscription.interval} + {subscription.interval.length > 0 ? `/ ${subscription.interval}` : ""}
: null} @@ -517,23 +574,17 @@ const LicencePopup = (props) => { : null} - {subscription.name.includes("Scale") ? - "" - : - - userdata.has_card_available === true ? - "While you have a card attached to your account, Shuffle will no longer prevent workflows from running. Billing will occur at the start of each month." - : + { isCloud ? - userdata?.app_execution_limit && userdata?.app_execution_limit >= 300000 ? - "You have subscribed to the Enterprise plan, which includes " + (userdata?.app_execution_limit/1000) + "K app runs/month. You can increase the limit by upgrading current plan. Contact support@shuffler.io for more information." : - `You are not subscribed to any plan and are using the free plan with max 10,000 app runs per month. Upgrade to deactivate this limit.` + userdata?.app_execution_limit && userdata?.app_execution_limit !== 10000 ? + "You have already subscribed to the Scale plan, which includes " + (userdata?.app_execution_limit/1000) + "K app runs/month. You can increase the limit by upgrading current plan. Contact support@shuffler.io for more information." : + `You are using free Starter plan with max ${userdata?.app_execution_limit === 10000 ? "10,000" : "2,000"} runs per month. Upgrade to increase this limit.` : `You are not subscribed to any plan and are using the free, open source plan. This plan has no enforced limits, but scale issues may occur due to CPU congestion.` } - {isCloud && (userdata.has_card_available === true || selectedOrganization?.Billing?.Email?.length > 0 )? + {/* {isCloud && (userdata.has_card_available === true || selectedOrganization?.Billing?.Email?.length > 0 )?
Billing email: {BillingEmail}
- : null} + : null} */}
{isCloud ? ( ) : null} } - + color="primary">Recommended + + { + billingCycle === "annual" && + ( + + + 10% OFF + + + ) + } +
+
+ + {scaleValue > 300 ? "Enterprise Plan" : "Scale Plan"} + + + + + Monthly + + + Annual + + +
- {shuffleVariant === 1 ? "Scale" : "Enterprise"} - {shuffleVariant === 0 ? - "SaaS / Cloud - Per Month" - : - "Open Source + Scale License" - } + App Runs Units - { - - if (calculatedCores === "Get A Quote") { - console.log("Clicked on get a quote") - if (window.drift !== undefined) { - window.drift.api.startInteraction({ interactionId: 340785 }) - } - } - }}>{calculatedCost} - For {shuffleVariant === 1 ? `${selectedValue} CPU cores` : `${selectedValue}k App Runs`}: -
- - { - handleChange(event, newValue) +
+ + {scaleValue > 300 ? "Let's Talk" : `$${getPrice(32) * (scaleValue / 10)}`} + + 300 ? 1 : 0, }} - marks - value={selectedValue} - step={shuffleVariant === 0 ? 100 : 4} - min={shuffleVariant === 0 ? 100 : 8} - max={shuffleVariant === 0 ? 1000 : 32} - valueLabelDisplay="auto" - /> + > + {scaleValue > 300 ? `for ${scaleValue > 500 ? "500k+" : `${scaleValue}k`} App Runs` : `/month for ${scaleValue}k App Runs`} +
+ + { + if(value === 510){ + return "500k+" + } + return `${value}k` + }} + step={10} + min={10} + max={510} + marks + sx={{ + color: "#ff8544", + "& .MuiSlider-thumb": { + width: 15, + height: 15, + }, + "& .MuiSlider-valueLabel": { + backgroundColor: "rgba(33, 33, 33, 1)", + color: "rgba(241, 241, 241, 1)", + fontSize: 14, + borderRadius: "4px", + border: "1px solid rgba(73, 73, 73, 1)", + fontFamily: theme?.typography?.fontFamily, + }, + }} + /> +
{defaultTaskIcon} - Priority Support + Standard Email Support
@@ -1117,7 +1357,7 @@ const LicencePopup = (props) => {
{defaultTaskIcon} - Help with Workflow and App development + 30 Days workflow run history
@@ -1136,7 +1376,7 @@ const LicencePopup = (props) => { navigate("/pricing") } else { - window.open("https://shuffler.io/pricing?tab=onprem", "_blank") + window.open("https://shuffler.io/pricing?tab=Self-Hosted", "_blank") } }} color="primary" @@ -1149,6 +1389,10 @@ const LicencePopup = (props) => { style={{ borderRadius: 4, textTransform: "capitalize", color: "#1a1a1a", backgroundColor: "#ff8544", width: "100%", fontSize: 16}} onClick={() => { if (isCloud) { + if(scaleValue > 300){ + navigate("/contact?category=cloud_enterprise_plan") + return; + } ReactGA.event({ category: "header", action: "upgread_clicks_popup", @@ -1170,7 +1414,7 @@ const LicencePopup = (props) => { }} color="primary" > - Upgrade + {scaleValue > 300 ? "Let's Talk" : "Upgrade"}
diff --git a/frontend/src/components/Navbar.jsx b/frontend/src/components/Navbar.jsx index 04e67ac7..54362e86 100644 --- a/frontend/src/components/Navbar.jsx +++ b/frontend/src/components/Navbar.jsx @@ -1363,7 +1363,6 @@ const Navbar = (props) => { sx={buttonStyles} onClick={() => { if(isCloud) { - // navigate("/new-pricing"); navigate("/pricing"); ReactGA.event({ category: "navbar", @@ -1371,7 +1370,7 @@ const Navbar = (props) => { label: "go_to_pricing", }) } else { - window.open("https://shuffler.io/pricing?env=Self-hosted", '_blank'); + window.open("https://shuffler.io/pricing?env=Self-Hosted", '_blank'); return; } }} diff --git a/frontend/src/components/OrgHeaderexpandedNew.jsx b/frontend/src/components/OrgHeaderexpandedNew.jsx index e5317da3..8d95b046 100644 --- a/frontend/src/components/OrgHeaderexpandedNew.jsx +++ b/frontend/src/components/OrgHeaderexpandedNew.jsx @@ -538,7 +538,7 @@ const OrgHeaderexpandedNew = (props) => { renderValue={(selected) => selected.join(', ')} MenuProps={MenuProps} > - {["contacted", "lead", "demo done", "pov", "customer", "open source", "student", "internal", "creator", "tech partner", "old customer", "old lead"].map((name) => ( + {["contacted", "lead", "demo done", "pov", "customer", "open source", "student", "internal", "creator", "tech partner", "integration partner", "distribution partner", "service partner", "old customer", "old lead"].map((name) => ( -1} /> diff --git a/frontend/src/components/ParsedAction.jsx b/frontend/src/components/ParsedAction.jsx index 5c35ec56..e2e7e14b 100755 --- a/frontend/src/components/ParsedAction.jsx +++ b/frontend/src/components/ParsedAction.jsx @@ -682,11 +682,14 @@ const ParsedAction = (props) => { // Process workflowExecutions if (workflowExecutions.length > 0) { + var appended = false + var foundvalue = "" for (let execution of workflowExecutions) { const execArg = execution.execution_argument; if (execArg && execArg.length > 0) { const valid = validateJson(execArg); if (valid.valid) { + appended = true newActionList.push({ type: "Runtime Argument", name: "Runtime Argument", @@ -697,9 +700,23 @@ const ParsedAction = (props) => { }) break + } else { + foundvalue = execArg } } } + + if (!appended && foundvalue !== undefined && foundvalue !== "") { + newActionList.push({ + type: "Runtime Argument", + name: "Runtime Argument", + highlight: "exec", + autocomplete: "exec", + + value: foundvalue, + example: foundvalue, + }) + } } // Add default Runtime Argument if none were added @@ -788,6 +805,8 @@ const ParsedAction = (props) => { } labels.push(parentNode.label); + + var secondaryExample = "" let exampleData = parentNode.example ?? ""; if (parentNode?.app_name === "http") { exampleData = "" @@ -798,9 +817,13 @@ const ParsedAction = (props) => { const foundResult = exec.results?.find(result => result?.action?.id === parentNode?.id); if (foundResult) { const valid = validateJson(foundResult.result); - if (valid.valid && valid.result.success !== false) { - exampleData = valid.result - break + if (valid.valid) { + if (valid.result.success !== false) { + exampleData = valid.result + break + } + } else { + secondaryExample = foundResult.result } } } @@ -834,6 +857,10 @@ const ParsedAction = (props) => { } } } + } + + if (exampleData === "" && secondaryExample !== "") { + exampleData = secondaryExample } if (parentNode.label === undefined) { @@ -1178,7 +1205,9 @@ const ParsedAction = (props) => { selectedAction.parameters[1].value = splitparsed[1] if (splitparsed.length > 2) { - toast.warn("Filter list only supports filtering at the first level. If you want multi-level filtering, please use the 'execute python' action with the 'filter a list' function in the code editor.") + toast.warn("Filter list only supports filtering on the first list. If you want multi-level filtering, please use the 'execute python' action with the 'filter a list' function in the code editor.", { + autoClose: 10000, + }) } else if (selectedAction.parameters[1].value.includes(".#")) { toast.warn("This filter may not work due to using .# indexing. Please use the 'execute python' action and try the 'filter a list' function in the code editor.") } @@ -1830,28 +1859,37 @@ const ParsedAction = (props) => { - + + {(selectedAction?.generated === true && selectedAction?.app_version === "1.0.0") || (selectedAction?.app_name === "Shuffle Tools" && selectedAction?.app_version !== "1.2.0") ?
} style={{ display: "table-cell", padding: 8, verticalAlign: "middle" }} diff --git a/frontend/src/components/UserManagmentTab.jsx b/frontend/src/components/UserManagmentTab.jsx index 8aff24a5..bb6870ff 100644 --- a/frontend/src/components/UserManagmentTab.jsx +++ b/frontend/src/components/UserManagmentTab.jsx @@ -248,6 +248,14 @@ const UserManagmentTab = memo((props) => { return; } + if (event.target.value.includes("ALL")) { + toast.info("Adding to available all sub-organizations. This may take a minute.") + event.target.value = selectedOrganization.child_orgs.map((org) => org.id) + } else if (event.target.value.includes("None")) { + toast.info("Removing from all sub-organizations. This may take a minute") + event.target.value = [] + } + console.log("event: ", event.target.value); setMatchingOrganizations(event.target.value); // Workaround for empty orgs @@ -286,6 +294,14 @@ const UserManagmentTab = memo((props) => { }} MenuProps={MenuProps} > + + + + + + + + {selectedOrganization.child_orgs.map((org, index) => ( -1} /> diff --git a/frontend/src/components/WorkflowValidationTimeline.jsx b/frontend/src/components/WorkflowValidationTimeline.jsx index 78e6b766..2d724cfc 100644 --- a/frontend/src/components/WorkflowValidationTimeline.jsx +++ b/frontend/src/components/WorkflowValidationTimeline.jsx @@ -608,8 +608,6 @@ const WorkflowValidationTimeline = (props) => { const ballsize = 8 const topMargin = 20 - console.log("CHIP: ", index, chipColor, chipBackground) - const chipStyle = { height: 40, minWidth: 125, diff --git a/frontend/src/views/Admin2.jsx b/frontend/src/views/Admin2.jsx index 45d933b9..30a356e5 100644 --- a/frontend/src/views/Admin2.jsx +++ b/frontend/src/views/Admin2.jsx @@ -76,6 +76,18 @@ const Admin2 = (props) => { leads.push("tech partner"); } + if (responseJson.lead_info.integration_partner) { + leads.push("integration partner"); + } + + if (responseJson.lead_info.distribution_partner) { + leads.push("distribution partner"); + } + + if (responseJson.lead_info.service_partner) { + leads.push("service partner"); + } + if (responseJson.lead_info.creator) { leads.push("creator"); } diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index 3394dfd9..2d800178 100755 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -475,6 +475,7 @@ const AngularWorkflow = (defaultprops) => { const [authGroups, setAuthGroups] = React.useState([]) const curpath = typeof window === "undefined" || window.location === undefined ? "" : window.location.pathname; + const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search; // 0 = normal, 1 = just done, 2 = normal @@ -913,7 +914,7 @@ const AngularWorkflow = (defaultprops) => { break } } else { - console.log("Found app, but no actions: ", foundapp) + //console.log("Found app, but no actions: ", foundapp) } if (cy !== undefined && cy !== null) { @@ -1794,7 +1795,6 @@ const AngularWorkflow = (defaultprops) => { const newkeys = sortByKey(responseJson.executions, "-started_at"); setWorkflowExecutions(newkeys); - const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search; var tmpView = new URLSearchParams(cursearch).get("execution_id"); if (execution_id !== undefined && execution_id !== null && execution_id.length > 0 && (tmpView === undefined || tmpView === null || tmpView.length === 0)) { tmpView = execution_id; @@ -1850,7 +1850,6 @@ const AngularWorkflow = (defaultprops) => { } } } else { - const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search; var tmpView = new URLSearchParams(cursearch).get("execution_id"); if (tmpView === undefined || tmpView === null || tmpView.length === 0) { const execution_id = tmpView; @@ -1893,7 +1892,6 @@ const AngularWorkflow = (defaultprops) => { //toast("Failed loading the workflow run") console.log("Status not 200 for stream results :O!"); - //const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search; //const newitem = removeParam("execution_id", cursearch); //navigate(curpath + newitem) } @@ -2821,9 +2819,7 @@ const AngularWorkflow = (defaultprops) => { } // 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") @@ -2878,7 +2874,7 @@ const AngularWorkflow = (defaultprops) => { 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}`) + navigate(`?execution_id=${responseJson.execution_id}&node=${curAction.id}&rerun=true`) setExecutionRequest({ execution_id: responseJson.execution_id, authorization: responseJson.authorization, @@ -4316,7 +4312,6 @@ const AngularWorkflow = (defaultprops) => { // Check for execution_id in URL // don't redirect if it exists - const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search; var execFound = new URLSearchParams(cursearch).get("execution_id"); var sessionToken = new URLSearchParams(cursearch).get("session_token"); if (execFound === null && sessionToken === null) { @@ -4978,7 +4973,7 @@ const AngularWorkflow = (defaultprops) => { } } - if (nodedata.app_name === "Webhook" || nodedata.app_name === "Schedule" || nodedata.app_name === "Gmail" || nodedata.app_name === "Office365") { + if (nodedata.app_name === "Webhook" || nodedata.app_name === "Schedule") { if (!found) { //console.log("Find amount of executions for the specific nodetype: ", nodedata.app_name, "Executions: ", workflowExecutions) // Find how many executions it has @@ -5007,6 +5002,7 @@ const AngularWorkflow = (defaultprops) => { } } else { // Readding the icon after moving the node + /* if (!found) { const iconInfo = GetIconInfo(nodedata); const svg_pin = ``; @@ -5034,6 +5030,7 @@ const AngularWorkflow = (defaultprops) => { } else { //console.log("Node already exists - don't add descriptor node"); } + */ } } @@ -7636,25 +7633,9 @@ const AngularWorkflow = (defaultprops) => { } break; case 86: - if (event.ctrlKey) { - //console.log("CTRL+V") - // The below parts are handled in the function handlePaste() - /* - const clipboard = navigator.clipboard - if (clipboard === undefined || window === undefined || window === null) { - toast("Can only use cliboard over HTTPS (port 3443)") - return - } - - console.log("CLIPBOARD: ", window.clipboardData) - const pastedData = window.clipboardData.getData('Text'); - console.log("PASTED: ", pastedData) - - - //var tmpAuth = JSON.parse(JSON.stringify(appAuthentication)) - var jsonvalid = true - var parsedjson = [] - */ + console.log("CTRL+V? ctrl: ", event.ctrlKey) + if (event.ctrlKey) { + // Paste is handled in the handlePaste() function. } break; case 88: @@ -7678,30 +7659,28 @@ const AngularWorkflow = (defaultprops) => { }; const handlePaste = (event) => { - if ( - event.path !== undefined && - event.path !== null && - event.path.length > 0 - ) { + console.log("PASTE EVENT: ", event) + if (event.path !== undefined && event.path !== null && event.path.length > 0) { if (event.path[0].localName !== "body") { - return; + console.log("Skipping paste because body is not targeted") + return; } } - if ( - event.target !== undefined && - event.target !== null - ) { + console.log("Paste target: ", event?.target) + /* + if (event.target !== undefined && event.target !== null) { if (event.target.localName !== "body") { + console.log("Skipping paste because body is not targeted (2). Target: ", event?.target?.localName) return; } } + */ - - event.preventDefault(); - const clipboard = (event.originalEvent || event).clipboardData.getData( - "text/plain" - ); + // Does this stop things? + //event.preventDefault() + const clipboard = (event.originalEvent || event).clipboardData.getData("text/plain") + console.log("CLIPBOARD TO PASTE: ", clipboard) try { const allnodes = cy.nodes().jsons() @@ -9710,8 +9689,6 @@ const AngularWorkflow = (defaultprops) => { setLeftSideBarOpenByClick(false) localStorage.setItem("expandLeftNav", false) - const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search; - // FIXME: Don't check specific one here const tmpExec = new URLSearchParams(cursearch).get("execution_highlight"); if ( @@ -11553,7 +11530,7 @@ const AngularWorkflow = (defaultprops) => { const positionInfo = document.activeElement.getBoundingClientRect() const outerlistitemStyle = { - width: "100%", + width: "90%", overflowX: "hidden", overflowY: "hidden", borderBottom: "1px solid rgba(255,255,255,0.4)", @@ -15106,28 +15083,6 @@ const AngularWorkflow = (defaultprops) => { Select a workflow to run
- - {workflow.triggers[selectedTriggerIndex].parameters[0].value - .length === 0 ? null : workflow.triggers[selectedTriggerIndex] - .parameters[0].value === props.match.params.key ? - null - : ( -
- - - -
- )}
{workflows === undefined || @@ -15239,13 +15194,36 @@ const AngularWorkflow = (defaultprops) => { }} renderInput={(params) => { return ( - - ); +
+ + {workflow.triggers[selectedTriggerIndex].parameters[0].value + .length === 0 ? null : workflow.triggers[selectedTriggerIndex] + .parameters[0].value === props.match.params.key ? + null + : ( +
+ + + +
+ )} +
+ ) }} /> )} @@ -17465,12 +17443,32 @@ const AngularWorkflow = (defaultprops) => { }} renderInput={(params) => { return ( - +
+ + + {subworkflow === null || subworkflow === undefined || subworkflow?.id === undefined || subworkflow?.id === null || subworkflow?.id.length === 0 ? null : + + + + + + } +
); }} /> @@ -17542,7 +17540,6 @@ const AngularWorkflow = (defaultprops) => { workflow?.triggers[selectedTriggerIndex].parameters[2] && workflow?.triggers[selectedTriggerIndex].parameters[2].value && ( - workflow?.triggers[selectedTriggerIndex].parameters[2].value.includes("email") || workflow?.triggers[selectedTriggerIndex].parameters[2].value.includes("sms") ) ? ( { if (!workflow.public && executionModalOpen) { setExecutionRunning(false); stop() - const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search; const newitem = removeParam("execution_id", cursearch); navigate(curpath + newitem) setExecutionModalView(0); @@ -19359,7 +19355,6 @@ const AngularWorkflow = (defaultprops) => { if (!workflow.public && executionModalOpen) { setExecutionRunning(false); stop() - const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search; const newitem = removeParam("execution_id", cursearch); navigate(curpath + newitem) setExecutionModalView(0); @@ -20424,7 +20419,7 @@ const AngularWorkflow = (defaultprops) => { > @@ -20930,6 +20925,7 @@ const AngularWorkflow = (defaultprops) => { const envStatus = !(executionData.workflow !== undefined && executionData.workflow !== null && executionData.workflow.actions !== undefined && executionData.workflow.actions !== null && executionData.workflow.actions.length > 0) ? "loading" : "success" var executionDelay = -75 + const executionModal = ( { onClose={() => { setExecutionModalOpen(false) - //const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search; //const newitem = removeParam("execution_id", cursearch); //navigate(curpath + newitem) }} @@ -21369,7 +21364,6 @@ const AngularWorkflow = (defaultprops) => {

{ - const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search; const newitem = removeParam("execution_id", cursearch); navigate(curpath + newitem) setExecutionRunning(false); @@ -21397,7 +21391,7 @@ const AngularWorkflow = (defaultprops) => { Rerun workflow. Uses same startnode as the original. Runs from scratch. } - placement="top" + placement="left" style={{ zIndex: 50000 }} > @@ -21752,6 +21746,7 @@ const AngularWorkflow = (defaultprops) => { } /> ) : null} +
{executionData.status !== undefined && @@ -21774,6 +21769,7 @@ const AngularWorkflow = (defaultprops) => { ) : null}
+ { executionData.results === undefined || executionData.results === null || @@ -21794,6 +21790,14 @@ const AngularWorkflow = (defaultprops) => { return null; } + const showRerun = new URLSearchParams(cursearch).get("rerun") + if (showRerun === "true") { + const showNode = new URLSearchParams(cursearch).get("node") + if (data.action.id !== showNode) { + 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); @@ -21873,10 +21877,10 @@ const AngularWorkflow = (defaultprops) => { ); } - if (data.action.app_name === "User Input") { + if (data?.action?.app_name === "User Input" || data?.action?.name === "run_userinput") { actionimg = ( {"Shuffle { } } - const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search; const chosenNodeId = new URLSearchParams(cursearch).get("node"); const highlightNode = chosenNodeId !== null && chosenNodeId !== undefined && chosenNodeId !== "" && chosenNodeId === data.action.id var relevant_errors = [] @@ -22081,7 +22084,7 @@ const AngularWorkflow = (defaultprops) => { color="primary" title={ - Expand result window. Errors: {relevant_errors.length} + Expand debug window. Errors: {relevant_errors.length} } placement="top" @@ -22720,7 +22723,7 @@ const AngularWorkflow = (defaultprops) => { {curapp === null ? null : ( {selectedResult.action.app_name} { // Automatically mapping fields that already exist (predefined). // Warning if fields are NOT filled for (let paramkey in selectedApp.authentication.parameters) { - if ( - authenticationOption.fields[ - selectedApp.authentication.parameters[paramkey].name - ].length === 0 - ) { + if (authenticationOption.fields[selectedApp.authentication.parameters[paramkey].name].length === 0) { + if ( selectedApp.authentication.parameters[paramkey].value !== undefined && selectedApp.authentication.parameters[paramkey].value !== null && @@ -23538,8 +23538,30 @@ const AngularWorkflow = (defaultprops) => { var newAuthOption = JSON.parse(JSON.stringify(authenticationOption)); var newFields = []; + + var warningsent = false for (let authkey in newAuthOption.fields) { - const value = newAuthOption.fields[authkey]; + var value = newAuthOption.fields[authkey]; + + if (value?.toLowerCase().includes("secret. replace")) { + value = "" + + if (authkey === "url") { + // Use default value of the url + const urlparam = selectedApp.authentication.parameters.find((data) => data.name === "url") + if (urlparam !== undefined && urlparam !== null) { + if (urlparam.example !== undefined && urlparam.example !== null && urlparam.example.length > 0) { + value = urlparam.example + } + } + } else { + if (!warningsent) { + warningsent = true + toast("Warning: As you didn't fill in all fields, be aware that the authentication may fail.") + } + } + } + newFields.push({ "key": authkey, "value": value, @@ -23692,18 +23714,18 @@ const AngularWorkflow = (defaultprops) => { }} fullWidth type={ - data.example !== undefined && data.example.includes("***") + data.example !== undefined && data.example.includes("**") ? "password" : "text" } color="primary" defaultValue={ - data.value !== undefined && data.value !== null && !data.value.includes("Secret. Replace") ? data.value : "" + data.value !== undefined && data.value !== null && !data.value.includes("Secret. Replace") ? data.value : + data?.example !== undefined && data?.example !== null && data?.example !== "" && !data?.example.includes("*") ? data.example : "" } placeholder={data.example} onChange={(event) => { - authenticationOption.fields[data.name] = - event.target.value; + authenticationOption.fields[data.name] = event.target.value; }} id={`${data.name}_auth`} /> diff --git a/frontend/src/views/RunWorkflow.jsx b/frontend/src/views/RunWorkflow.jsx index 919d3551..6a9e7442 100644 --- a/frontend/src/views/RunWorkflow.jsx +++ b/frontend/src/views/RunWorkflow.jsx @@ -19,6 +19,7 @@ import RecentWorkflow from "../components/RecentWorkflow.jsx"; import { Tooltip, + Fade, Select, IconButton, CircularProgress, @@ -462,6 +463,8 @@ const RunWorkflow = (defaultprops) => { } else { console.log("Started execution") + start() + setExecutionRunning(true); if (answer !== undefined && answer !== null) { console.log("Skipping start") } else { @@ -1055,7 +1058,7 @@ const RunWorkflow = (defaultprops) => { 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) + setMessage("Answered by " + parsedresult.click_info.user) } } else { @@ -1325,7 +1328,8 @@ const RunWorkflow = (defaultprops) => { })} : - answer !== undefined && answer !== null ? null : + (answer !== undefined && answer !== null) || message !== "" ? null : + Runtime Argument
@@ -1334,7 +1338,13 @@ const RunWorkflow = (defaultprops) => { style={{backgroundColor: theme.palette.inputColor, marginTop: 5, }} multiLine maxRows={2} + type="text" + autoComplete="off" InputProps={{ + autocomplete: "off", + form: { + autocomplete: "off", + }, style:{ height: "50px", color: "white", @@ -1375,9 +1385,11 @@ const RunWorkflow = (defaultprops) => { {message}. You may close this window. : - - {disabledButtons ? "Answered. You may close this window." : ""} - + + + {disabledButtons ? "Answered. You may close this window." : ""} + + } {disabledButtons ? null : @@ -1387,14 +1399,22 @@ const RunWorkflow = (defaultprops) => { }
- + onSubmit(null, execution_id, authorization, true) + }}> + Continue  or  diff --git a/frontend/src/views/Workflows2.jsx b/frontend/src/views/Workflows2.jsx index 45d598ee..e59507f5 100644 --- a/frontend/src/views/Workflows2.jsx +++ b/frontend/src/views/Workflows2.jsx @@ -661,7 +661,7 @@ const Workflows2 = (props) => { const [mouseHoverIndex, setMouseHoverIndex] = useState(-1); const [isLoadingWorkflow, setIsLoadingWorkflow] = useState(false); const [isLoadingPublicWorkflow, setIsLoadingPublicWorkflow] = useState(false); - const [view, setView] = useState("grid"); + const [view, setView] = useState(localStorage?.getItem("workflowView") || "grid"); const classes = useStyles(theme) const imgSize = 60; @@ -2074,55 +2074,6 @@ const Workflows2 = (props) => { }; const hasWorkflows = workflows === undefined || workflows === null || workflows.length === 0 - const NewWorkflowPaper = () => { - const [hover, setHover] = React.useState(false); - - const innerColor = "rgba(255,255,255,0.3)" - - const setupPaperStyle = { - minHeight: paperAppStyle.minHeight, - maxWidth: "100%", - minWidth: paperAppStyle.width, - color: innerColor, - padding: paperAppStyle.padding, - display: "flex", - boxSizing: "border-box", - position: "relative", - border: hasWorkflows ? `2px solid #f85a3e` : `2px solid ${innerColor}`, - cursor: "pointer", - backgroundColor: hover ? "rgba(39,41,45,0.5)" : "rgba(39,41,45,1)", - borderRadius: paperAppStyle.borderRadius, - } - - return ( - - { - setModalOpen(true) - setIsEditing(false) - }} - onMouseOver={() => { - setHover(true); - }} - onMouseOut={() => { - setHover(false); - }} - > - - - - - New Workflow - - - - - - ); - }; - const getWorkflowAppgroup = (data) => { if (currTab !== 2) { if (data.actions === undefined || data.actions === null) { @@ -2517,6 +2468,7 @@ const Workflows2 = (props) => { /> : null} + { to={ type === "public" ? parsedUrl : data.workflow_as_code ? `/workflows/${data.id}/code` : `/workflows/${data.id}` } - style={{ textDecoration: "none", color: "inherit" }} - > + style={{ textDecoration: "none", color: "inherit", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", maxWidth: "90%", display: "block" }} > {parsedName} diff --git a/functions/onprem/orborus/go.mod b/functions/onprem/orborus/go.mod index 0b35fe90..8e581980 100644 --- a/functions/onprem/orborus/go.mod +++ b/functions/onprem/orborus/go.mod @@ -4,7 +4,7 @@ go 1.23.0 toolchain go1.23.6 -//replace github.com/shuffle/shuffle-shared => ../../../../shuffle-shared +replace github.com/shuffle/shuffle-shared => ../../../../shuffle-shared require ( github.com/docker/docker v27.5.0+incompatible diff --git a/functions/onprem/orborus/orborus.go b/functions/onprem/orborus/orborus.go index 18f3b9aa..da9b51d1 100755 --- a/functions/onprem/orborus/orborus.go +++ b/functions/onprem/orborus/orborus.go @@ -787,15 +787,19 @@ func handleBackendImageDownload(ctx context.Context, images string) error { newImages = append(newImages, curimage) // Force remove the current image to avoid cached layers - _, err := dockercli.ImageRemove(ctx, curimage, image.RemoveOptions{ - Force: true, - PruneChildren: true, - }) + if swarmConfig == "run" || swarmConfig == "swarm" { + _, err := dockercli.ImageRemove(ctx, curimage, image.RemoveOptions{ + Force: true, + PruneChildren: true, + }) - if err != nil { - log.Printf("[ERROR] Failed removing image for re-download: %s", err) + if err != nil { + log.Printf("[ERROR] Failed removing image for re-download: %s", err) + } else { + log.Printf("[DEBUG] Removed image: %s", curimage) + } } else { - log.Printf("[DEBUG] Removed image: %s", curimage) + log.Printf("[DEBUG] Skipping image removal for %s as swarmConfig is not set to run or swarm. Value: %#v", curimage, swarmConfig) } err = shuffle.DownloadDockerImageBackend(&http.Client{Timeout: imagedownloadTimeout}, curimage) @@ -2261,9 +2265,6 @@ func main() { log.Printf("[INFO] Re-downloading new image(s): %#v", incRequest.ExecutionArgument) if len(incRequest.ExecutionArgument) > 0 { - // FIXME: Wait X seconds before running this as the image build may not be done yet. This is shitty, but may be ok to do in Orborus. Easy fix for the future: Just let it run through jobs 5-10 times before actually picking it up - - // Run after 25 seconds in the goroutine instead go handleBackendImageDownload(ctx, incRequest.ExecutionArgument) } else { log.Printf("[ERROR] No image name provided for download. Removing job from queue.")