diff --git a/frontend/package.json b/frontend/package.json index 186c50c7..c45a9a9f 100755 --- a/frontend/package.json +++ b/frontend/package.json @@ -59,6 +59,7 @@ "mui-chips-input": "^2.1.3", "mui-nested-menu": "^3.2.1", "react": "^18.2.0", + "react-ace": "^10.1.0", "react-alice-carousel": "^2.6.4", "react-avatar-editor": "^11.1.0", "react-beforeunload": "^2.2.1", diff --git a/frontend/src/codeeditor-index.css b/frontend/src/codeeditor-index.css new file mode 100644 index 00000000..3691b90d --- /dev/null +++ b/frontend/src/codeeditor-index.css @@ -0,0 +1,60 @@ +@import url('https://fonts.googleapis.com/css?family=Nunito+Sans'); + +body { + margin: 0; + padding: 0; + font-family: "Nunito Sans", sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +code { + font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", + monospace; +} + +.partner_container { + height: 200px; + position: relative; +} + +.imggrow { + padding: 60px auto 50px auto; + height: 100px; + width: auto !important; + margin-right: 1rem; + width: 100%; + transition: transform .2s; + border-radius: 10px; + float: center !important; + text-align: center !important; + justify-content: "center" !important; + background-color: #ffffff; + color: black; + position: relative; + overflow: hidden; + z-index: 1; +} + +.imggrow:hover{ + height: 230px; + transform: transform(1.5); + cursor: pointer; + border-color: #3585f9; + display: block; + box-shadow: 0 4px 8px #FC7305; +} + +.bad-marker { + position: absolute; + background-color: #7b322e; + color: white !important; + opacity: 1; +} + +.good-marker { + position: absolute; + background-color: #5ea36a; + color: white !important; + opacity: 0.6; +} diff --git a/frontend/src/components/ShuffleCodeEditor1.jsx b/frontend/src/components/ShuffleCodeEditor1.jsx new file mode 100644 index 00000000..9bec37ad --- /dev/null +++ b/frontend/src/components/ShuffleCodeEditor1.jsx @@ -0,0 +1,1760 @@ +import React, { useRef, useState, useEffect, useLayoutEffect, } from 'react'; +import { toast } from 'react-toastify'; +import '../codeeditor-index.css'; +import { + CircularProgress, + IconButton, + Dialog, + Modal, + Tooltip, + DialogTitle, + DialogContent, + Typography, + Paper, + Menu, + MenuItem, + Button, +} from '@mui/material'; + +import theme from '../theme.jsx'; +import Checkbox from '@mui/material/Checkbox'; +import { isMobile } from "react-device-detect" +import { NestedMenuItem } from "mui-nested-menu" +import { GetParsedPaths, FindJsonPath } from "../views/Apps.jsx"; +import { SetJsonDotnotation } from "../views/AngularWorkflow.jsx"; + +import { + FullscreenExit as FullscreenExitIcon, + Extension as ExtensionIcon, + Apps as AppsIcon, + FavoriteBorder as FavoriteBorderIcon, + Schedule as ScheduleIcon, + FormatListNumbered as FormatListNumberedIcon, + SquareFoot as SquareFootIcon, + Circle as CircleIcon, + Add as AddIcon, + PlayArrow as PlayArrowIcon, + AutoFixHigh as AutoFixHighIcon, + CompressOutlined, + QrCodeScannerOutlined, + + Close as CloseIcon, + DragIndicator as DragIndicatorIcon, +} from '@mui/icons-material'; + + +import { validateJson } from "../views/Workflows.jsx"; +import ReactJson from "react-json-view"; +import PaperComponent from "../components/PaperComponent.jsx"; + +import { padding, textAlign } from '@mui/system'; +import data from '../frameworkStyle.jsx'; +import { useNavigate, Link, useParams } from "react-router-dom"; +import { tags as t } from '@lezer/highlight'; + + +import AceEditor from "react-ace"; +import 'ace-builds/src-noconflict/mode-python'; +import 'ace-builds/src-noconflict/theme-twilight'; +import "ace-builds/src-noconflict/ext-language_tools"; +import ace from "ace-builds"; + +const liquidFilters = [ + {"name": "Default", "value": `default: []`, "example": `{{ "" | default: "no input" }}`}, + {"name": "Split", "value": `split: ","`, "example": `{{ "this,can,become,a,list" | split: "," }}`}, + {"name": "Join", "value": `join: ","`, "example": `{{ ["this","can","become","a","string"] | join: "," }}`}, + {"name": "Size", "value": "size", "example": ""}, + {"name": "Date", "value": `date: "%Y%m%d"`, "example": `{{ "now" | date: "%s" }}`}, + {"name": "Escape String", "value": `{{ \"\"\"'string with weird'" quotes\"\"\" | escape_string }}`, "example": ``}, + {"name": "Flatten", "value": `flatten`, "example": `{{ [1, [1, 2], [2, 3, 4]] | flatten }}`}, + {"name": "URL encode", "value": `url_encode`, "example": `{{ "https://www.google.com/search?q=hello world" | url_encode }}`}, + {"name": "URL decode ", "value": `url_decode`, "example": `{{ "https://www.google.com/search?q=hello%20world" | url_decode }}`}, + {"name": "base64_encode", "value": `base64_encode`, "example": `{{ "https://www.google.com/search?q=hello%20world" | base64_encode }}`}, + {"name": "base64_decode", "value": `base64_decode`, "example": `{{ "aGVsbG8K" | base64_encode }}`}, +] + +const mathFilters = [ + {"name": "Plus", "value": "plus: 1", "example": `{{ "1" | plus: 1 }}`}, + {"name": "Minus", "value": "minus: 1", "example": `{{ "1" | minus: 1 }}`}, +] + +const pythonFilters = [ + {"name": "Hello World", "value": `{% python %}\nprint("hello world")\n{% endpython %}`, "example": ``}, + {"name": "Handle JSON", "value": `{% python %}\nimport json\njsondata = json.loads(r"""$nodename""")\n{% endpython %}`, "example": ``}, +] + +const extensions = [] +const CodeEditor = (props) => { + const { + globalUrl, + fieldCount, + actionlist, + changeActionParameterCodeMirror, + expansionModalOpen, + setExpansionModalOpen, + codedata, + setcodedata, + isFileEditor, + runUpdateText, + toolsAppId, + parameterName, + selectedAction , + workflowExecutions, + getParents, + + fieldname, + } = props + + + + const [localcodedata, setlocalcodedata] = React.useState(codedata === undefined || codedata === null || codedata.length === 0 ? "" : codedata); + + //const { setContainer } = useCodeMirror({ + // container: editorRef.current, + // extensions, + // value: localcodedata, + //}) + // const {codelang, setcodelang} = props + + const [validation, setValidation] = React.useState(false); + const [expOutput, setExpOutput] = React.useState(" "); + const [linewrap, setlinewrap] = React.useState(true); + //const [codeTheme, setcodeTheme] = React.useState("gruvbox-dark"); + const [editorPopupOpen, setEditorPopupOpen] = React.useState(false); + + const [currentCharacter, setCurrentCharacter] = React.useState(-1); + const [currentLine, setCurrentLine] = React.useState(-1); + + const [variableOccurences, setVariableOccurences] = React.useState([]); + const [currentLocation, setCurrentLocation] = React.useState([]); + const [currentVariable, setCurrentVariable] = React.useState(""); + const [anchorEl, setAnchorEl] = React.useState(null); + const [anchorEl2, setAnchorEl2] = React.useState(null); + const [anchorEl3, setAnchorEl3] = React.useState(null); + const [mainVariables, setMainVariables] = React.useState([]); + const [availableVariables, setAvailableVariables] = React.useState([]); + + const [codeTheme, setcodeTheme] = React.useState("gruvbox-dark"); + + const [menuPosition, setMenuPosition] = useState(null); + const [showAutocomplete, setShowAutocomplete] = React.useState(false); + const [markers, setMarkers] = useState([]); + + const [isAiLoading, setIsAiLoading] = React.useState(false); + // let markers = []; + const baseResult = "" + const [executionResult, setExecutionResult] = useState({ + "valid": false, + "result": baseResult, + }) + const [executing, setExecuting] = useState(false) + + const liquidOpen = Boolean(anchorEl); + const mathOpen = Boolean(anchorEl2); + const pythonOpen = Boolean(anchorEl3); + + const handleMenuClose = () => { + setShowAutocomplete(false); + + setMenuPosition(null); + } + + let navigate = useNavigate(); + + useEffect(() => { + var allVariables = [] + var tmpVariables = [] + + if (actionlist === undefined || actionlist === null) { + return + } + + for(var i=0; i < actionlist.length; i++){ + allVariables.push('$'+actionlist[i].autocomplete.toLowerCase()) + tmpVariables.push('$'+actionlist[i].autocomplete.toLowerCase()) + + var parsedPaths = [] + if (typeof actionlist[i].example === "object") { + parsedPaths = GetParsedPaths(actionlist[i].example, ""); + } + + for (var key in parsedPaths) { + const fullpath = "$"+actionlist[i].autocomplete.toLowerCase()+parsedPaths[key].autocomplete + if (!allVariables.includes(fullpath)) { + allVariables.push(fullpath) + } + } + } + + setAvailableVariables(allVariables) + setMainVariables(tmpVariables) + + //console.log("Checking local codedata: ", localcodedata) + expectedOutput(localcodedata) + }, []) + + useEffect(() => { + expectedOutput(localcodedata) + }, [availableVariables]) + + var to_be_copied = ""; + const HandleJsonCopy = (base, copy, base_node_name) => { + if (typeof copy.name === "string") { + copy.name = copy.name.replaceAll(" ", "_"); + } + + //lol + if (typeof base === 'object' || typeof base === 'dict') { + base = JSON.stringify(base) + } + + if (base_node_name === "execution_argument" || base_node_name === "Execution Argument") { + base_node_name = "exec" + } + + console.log("COPY: ", base_node_name, copy); + + //var newitem = JSON.parse(base); + var newitem = validateJson(base).result + to_be_copied = "$" + base_node_name.toLowerCase().replaceAll(" ", "_"); + for (let copykey in copy.namespace) { + if (copy.namespace[copykey].includes("Results for")) { + continue; + } + + if (newitem !== undefined && newitem !== null) { + newitem = newitem[copy.namespace[copykey]]; + if (!isNaN(copy.namespace[copykey])) { + to_be_copied += ".#"; + } else { + to_be_copied += "." + copy.namespace[copykey]; + } + } + } + + if (newitem !== undefined && newitem !== null) { + newitem = newitem[copy.name]; + if (!isNaN(copy.name)) { + to_be_copied += ".#"; + } else { + to_be_copied += "." + copy.name; + } + } + + to_be_copied.replaceAll(" ", "_"); + const elementName = "copy_element_shuffle"; + var copyText = document.getElementById(elementName); + if (copyText !== null && copyText !== undefined) { + console.log("NAVIGATOR: ", navigator); + const clipboard = navigator.clipboard; + if (clipboard === undefined) { + toast("Can only copy over HTTPS (port 3443)"); + return; + } + + navigator.clipboard.writeText(to_be_copied); + copyText.select(); + copyText.setSelectionRange(0, 99999); /* For mobile devices */ + + /* Copy the text inside the text field */ + document.execCommand("copy"); + console.log("COPYING!"); + toast("Copied JSON path to clipboard.") + } else { + console.log("Couldn't find element ", elementName); + } + } + + const aiSubmit = (value, inputAction) => { + if (value === undefined || value === "") { + console.log("No value input!") + return + } + + setIsAiLoading(true) + + // Time to construct this huh... Hmm + var AppContext = [] + if (inputAction !== undefined && inputAction !== null && getParents !== undefined && getParents !== null && workflowExecutions !== undefined && workflowExecutions !== null) { + const parents = getParents(inputAction) + + console.log("Parents: ", parents) + var actionlist = [] + if (parents.length > 1) { + for (let [key,keyval] in Object.entries(parents)) { + const item = parents[key]; + if (item.label === "Execution Argument") { + continue; + } + + var exampledata = item.example === undefined || item.example === null ? "" : item.example; + // Find previous execution and their variables + //exampledata === "" && + if (workflowExecutions.length > 0) { + // Look for the ID + const found = false; + for (let [key,keyval] in Object.entries(workflowExecutions)) { + if (workflowExecutions[key].results === undefined || workflowExecutions[key].results === null) { + continue; + } + + var foundResult = workflowExecutions[key].results.find((result) => result.action.id === item.id); + if (foundResult === undefined || foundResult === null) { + continue; + } + + if (foundResult.result !== undefined && foundResult.result !== null) { + foundResult = foundResult.result + } + + const valid = validateJson(foundResult, true) + if (valid.valid) { + if (valid.result.success === false) { + //console.log("Skipping success false autocomplete") + } else { + exampledata = valid.result; + break; + } + } else { + exampledata = foundResult; + } + } + } + + // 1. Take + const itemlabelComplete = item.label === null || item.label === undefined ? "" : item.label.split(" ").join("_"); + + const actionvalue = { + app_name: item.app_name, + action_name: item.name, + label: item.label, + + type: "action", + id: item.id, + name: item.label, + autocomplete: itemlabelComplete, + example: exampledata, + }; + + actionlist.push(actionvalue); + } + } + + var fixedResults = [] + for (var i = 0; i < actionlist.length; i++) { + const item = actionlist[i]; + const responseFix = SetJsonDotnotation(item.example, "") + + // Check if json + const validated = validateJson(responseFix) + var exampledata = responseFix; + if (validated.valid) { + exampledata = JSON.stringify(validated.result) + } + + AppContext.push({ + "app_name": item.app_name, + "action_name": item.action_name, + "label": item.label, + "example": exampledata, + }) + } + } + + var conversationData = { + "query": value, + "output_format": "action", + "app_context": AppContext, + } + + if (inputAction !== undefined) { + console.log("Add app context! This should them get parameters directly") + conversationData.output_format = "action_parameters" + + conversationData.app_id = inputAction.app_id + conversationData.app_name = inputAction.app_name + conversationData.action_name = inputAction.name + conversationData.parameters = inputAction.parameters + } + + fetch(`${globalUrl}/api/v1/conversation`, { + method: "POST", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + body: JSON.stringify(conversationData), + credentials: "include", + }) + .then((response) => { + if (response.status !== 200) { + console.log("Status not 200 for stream results :O!"); + } + + return response.json(); + }) + .then((responseJson) => { + console.log("Conversation response: ", responseJson) + setIsAiLoading(false) + if (responseJson.success === false) { + if (responseJson.reason !== undefined) { + } + + return + } + + if (inputAction !== undefined) { + console.log("In input action! Should check params if they match, and add suggestions") + + if (responseJson.parameters === undefined || responseJson.parameters.length === 0) { + return + } + + for (let respParam of responseJson.parameters) { + if (respParam.name !== parameterName) { + continue + } + + if (respParam.value === "") { + break + } + + setlocalcodedata(respParam.value) + break + } + + return + } + }) + .catch((error) => { + setIsAiLoading(false) + console.log("Conv response error: ", error); + }); + } + + const autoFormat = (input) => { + // Check if it's default too + if (validation !== true) { + + // Should try to automatically fix this input + console.log("Running AI input fixer") + if (aiSubmit !== undefined && parameterName !== undefined && selectedAction !== undefined) { + + // Should remove params from selectedAction that aren't parameterName + var tmpAction = JSON.parse(JSON.stringify(selectedAction)) + var tmpParams = selectedAction.parameters.filter((param) => param.name === parameterName) + + var aiMsg = `Make it valid for action ${tmpAction.label} with parameter ${parameterName}: ` + if (tmpParams.length > 0) { + aiMsg += tmpParams[0].value + } + + + if (localcodedata.startsWith("//")) { + aiMsg = localcodedata + } + + tmpAction.parameters = tmpParams + console.log("Parameters: ", tmpParams.length) + + aiSubmit(aiMsg, tmpAction) + } + + return + } + + try { + input = JSON.stringify(JSON.parse(input), null, 4) + } catch (e) { + console.log("Failed magic JSON stringification: ", e) + } + + if (input !== localcodedata) { + setlocalcodedata(input) + } + } + + const findIndex = (line, loc) => { + var code_line = localcodedata.split('\n')[line] + if (code_line === undefined) { + return + } + + var dollar_occurences = [] + var dollar_occurences_len = [] + var variable_ranges = [] + var popup = false + + for(var ch=0; ch < code_line.length; ch++){ + if(code_line[ch] === '$'){ + dollar_occurences.push(ch) + } + } + + var variable_occurences = code_line.match(/[$]{1}([a-zA-Z0-9_-]+\.?){1}([a-zA-Z0-9#_-]+\.?){0,}/g) + + try{ + for(var occ = 0; occ < variable_occurences.length; occ++){ + dollar_occurences_len.push(variable_occurences[occ].length) + } + } catch (e) {} + + for(var occ = 0; occ < dollar_occurences.length; occ++){ + // var temp_arr = [] + // for(var occ_len = 0; occ_len { + if (inputvariable === undefined || inputvariable === null) { + return inputvariable + } + + if (!inputvariable.includes(".")) { + return inputvariable + } + + const itemsplit = inputvariable.split(".") + var newitem = [] + var removedIndexes = 0 + for (var key in itemsplit) { + var tmpitem = itemsplit[key] + + // Makes sure #0 and # are same, as we only visualize first one anyway + if (tmpitem.startsWith("#")) { + removedIndexes += tmpitem.length-1 + tmpitem = "#" + } + + newitem.push(tmpitem) + } + + return newitem.join(".") + //return inputvariable + } + + const highlight_variables = (value) => { + // var session = localcodedata.getSession(); + var code_lines = localcodedata.split('\n'); + + const newMarkers = []; + for (var i = 0; i < code_lines.length; i++) { + var current_code_line = code_lines[i]; + var variable_occurence = current_code_line.match(/[\\]{0,1}[$]{1}([a-zA-Z0-9_-]+\.?){1}([a-zA-Z0-9#_-]+\.?){0,}/g); + + if (!variable_occurence) { + continue; + } + + var new_occurences = variable_occurence.filter((occurrence) => occurrence[0]); + variable_occurence = new_occurences; + + var dollar_occurence = []; + for (let ch = 0; ch < current_code_line.length; ch++) { + if (current_code_line[ch] === '$' && (ch === 0)) { + dollar_occurence.push(ch); + } + } + + var dollar_occurence_len = [] + try{ + for(let occ = 0; occ < variable_occurence.length; occ++){ + dollar_occurence_len.push(variable_occurence[occ].length) + } + } catch (e) {} + + try { + if (variable_occurence.length === 0) { + //value.markText({line:i, ch:0}, {line:i, ch:code_lines[i].length-1}, {"css": "background-color: #282828; border-radius: 0px; color: #b8bb26"}) + //value.markText({line:i, ch:0}, {line:i, ch:code_lines[i].length-1}, {"css": "background-color: #; border-radius: 0px; color: inherit"}) + } + for (let occ = 0; occ < variable_occurence.length; occ++) { + const fixedVariable = fixVariable(variable_occurence[occ]) + var correctVariable = availableVariables.includes(fixedVariable) + var startCh = dollar_occurence[occ] + var endCh = dollar_occurence[occ] + dollar_occurence_len[occ] + newMarkers.push({ + startRow: i, + startCol: startCh, + endRow: i, + endCol: endCh, + className: correctVariable ? "good-marker" : "bad-marker", + type: "text", + }) + } + + setMarkers(newMarkers) + } catch (e) { + console.log("Error in color highlighting: ", e); + } + } + }; + + const replaceVariables = (swapVariable) => { + // var updatedCode = localcodedata.slice(0,index) + "$" + str + localcodedata.slice(index+currentVariable.length+1,) + // setlocalcodedata(updatedCode) + // setEditorPopupOpen(false) + // setCurrentLocation(0) + // console.log(index) + // console.log(currentLocation) + + var code_lines = localcodedata.split('\n') + var parsedVariable = currentVariable + if (currentVariable === undefined || currentVariable === null) { + console.log("Location: ", currentLocation) + parsedVariable= "$" + } + + code_lines[currentLine] = code_lines[currentLine].slice(0,currentLocation[1]) + "$" + swapVariable + code_lines[currentLine].slice(currentLocation[1]+parsedVariable.length,) + // console.log(code_lines) + var updatedCode = code_lines.join('\n') + // console.log(updatedCode) + setlocalcodedata(updatedCode) + } + + const fixStringInput = (new_input) => { + // Newline fixes + new_input = new_input.replace(/\r\n/g, "\\n") + new_input = new_input.replace(/\n/g, "\\n") + + // Quote fixes + new_input = new_input.replace(/\\"/g, '"') + new_input = new_input.replace(/"/g, '\\"') + + new_input = new_input.replace(/\\'/g, "'") + new_input = new_input.replace(/'/g, "\\'") + + + return new_input + } + + + const expectedOutput = (input) => { + //const found = input.match(/[$]{1}([a-zA-Z0-9_-]+\.?){1}([a-zA-Z0-9#_-]+\.?){0,}/g) + const found = input.match(/[$]{1}([a-zA-Z0-9_-]+\.?){1}([a-zA-Z0-9#_-]+\.?){0,}/g) + + // Whelp this is inefficient af. Single loop pls + // When the found array is empty. + if (found !== null && found !== undefined) { + try { + for (var i = 0; i < found.length; i++) { + try { + const fixedVariable = fixVariable(found[i]) + var valuefound = false + for (var j = 0; j < actionlist.length; j++) { + if(fixedVariable.slice(1,).toLowerCase() !== actionlist[j].autocomplete.toLowerCase()){ + continue + } + + valuefound = true + try { + if (typeof actionlist[j].example === "object") { + + input = input.replace(found[i], JSON.stringify(actionlist[j].example), -1); + + } else if (actionlist[j].example.trim().startsWith("{") || actionlist[j].example.trim().startsWith("[")) { + input = input.replace(found[i], JSON.stringify(actionlist[j].example), -1); + } else { + const newExample = fixStringInput(actionlist[j].example) + input = input.replace(found[i], newExample, -1) + } + } catch (e) { + input = input.replace(found[i], actionlist[j].example, -1) + } + } + + + //if (!valuefound) { + // console.log("Couldn't find value "+fixedVariable) + //} + + if (!valuefound && availableVariables.includes(fixedVariable)) { + var shouldbreak = false + for (var k=0; k < actionlist.length; k++){ + var parsedPaths = [] + if (typeof actionlist[k].example === "object") { + parsedPaths = GetParsedPaths(actionlist[k].example, ""); + } + + for (var key in parsedPaths) { + const fullpath = "$"+actionlist[k].autocomplete.toLowerCase()+parsedPaths[key].autocomplete + if (fullpath !== fixedVariable) { + continue + } + + //if (actionlist[k].example === undefined) { + // actionlist[k].example = "TMP" + //} + + var new_input = "" + try { + new_input = FindJsonPath(fullpath, actionlist[k].example) + } catch (e) { + console.log("ERR IN INPUT: ", e) + } + + //console.log("Got output for: ", fullpath, new_input, actionlist[k].example, typeof new_input) + + if (typeof new_input === "object") { + new_input = JSON.stringify(new_input) + } else { + if (typeof new_input === "string") { + // Check if it contains any newlines, and replace them with raw newlines + new_input = fixStringInput(new_input) + + // Replace quotes with nothing + } else { + console.log("NO TYPE? ", typeof new_input) + try { + new_input = new_input.toString() + } catch (e) { + new_input = "" + } + } + } + + input = input.replace(fixedVariable, new_input, -1) + input = input.replace(found[i], new_input, -1) + + //} catch (e) { + // input = input.replace(found[i], actionlist[k].example) + //} + + shouldbreak = true + break + } + + if (shouldbreak) { + break + } + } + } + } catch (e) { + console.log("Replace error: ", e) + } + } + } catch (e) { + console.log("Outer replace error: ", e) + } + } + + const tmpValidation = validateJson(input.valueOf()) + //setValidation(true) + if (tmpValidation.valid === true) { + setValidation(true) + setExpOutput(tmpValidation.result) + } else { + setExpOutput(input.valueOf()) + setValidation(false) + } + } + + const handleItemClick = (values) => { + if ( + values === undefined || + values === null || + values.length === 0 + ) { + return; + } + + var toComplete = localcodedata.trim().endsWith("$") ? values[0].autocomplete : "$" + values[0].autocomplete; + + toComplete = toComplete.toLowerCase().replaceAll(" ", "_"); + for (var key in values) { + if (key == 0 || values[key].autocomplete.length === 0) { + continue; + } + + toComplete += values[key].autocomplete; + } + + setlocalcodedata(localcodedata+toComplete) + setMenuPosition(null) + } + + const handleClick = (item) => { + if (item === undefined || item.value === undefined || item.value === null) { + return + } + + if (!item.value.includes("{%") && !item.value.includes("{{")) { + setlocalcodedata(localcodedata+" | "+item.value+" }}") + } else { + setlocalcodedata(localcodedata+item.value) + } + + setAnchorEl(null) + setAnchorEl2(null) + setAnchorEl3(null) + } + + const executeSingleAction = (inputdata) => { + if (validation === true) { + inputdata = JSON.stringify(inputdata) + } + + // Shuffle Tools 1.2.0 (in most cases?) + const appid = toolsAppId !== undefined && toolsAppId !== null && toolsAppId.length > 0 ? toolsAppId : "3e2bdf9d5069fe3f4746c29d68785a6a" + + const actionname = selectedAction.name === "execute_python" && !inputdata.replaceAll(" ", "").includes("{%python%}") ? "execute_python" : "repeat_back_to_me" + const params = actionname === "execute_python" ? [{"name": "code", "value":inputdata}] : [{"name":"call", "value": inputdata}] + + const actiondata = {"description":"Repeats the call parameter","id":"","name":actionname,"label":"","node_type":"","environment":"","sharing":false,"private_id":"","public_id":"","app_id": appid,"tags":null,"authentication":[],"tested":false,"parameters": params, "execution_variable":{"description":"","id":"","name":"","value":""},"returns":{"description":"","example":"","id":"","schema":{"type":"string"}},"authentication_id":"","example":"","auth_not_required":false,"source_workflow":"","run_magic_output":false,"run_magic_input":false,"execution_delay":0,"app_name":"Shuffle Tools","app_version":"1.2.0","selectedAuthentication":{}} + + setExecutionResult({ + "valid": false, + "result": baseResult, + "errors": [], + }) + + setExecuting(true) + + fetch(`${globalUrl}/api/v1/apps/${appid}/execute`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + }, + body: JSON.stringify(actiondata), + credentials: "include", + }) + .then((response) => { + if (response.status !== 200) { + console.log("Status not 200 for stream results :O!") + } + + return response.json() + }) + .then((responseJson) => { + //console.log("RESPONSE: ", responseJson) + var newResult = {} + if (responseJson.success === true && responseJson.result !== null && responseJson.result !== undefined && responseJson.result.length > 0) { + const result = responseJson.result.slice(0, 50)+"..." + //toast("SUCCESS: "+result) + + const validate = validateJson(responseJson.result) + newResult = validate + } else if (responseJson.success === false && responseJson.reason !== undefined && responseJson.reason !== null) { + toast(responseJson.reason) + newResult = {"valid": false, "result": responseJson.reason} + } else if (responseJson.success === true) { + newResult = {"valid": false, "result": "Couldn't finish execution. Please fill all the required fields, and retry the execution."} + } else { + newResult = {"valid": false, "result": "Couldn't finish execution (2). Please fill all the required fields, and validate the execution."} + } + + if (responseJson.errors !== undefined && responseJson.errors !== null && responseJson.errors.length > 0) { + newResult.errors = responseJson.errors + } + + setExecutionResult(newResult) + setExecuting(false) + }) + .catch(error => { + //toast("Execution error: "+error.toString()) + console.log("error: ", error) + setExecuting(false) + }) + } + + const adjustPosition = (menuPosition) => { + const { top, left, width, height } = menuPosition; + const windowWidth = window.innerWidth; + const windowHeight = window.innerHeight; + + let adjustedTop = top; + let adjustedLeft = left; + + // Adjust top position if menu overflows the bottom of the window + if (top + height > windowHeight) { + adjustedTop = windowHeight - height; + } + + // Adjust left position if menu overflows the right side of the window + if (left + width > windowWidth) { + adjustedLeft = windowWidth - width; + } + + return { + "top": adjustedTop, + "left": adjustedLeft + } + }; + + + + return ( + { + console.log("In closer") + + if (changeActionParameterCodeMirror !== undefined) { + changeActionParameterCodeMirror({target: {value: ""}}, fieldCount, localcodedata) + } else { + console.log("No action called changeActionParameterCodeMirror in code editor") + } + //setExpansionModalOpen(false) + }} + PaperComponent={PaperComponent} + PaperProps={{ + style: { + zIndex: 12501, + color: "white", + minWidth: isMobile ? "100%" : isFileEditor ? 650 : "80%", + maxWidth: isMobile ? "100%" : isFileEditor ? 650 : 1100, + minHeight: isMobile ? "100%" : "auto", + maxHeight: isMobile ? "100%" : 700, + border: theme.palette.defaultBorder, + padding: isMobile ? "25px 10px 25px 10px" : 25, + }, + }} + > + + { + }} + > + + + + + { + setExpansionModalOpen(false) + }} + > + + + +
+
+ { isFileEditor ? +
+
+ + File Editor + +
+
+ : +
+
+ {/* + + Code Editor + + */} + { isFileEditor ? null : +
+ {selectedAction.name === "execute_python" ? + + Run Python Code + + : +
+ + { + setAnchorEl(null); + }} + MenuListProps={{ + 'aria-labelledby': 'basic-button', + }} + > + {liquidFilters.map((item, index) => { + return ( + { + handleClick(item) + }}>{item.name} + ) + })} + + + { + setAnchorEl2(null); + }} + MenuListProps={{ + 'aria-labelledby': 'basic-button', + }} + > + {mathFilters.map((item, index) => { + return ( + { + handleClick(item) + }}>{item.name} + ) + })} + + + { + setAnchorEl3(null); + }} + MenuListProps={{ + 'aria-labelledby': 'basic-button', + }} + > + {pythonFilters.map((item, index) => { + return ( + { + handleClick(item) + }}>{item.name} + ) + })} + +
+ } + + { + handleMenuClose(); + }} + open={!!menuPosition} + style={{ + color: "white", + marginTop: 2, + maxHeight: 650, + }} + > + {actionlist.map((innerdata) => { + const icon = + innerdata.type === "action" ? ( + + ) : innerdata.type === "workflow_variable" || + innerdata.type === "execution_variable" ? ( + + ) : ( + + ); + + const handleExecArgumentHover = (inside) => { + var exec_text_field = document.getElementById( + "execution_argument_input_field" + ); + if (exec_text_field !== null) { + if (inside) { + exec_text_field.style.border = "2px solid #f85a3e"; + } else { + exec_text_field.style.border = ""; + } + } + }; + + const handleActionHover = (inside, actionId) => { + }; + + const handleMouseover = () => { + if (innerdata.type === "Execution Argument") { + handleExecArgumentHover(true); + } else if (innerdata.type === "action") { + handleActionHover(true, innerdata.id); + } + }; + + const handleMouseOut = () => { + if (innerdata.type === "Execution Argument") { + handleExecArgumentHover(false); + } else if (innerdata.type === "action") { + handleActionHover(false, innerdata.id); + } + }; + + var parsedPaths = []; + if (typeof innerdata.example === "object") { + parsedPaths = GetParsedPaths(innerdata.example, ""); + } + + const coverColor = "#82ccc3" + //menuPosition.left -= 50 + //menuPosition.top -= 250 + //console.log("POS: ", menuPosition1) + var menuPosition1 = menuPosition + if (menuPosition1 === null) { + menuPosition1 = { + "left": 0, + "top": 0, + } + } else if (menuPosition1.top === null || menuPosition1.top === undefined) { + menuPosition1.top = 0 + } else if (menuPosition1.left === null || menuPosition1.left === undefined) { + menuPosition1.left = 0 + } + + //console.log("POS1: ", menuPosition1) + + return parsedPaths.length > 0 ? ( + + {icon} {innerdata.name} +
+ } + parentMenuOpen={!!menuPosition} + style={{ + color: "white", + minWidth: 250, + maxWidth: 250, + maxHeight: 50, + overflow: "hidden", + }} + onClick={() => { + console.log("CLICKED: ", innerdata); + console.log(innerdata.example) + handleItemClick([innerdata]); + }} + > + + { + //console.log("HOVER: ", pathdata); + }} + onClick={() => { + handleItemClick([innerdata]); + }} + > + + {innerdata.name} + + + {parsedPaths.map((pathdata, index) => { + // FIXME: Should be recursive in here + // + const icon = + pathdata.type === "value" ? ( + + ) : pathdata.type === "list" ? ( + + ) : ( + + ); + // + + const indentation_count = (pathdata.name.match(/\./g) || []).length+1 + //const boxPadding = pathdata.type === "object" ? "10px 0px 0px 0px" : 0 + const boxPadding = 0 + const namesplit = pathdata.name.split(".") + const newname = namesplit[namesplit.length-1] + return ( + { + //console.log("HOVER: ", pathdata); + }} + onClick={() => { + handleItemClick([innerdata, pathdata]); + }} + > + +
+ {Array(indentation_count).fill().map((subdata, subindex) => { + return ( +
+ ) + })} + {icon} {newname} + {pathdata.type === "list" ? { + e.preventDefault() + e.stopPropagation() + + console.log("INNER: ", innerdata, pathdata) + + // Removing .list from autocomplete + var newname = pathdata.name + if (newname.length > 5) { + newname = newname.slice(0, newname.length-5) + } + + //selectedActionParameters[count].value += `{{ $${innerdata.name}.${newname} | size }}` + //selectedAction.parameters[count].value = selectedActionParameters[count].value; + //setSelectedAction(selectedAction); + //setShowDropdown(false); + setMenuPosition(null); + + // innerdata.name + // pathdata.name + //handleItemClick([innerdata, newpathdata]) + //console.log("CLICK LENGTH!") + }} /> : null} +
+ + + ); + })} + + + ) : ( + handleMouseover()} + onMouseOut={() => { + handleMouseOut(); + }} + onClick={() => { + handleItemClick([innerdata]); + }} + > + +
+ {icon} {innerdata.name} +
+
+
+ ); + })} + +
+ } + { + + }} + > + + + + + + + { + autoFormat(localcodedata) + }} + > + + {isAiLoading ? + + : + + } + + +
+
+ } + + {/* +
+ */} + +
+ { + console.log("LOAD: ", editor) + highlight_variables(localcodedata) + }} + onCursorChange={(cursorPosition, editor, value) => { + setCurrentCharacter(cursorPosition.column) + setCurrentLine(cursorPosition.row) + findIndex(cursorPosition.row, cursorPosition.column) + highlight_variables(value) + }} + onChange={(value, editor) => { + // setlocalcodedata(value) + // expectedOutput(value) + // highlight_variables(value,editor) + setlocalcodedata(value) + expectedOutput(value) + highlight_variables(value) + }} + setOptions={{ + enableBasicAutocompletion: true, + enableLiveAutocompletion: true, + enableSnippets: true, + useWorker: false + }} + // options={options} + /> +
+ +
+
+
+ + {isFileEditor ? null : +
+
+ {isMobile ? null : + +
+ + Expected Output + +
+ +
+ } + +
+ + + + + {isMobile ? null : + validation === true ? + { + //handleReactJsonClipboard(copy); + }} + displayDataTypes={false} + onSelect={(select) => { + var basename = "exec" + if (selectedAction !== undefined && selectedAction !== null && Object.keys(selectedAction).length !== 0) { + basename = selectedAction.label.toLowerCase().replaceAll(" ", "_") + } + + HandleJsonCopy(expOutput, select, basename) + }} + name={"JSON autocompletion"} + /> + : +

+ {expOutput} +

+ } +
+ + {executionResult.valid === true ? + { + //handleReactJsonClipboard(copy); + }} + displayDataTypes={false} + onSelect={(select) => { + //HandleJsonCopy(executionResult.result, select, "exec"); + }} + name={"Test result"} + /> + : + + {executionResult.result.length > 0 ? + + + Test output + + + {executionResult.result} + + + : + +
+ + Output is based on the last VALID run of the node(s) you are referencing. Only updates when you refresh the Workflow Window. + + + No test output yet. + +
+ } + {executionResult.errors !== undefined && executionResult.errors !== null && executionResult.errors.length > 0 ? + + Errors ({executionResult.errors.length}): {executionResult.errors.join("\n")} + + : null} +
+ } +
+ +
+ } +
+ + +
+ + +
+
) +} + +export default CodeEditor; diff --git a/frontend/src/views/HandlePaymentNew.jsx b/frontend/src/views/HandlePaymentNew.jsx index 89546b9b..4be0714c 100644 --- a/frontend/src/views/HandlePaymentNew.jsx +++ b/frontend/src/views/HandlePaymentNew.jsx @@ -3,6 +3,7 @@ import React, { useState, useEffect } from 'react'; import ReactGA from 'react-ga4'; import { useNavigate, Link } from "react-router-dom"; import {isMobile} from "react-device-detect"; +import { toast } from "react-toastify" import { Done as DoneIcon, @@ -36,6 +37,24 @@ import Services from "./Services.jsx"; export const typecost = 0.0018 export const typecost_single = (typecost * 1.33).toFixed(4) +export const handlePayasyougo = (userdata) => { + var billingurl = "https://billing.stripe.com/p/login/bIY5lo5bMbWs9Py5kk" + + if (userdata !== undefined && userdata !== null) { + if (userdata.org_email !== undefined && userdata.org_email !== null && userdata.org_email !== "") { + billingurl += `?prefilled_email=${userdata.org_email}` + + } else if (userdata.username !== undefined && userdata.username !== null && userdata.username !== "") { + billingurl += `?prefilled_email=${userdata.username}` + } + } + + toast("Redirecting in 2 seconds. Use the organization owner email.") + setTimeout(() => { + window.location = billingurl + }, 2500) +} + // 1. Create 2-3 payment tiers (slider?) // 2. Create a way to show them anywhere //