4478 lines
147 KiB
React
Executable File
4478 lines
147 KiB
React
Executable File
import React, { useState, useEffect, useLayoutEffect, useMemo } from "react";
|
|
import { toast } from 'react-toastify';
|
|
import { makeStyles, createStyles } from "@mui/styles";
|
|
import theme from '../theme.jsx';
|
|
|
|
import { useNavigate, Link, useParams } from "react-router-dom";
|
|
import { validateJson, GetIconInfo } from "../views/Workflows.jsx";
|
|
import { GetParsedPaths } from "../views/Apps.jsx";
|
|
import { sortByKey } from "../views/AngularWorkflow.jsx";
|
|
import { NestedMenuItem } from "mui-nested-menu";
|
|
import { parsedDatatypeImages } from "../components/AppFramework.jsx";
|
|
import { green, yellow, red } from "../views/AngularWorkflow.jsx"
|
|
//import { useAlert
|
|
|
|
import {
|
|
Chip,
|
|
ButtonGroup,
|
|
Popper,
|
|
TextField,
|
|
TextareaAutosize,
|
|
Drawer,
|
|
Button,
|
|
Paper,
|
|
Tabs,
|
|
InputAdornment,
|
|
Tab,
|
|
ButtonBase,
|
|
Tooltip,
|
|
Select,
|
|
MenuItem,
|
|
Divider,
|
|
Dialog,
|
|
Modal,
|
|
DialogActions,
|
|
DialogTitle,
|
|
InputLabel,
|
|
DialogContent,
|
|
FormControl,
|
|
IconButton,
|
|
Menu,
|
|
Input,
|
|
FormGroup,
|
|
FormControlLabel,
|
|
Typography,
|
|
Checkbox,
|
|
Breadcrumbs,
|
|
CircularProgress,
|
|
Switch,
|
|
Collapse,
|
|
Autocomplete,
|
|
Box
|
|
} from "@mui/material";
|
|
|
|
import {
|
|
HelpOutline as HelpOutlineIcon,
|
|
OpenInFull as OpenInFullIcon,
|
|
Description as DescriptionIcon,
|
|
GetApp as GetAppIcon,
|
|
Search as SearchIcon,
|
|
ArrowUpward as ArrowUpwardIcon,
|
|
Visibility as VisibilityIcon,
|
|
Done as DoneIcon,
|
|
Close as CloseIcon,
|
|
Error as ErrorIcon,
|
|
FindReplace as FindreplaceIcon,
|
|
ArrowLeft as ArrowLeftIcon,
|
|
Cached as CachedIcon,
|
|
DirectionsRun as DirectionsRunIcon,
|
|
Add as AddIcon,
|
|
Polymer as PolymerIcon,
|
|
FormatListNumbered as FormatListNumberedIcon,
|
|
Create as CreateIcon,
|
|
PlayArrow as PlayArrowIcon,
|
|
AspectRatio as AspectRatioIcon,
|
|
MoreVert as MoreVertIcon,
|
|
Apps as AppsIcon,
|
|
Schedule as ScheduleIcon,
|
|
FavoriteBorder as FavoriteBorderIcon,
|
|
Pause as PauseIcon,
|
|
Delete as DeleteIcon,
|
|
AddCircleOutline as AddCircleOutlineIcon,
|
|
Save as SaveIcon,
|
|
KeyboardArrowLeft as KeyboardArrowLeftIcon,
|
|
KeyboardArrowRight as KeyboardArrowRightIcon,
|
|
ArrowBack as ArrowBackIcon,
|
|
Settings as SettingsIcon,
|
|
LockOpen as LockOpenIcon,
|
|
ExpandMore as ExpandMoreIcon,
|
|
VpnKey as VpnKeyIcon,
|
|
AutoFixHigh as AutoFixHighIcon,
|
|
Circle as CircleIcon,
|
|
SquareFoot as SquareFootIcon,
|
|
Storage as StorageIcon,
|
|
Check as CheckIcon,
|
|
} from '@mui/icons-material';
|
|
|
|
export const useStyles = makeStyles({
|
|
root: {
|
|
"& .MuiAutocomplete-listbox": {
|
|
border: "2px solid grey",
|
|
color: "white",
|
|
fontSize: 18,
|
|
"& li:nth-child(even)": {
|
|
backgroundColor: "#CCC",
|
|
},
|
|
"& li:nth-child(odd)": {
|
|
backgroundColor: "#FFF",
|
|
},
|
|
},
|
|
},
|
|
inputRoot: {
|
|
color: "white",
|
|
"&:hover .MuiOutlinedInput-notchedOutline": {
|
|
borderColor: "#f86a3e",
|
|
},
|
|
},
|
|
});
|
|
|
|
const openApiFieldDesc = "Generated by OpenAPI body example";
|
|
const ParsedAction = (props) => {
|
|
const {
|
|
workflow,
|
|
files,
|
|
setWorkflow,
|
|
setAction,
|
|
setSelectedAction,
|
|
setUpdate,
|
|
appActionArguments,
|
|
selectedApp,
|
|
workflowExecutions,
|
|
setSelectedResult,
|
|
selectedAction,
|
|
setSelectedApp,
|
|
setSelectedTrigger,
|
|
setSelectedEdge,
|
|
setCurrentView,
|
|
cy,
|
|
setAuthenticationModalOpen,
|
|
setVariablesModalOpen,
|
|
setCodeModalOpen,
|
|
selectedNameChange,
|
|
rightsidebarStyle,
|
|
showEnvironment,
|
|
selectedActionEnvironment,
|
|
environments,
|
|
setNewSelectedAction,
|
|
appApiViewStyle,
|
|
globalUrl,
|
|
setSelectedActionEnvironment,
|
|
requiresAuthentication,
|
|
hideExtraTypes,
|
|
scrollConfig,
|
|
setScrollConfig,
|
|
authenticationType,
|
|
appAuthentication,
|
|
getAppAuthentication,
|
|
actionDelayChange,
|
|
getParents,
|
|
isCloud,
|
|
lastSaved,
|
|
setLastSaved,
|
|
setShowVideo,
|
|
toolsAppId,
|
|
aiSubmit,
|
|
|
|
expansionModalOpen,
|
|
setExpansionModalOpen,
|
|
|
|
listCache,
|
|
setActiveDialog,
|
|
authGroups,
|
|
apps,
|
|
setEditorData,
|
|
setcodedata,
|
|
setAiQueryModalOpen,
|
|
} = props;
|
|
|
|
let navigate = useNavigate();
|
|
const classes = useStyles();
|
|
|
|
const [hideBody, setHideBody] = React.useState(false)
|
|
const [activateHidingBodyButton, setActivateHidingBodyButton] = React.useState(false)
|
|
const [appActionName, setAppActionName] = React.useState(selectedAction?.label);
|
|
const [delay, setDelay] = React.useState(selectedAction?.execution_delay || 0);
|
|
const [prevActionName, setPrevActionName] = React.useState(selectedAction?.label);
|
|
const [fieldCount, setFieldCount] = React.useState(0);
|
|
const [hiddenDescription, setHiddenDescription] = React.useState(true);
|
|
const [hiddenParameters, setHiddenParameters] = React.useState(true);
|
|
const [autoCompleting, setAutocompleting] = React.useState(false);
|
|
const [selectedActionParameters, setSelectedActionParameters] = React.useState(selectedAction?.parameters || []);
|
|
const [selectedVariableParameter, setSelectedVariableParameter] = React.useState("");
|
|
const [paramUpdate, setParamUpdate] = React.useState("");
|
|
const [actionlist, setActionlist] = React.useState([]);
|
|
const [jsonList, setJsonList] = React.useState([]);
|
|
const [showDropdown, setShowDropdown] = React.useState(false);
|
|
const [showDropdownNumber, setShowDropdownNumber] = React.useState(0);
|
|
const [showAutocomplete, setShowAutocomplete] = React.useState(false);
|
|
const [menuPosition, setMenuPosition] = useState(null);
|
|
const [uiBox, setUiBox] = useState(null);
|
|
const isIntegration = selectedAction.app_id === "integration"
|
|
|
|
useEffect(() => {
|
|
if (setLastSaved !== undefined) {
|
|
setLastSaved(false)
|
|
}
|
|
}, [expansionModalOpen])
|
|
|
|
|
|
useEffect(() => {
|
|
// Changes the order of params to show in order:
|
|
// auth, required, optional
|
|
var changed = false
|
|
if (selectedActionParameters === undefined || selectedActionParameters === null || selectedActionParameters.length === 0) {
|
|
return
|
|
}
|
|
|
|
// Fixing required fields with a shitty structure :)
|
|
if (selectedApp !== undefined && selectedApp !== null && selectedApp.generated === true && selectedAction !== undefined && selectedAction !== null && selectedAction.name !== undefined && selectedAction.name !== null && selectedApp.actions !== undefined && selectedApp.actions !== null && selectedApp.actions.length > 0 && (selectedAction.required_body_fields === undefined || selectedAction.required_body_fields === null || selectedAction.required_body_fields.length === 0)) {
|
|
// Check for required fields
|
|
for (var actionkey in selectedApp.actions) {
|
|
var action = selectedApp.actions[actionkey]
|
|
if (action.name === selectedAction.name) {
|
|
selectedAction.required_body_fields = action.required_body_fields
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check if missing parameters?
|
|
var auth = []
|
|
var required = []
|
|
var optional = []
|
|
|
|
var bodyfield = []
|
|
var special_optional = []
|
|
var generated_optional = []
|
|
|
|
var keyorder = []
|
|
for (let paramkey in selectedActionParameters) {
|
|
var param = selectedActionParameters[paramkey]
|
|
keyorder.push(param.name)
|
|
|
|
if (param.configuration) {
|
|
auth.push(param)
|
|
continue
|
|
}
|
|
|
|
if (param?.value?.toLowerCase().includes("secret. replace")) {
|
|
param.value = ""
|
|
}
|
|
|
|
|
|
if (selectedApp?.generated === true && param?.name === "body") {
|
|
param.required = true
|
|
bodyfield.push(param)
|
|
continue
|
|
}
|
|
|
|
if (param.required === false && param.name.startsWith("${") && param.name.endsWith("}")) {
|
|
// Check if it's a required param
|
|
param.autocompleted = false
|
|
if (selectedAction.required_body_fields !== undefined && selectedAction.required_body_fields !== null && selectedAction.required_body_fields.length > 0) {
|
|
if (selectedAction.required_body_fields.includes(param.name)) {
|
|
param.required = true
|
|
}
|
|
}
|
|
}
|
|
|
|
if (param.required) {
|
|
required.push(param)
|
|
continue
|
|
}
|
|
|
|
if (param.name === "headers" || param.name === "queries") {
|
|
special_optional.push(param)
|
|
continue
|
|
}
|
|
|
|
if (hideBody && param?.description.includes("Generated")) {
|
|
continue
|
|
}
|
|
|
|
if (param.field_active === true) {
|
|
generated_optional.push(param)
|
|
continue
|
|
}
|
|
|
|
optional.push(param)
|
|
}
|
|
|
|
// Sort order: auth > body(used for simple/advanced) > required > optional
|
|
// Optional field order:
|
|
// 1. headers & queries
|
|
// 2. other fields
|
|
// 3. generated fields & all else
|
|
|
|
|
|
const newparams = auth
|
|
.concat(bodyfield)
|
|
.concat(required)
|
|
.concat(special_optional)
|
|
.concat(generated_optional)
|
|
.concat(optional)
|
|
|
|
var newkeyorder = []
|
|
for (let paramkey in newparams) {
|
|
//console.log("Param: ", newparams[paramkey])
|
|
|
|
newkeyorder.push(newparams[paramkey].name)
|
|
}
|
|
|
|
if (keyorder.join(",") !== newkeyorder.join(",")) {
|
|
//toast("KEYORDER CHANGED!")
|
|
|
|
setSelectedActionParameters(newparams)
|
|
selectedAction.parameters = newparams
|
|
setSelectedAction(selectedAction)
|
|
}
|
|
}, [selectedActionParameters])
|
|
|
|
useEffect(() => {
|
|
const shouldHide = localStorage.getItem("hideBody")
|
|
if (shouldHide !== null) {
|
|
const ishiding = shouldHide !== "true"
|
|
if (ishiding !== hideBody) {
|
|
setHideBody(ishiding)
|
|
}
|
|
}
|
|
|
|
if (selectedActionEnvironment === undefined || selectedActionEnvironment === null || Object.keys(selectedActionEnvironment).length === 0) {
|
|
|
|
if (environments !== undefined && environments !== null && environments.length > 0) {
|
|
if (selectedAction.environment !== undefined && selectedAction.environment !== null) {
|
|
|
|
const foundenv = environments.find(env => env.id === selectedAction.environment || selectedAction.environment === env.Name)
|
|
|
|
if (foundenv !== undefined && foundenv !== null) {
|
|
setSelectedActionEnvironment(foundenv)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}, [])
|
|
|
|
const keywords = [
|
|
"len(",
|
|
"lower(",
|
|
"upper(",
|
|
"trim(",
|
|
"split(",
|
|
"length(",
|
|
"number(",
|
|
"parse(",
|
|
"join(",
|
|
];
|
|
|
|
const getApp = (appId, setApp) => {
|
|
fetch(globalUrl + "/api/v1/apps/" + appId + "/config?openapi=false", {
|
|
headers: {
|
|
Accept: "application/json",
|
|
},
|
|
credentials: "include",
|
|
})
|
|
.then((response) => {
|
|
if (response.status === 200) {
|
|
//toast("Successfully GOT app "+appId)
|
|
} else {
|
|
toast("Failed getting app");
|
|
}
|
|
|
|
return response.json();
|
|
})
|
|
.then((responseJson) => {
|
|
console.log("RESPONSE: ", responseJson);
|
|
|
|
const parsedapp =
|
|
responseJson.app !== undefined && responseJson.app !== null
|
|
? JSON.parse(atob(responseJson.app))
|
|
: {};
|
|
console.log("PARSED: ", parsedapp);
|
|
//data = parsedapp.body === undefined ? parsedapp : parsedapp.body
|
|
|
|
if (
|
|
setApp &&
|
|
parsedapp.actions !== undefined &&
|
|
parsedapp.actions !== null
|
|
) {
|
|
console.log("Inside first if");
|
|
if (
|
|
selectedApp.versions !== undefined &&
|
|
selectedApp.versions !== null
|
|
) {
|
|
parsedapp.versions = selectedApp.versions;
|
|
}
|
|
|
|
if (
|
|
selectedApp.loop_versions !== undefined &&
|
|
selectedApp.loop_versions !== null
|
|
) {
|
|
parsedapp.loop_versions = selectedApp.loop_versions;
|
|
}
|
|
|
|
// Find authentication, and if it works?
|
|
// If authentication has less OR more fields, it has to change
|
|
//console.log(selected
|
|
|
|
console.log("Inside first if2");
|
|
var foundAction = parsedapp.actions.find(
|
|
(action) =>
|
|
action.name.toLowerCase() === selectedAction.name.toLowerCase()
|
|
);
|
|
if (foundAction !== null && foundAction !== undefined) {
|
|
var foundparams = [];
|
|
for (let [paramkey,paramkeyval] in Object.entries(foundAction.parameters)) {
|
|
const param = foundAction.parameters[paramkey];
|
|
|
|
const foundParam = selectedAction.parameters.find(
|
|
(item) => item.name.toLowerCase() === param.name.toLowerCase()
|
|
);
|
|
if (foundParam === undefined) {
|
|
console.log("COULDNT find Param: ", param);
|
|
} else {
|
|
foundAction.parameters[paramkey] = foundParam;
|
|
}
|
|
//foundparams.push(param.name)
|
|
}
|
|
} else {
|
|
toast("Couldn't find action " + selectedAction.name);
|
|
}
|
|
|
|
selectedAction.errors = [];
|
|
selectedAction.is_valid = true;
|
|
|
|
// Updating params for the new action
|
|
selectedAction.parameters = foundAction.parameters;
|
|
selectedAction.app_id = appId;
|
|
selectedAction.app_version = parsedapp.app_version;
|
|
|
|
setSelectedAction(selectedAction);
|
|
setSelectedApp(parsedapp);
|
|
}
|
|
})
|
|
.catch((error) => {
|
|
toast(error.toString());
|
|
});
|
|
};
|
|
|
|
|
|
|
|
const defineStartnode = () => {
|
|
if (cy === undefined) {
|
|
return;
|
|
}
|
|
|
|
var oldstartnode = cy.getElementById(workflow.start);
|
|
if (oldstartnode.length > 0) {
|
|
oldstartnode[0].data("isStartNode", false);
|
|
var oldnodecnt = workflow.actions.findIndex(
|
|
(a) => a.id === workflow.start
|
|
);
|
|
if (workflow.actions[oldnodecnt] !== undefined) {
|
|
workflow.actions[oldnodecnt].isStartNode = false;
|
|
}
|
|
}
|
|
|
|
var newstartnode = cy.getElementById(selectedAction.id);
|
|
if (newstartnode.length > 0) {
|
|
newstartnode[0].data("isStartNode", true);
|
|
var newnodecnt = workflow.actions.findIndex(
|
|
(a) => a.id === selectedAction.id
|
|
);
|
|
console.log("NEW NODE CNT: ", newnodecnt);
|
|
if (workflow.actions[newnodecnt] !== undefined) {
|
|
workflow.actions[newnodecnt].isStartNode = true;
|
|
console.log(workflow.actions[newnodecnt]);
|
|
}
|
|
}
|
|
|
|
// Find branches with triggers as source nodes
|
|
// Move these targets to be the new node
|
|
// Set arrows pointing to new startnode with errors
|
|
//for (var key in workflow.branches) {
|
|
// var item = workflow.branches[key]
|
|
// if (item.destination_id === oldstartnode[0].data()["id"]) {
|
|
// var curbranch = cy.getElementById(item.id)
|
|
// if (curbranch.length > 0) {
|
|
// //console.log(curbranch[0].data())
|
|
// //curbranch[0].data("target", selectedAction.id)
|
|
// //curbranch[0].data("hasErrors", true)
|
|
// //workflow.branches[key].destination_id = selectedAction.id
|
|
// //console.log(curbranch[0].data())
|
|
// }
|
|
// }
|
|
//}
|
|
|
|
setUpdate("start_node" + selectedAction.id);
|
|
workflow.start = selectedAction.id;
|
|
setWorkflow(workflow);
|
|
//setStartNode(selectedAction.id)
|
|
};
|
|
|
|
|
|
useEffect(() => {
|
|
// Only set app action name if it has changed
|
|
if (selectedAction.label !== appActionName) {
|
|
setAppActionName(selectedAction.label);
|
|
|
|
const shouldHide = localStorage.getItem("hideBody")
|
|
if (shouldHide !== null) {
|
|
const ishiding = shouldHide !== "true"
|
|
if (ishiding !== hideBody) {
|
|
setHideBody(ishiding)
|
|
}
|
|
}
|
|
}
|
|
|
|
if(selectedAction.label !== prevActionName){
|
|
setPrevActionName(selectedAction.label)
|
|
}
|
|
|
|
// Only set delay if it has changed
|
|
const newDelay = selectedAction?.execution_delay || 0;
|
|
if (newDelay !== delay) {
|
|
setDelay(newDelay);
|
|
}
|
|
|
|
// Only set selected action parameters if they have changed
|
|
if (selectedAction?.parameters?.length > 0 && selectedAction.label !== appActionName) {
|
|
//console.log("PARAMS CHANGED DURING APPCHANGE: ", selectedAction.parameters)
|
|
setSelectedActionParameters(selectedAction.parameters);
|
|
}
|
|
|
|
// Only set selected variable parameter if it is null or undefined
|
|
if (!selectedVariableParameter && workflow.workflow_variables?.length > 0) {
|
|
setSelectedVariableParameter(workflow.workflow_variables[0].name);
|
|
}
|
|
},[selectedAction,selectedApp,setNewSelectedAction,workflow, workflowExecutions, getParents])
|
|
|
|
useEffect(() => {
|
|
const newActionList = [];
|
|
const parentActionList = [];
|
|
|
|
// Process workflowExecutions
|
|
if (workflowExecutions.length > 0) {
|
|
for (let execution of workflowExecutions) {
|
|
const execArg = execution.execution_argument;
|
|
if (execArg && execArg.length > 0) {
|
|
const valid = validateJson(execArg);
|
|
if (valid.valid) {
|
|
newActionList.push({
|
|
type: "Execution Argument",
|
|
name: "Execution Argument",
|
|
value: "$exec",
|
|
highlight: "exec",
|
|
autocomplete: "exec",
|
|
example: valid.result,
|
|
})
|
|
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add default Execution Argument if none were added
|
|
if (newActionList.length === 0) {
|
|
newActionList.push({
|
|
type: "Execution Argument",
|
|
name: "Execution Argument",
|
|
value: "$exec",
|
|
highlight: "exec",
|
|
autocomplete: "exec",
|
|
example: "",
|
|
})
|
|
}
|
|
|
|
// Look for cachekey
|
|
if (newActionList.find((item) => item.type === "Shuffle DB") === undefined) {
|
|
let cacheKey = {
|
|
type: "Shuffle DB",
|
|
name: "Shuffle DB",
|
|
value: "$shuffle_cache",
|
|
highlight: "shuffle_cache",
|
|
autocomplete: "shuffle_cache",
|
|
example: "",
|
|
};
|
|
|
|
if (listCache?.keys?.length > 0) {
|
|
cacheKey.example = {};
|
|
for (let item of listCache.keys) {
|
|
if (item.key) {
|
|
let itemValue = item.value ?? "";
|
|
if (itemValue.length > 10000) {
|
|
itemValue = "";
|
|
}
|
|
cacheKey.example[item.key.split(" ").join("_")] = { value: itemValue };
|
|
}
|
|
}
|
|
}
|
|
|
|
newActionList.push(cacheKey);
|
|
}
|
|
|
|
// Process workflow variables
|
|
if (workflow.workflow_variables?.length > 0) {
|
|
for (let variable of workflow.workflow_variables) {
|
|
newActionList.push({
|
|
type: "workflow_variable",
|
|
name: variable.name,
|
|
value: variable.value,
|
|
id: variable.id,
|
|
autocomplete: variable.name.split(" ").join("_"),
|
|
example: variable.value,
|
|
});
|
|
}
|
|
}
|
|
|
|
// Process execution variables
|
|
if (workflow.execution_variables?.length > 0) {
|
|
for (let variable of workflow.execution_variables) {
|
|
let exampleOutput = "";
|
|
for (let exec of workflowExecutions) {
|
|
const foundExec = exec.execution_variables?.find(exvar => exvar.name === variable.name);
|
|
if (foundExec?.value) {
|
|
exampleOutput = foundExec.value;
|
|
break;
|
|
}
|
|
}
|
|
newActionList.push({
|
|
type: "execution_variable",
|
|
name: variable.name,
|
|
value: variable.value,
|
|
id: variable.id,
|
|
autocomplete: variable.name.split(" ").join("_"),
|
|
example: exampleOutput,
|
|
});
|
|
}
|
|
}
|
|
|
|
// Process parent actions if getParents is provided
|
|
if (getParents) {
|
|
const parents = getParents(selectedAction);
|
|
if (parents.length > 1) {
|
|
const labels = [];
|
|
for (let parentNode of parents) {
|
|
if (parentNode.label !== "Execution Argument" && !labels.includes(parentNode.label)) {
|
|
labels.push(parentNode.label);
|
|
let exampleData = parentNode.example ?? "";
|
|
if (!exampleData && workflowExecutions.length > 0) {
|
|
for (let exec of workflowExecutions) {
|
|
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 (parentNode.label === undefined) {
|
|
parentNode.label = ""
|
|
}
|
|
|
|
newActionList.push({
|
|
type: "action",
|
|
id: parentNode.id,
|
|
name: parentNode.label,
|
|
autocomplete: parentNode.label.split(" ").join("_"),
|
|
example: exampleData,
|
|
});
|
|
|
|
parentActionList.push({
|
|
type: "action",
|
|
id: parentNode.id,
|
|
name: parentNode.label,
|
|
autocomplete: parentNode.label.split(" ").join("_"),
|
|
example: exampleData,
|
|
});
|
|
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
let newParameters = selectedAction?.parameters?.map((param) => {
|
|
let paramvalue = param.value === undefined || param.value === null ? "" : param.value;
|
|
let errorVars = [];
|
|
|
|
if(paramvalue.includes("$")){
|
|
let actions = workflow.actions?.map((action) => {
|
|
return "$"+action.label?.toLowerCase();
|
|
})
|
|
|
|
if(newActionList?.length > 0){
|
|
let appParentActions = parentActionList?.map(action => "$" + action.name.toLowerCase());
|
|
let notPresentAction = actions?.filter((action) => !appParentActions?.includes(action))
|
|
notPresentAction?.forEach((action) => {
|
|
action = action.replace(" ", "_");
|
|
if(paramvalue.includes(action)){
|
|
errorVars.push(action);
|
|
// paramvalue = paramvalue.replace(action, "")
|
|
// paramvalue = paramvalue.replace(/^\s*[\r\n]/gm, "");
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
let message = "";
|
|
if(errorVars.length > 0){
|
|
if(errorVars.length === 1){
|
|
message = errorVars[0] + " is not accessible in this action.";
|
|
}else{
|
|
message = errorVars.join(", ") + " are not accessible in this action.";
|
|
}
|
|
}
|
|
|
|
if (param?.configuration) {
|
|
let regex = /(^|[^\\])\$/;
|
|
if (regex.test(paramvalue)) {
|
|
if(message.length > 0){
|
|
message += "\nUse \"\\$\" instead of \"$\" if you want to escape $ (1)";
|
|
}else{
|
|
message = "Use \"\\$\" instead of \"$\" if you want to escape $ (2)";
|
|
}
|
|
}
|
|
}
|
|
return {...param, value: paramvalue, error: message}
|
|
});
|
|
setSelectedActionParameters(newParameters);
|
|
setActionlist(newActionList);
|
|
}, [workflow.execution_variables, paramUpdate, workflow.workflow_variables, workflowExecutions, workflow, selectedAction, listCache, getParents,setNewSelectedAction]);
|
|
|
|
useEffect(() => {
|
|
selectedNameChange(appActionName)
|
|
|
|
if (actionDelayChange !== undefined) {
|
|
actionDelayChange(delay)
|
|
}
|
|
},[appActionName,delay])
|
|
|
|
const handleParamChange = (event, count,data) => {
|
|
const newParams = [...selectedActionParameters];
|
|
newParams.map((param) => {
|
|
if (param.name === data.name) {
|
|
param.value = event.target.value;
|
|
}
|
|
})
|
|
setSelectedActionParameters(newParams);
|
|
setParamUpdate(event.target.value);
|
|
changeActionParameter(event, count, data)
|
|
}
|
|
|
|
const calculateHelpertext = (input_data) => {
|
|
var helperText = ""
|
|
var looperText = ""
|
|
//const found = input_data.match(/[$]{1}([a-zA-Z0-9_-]+\.?){1}([a-zA-Z0-9#_-]+\.?){0,}/g)
|
|
var found = input_data.match(/[\\]{0,1}[$]{1}([a-zA-Z0-9_@-]+\.?){1}([a-zA-Z0-9#_@-]+\.?){0,}/g)
|
|
|
|
if (found !== null && found !== undefined) {
|
|
var new_occurences = []
|
|
for (let [key,keyval] in Object.entries(found)) {
|
|
if (found[key][0] !== "\\") {
|
|
new_occurences.push(found[key])
|
|
}
|
|
}
|
|
|
|
found = new_occurences.valueOf()
|
|
}
|
|
|
|
if (found !== null) {
|
|
try {
|
|
// When the found array is empty.
|
|
for (let i = 0; i < found.length; i++) {
|
|
const variableSplit = found[i].split(".#")
|
|
if ((variableSplit.length-1) > 1) {
|
|
//console.log("Larger than 1: ", variableSplit)
|
|
if (looperText.length === 0) {
|
|
looperText += "PS: Double looping (.#.#) may cause problems."
|
|
}
|
|
}
|
|
|
|
var foundSlice = false
|
|
for (let j = 0; j < actionlist.length; j++) {
|
|
//console.log("ACTION: ", found[i], actionlist[j])
|
|
//console.log("ACTION :", found[i].split(".")[0].slice(1,).toLowerCase(), actionlist[j].autocomplete.toLowerCase())
|
|
if(found[i].split(".")[0].slice(1,).toLowerCase() == actionlist[j].autocomplete.toLowerCase()){
|
|
//console.log("Found: ", found[i])
|
|
// Validate path?
|
|
|
|
foundSlice = true
|
|
}
|
|
}
|
|
|
|
if (!foundSlice) {
|
|
if (!helperText.includes("Invalid variables")) {
|
|
helperText+= "Invalid variables: "
|
|
}
|
|
helperText+= found[i] + ", "
|
|
}
|
|
}
|
|
} catch (e) {
|
|
console.log("Parsing error: ", e)
|
|
}
|
|
}
|
|
|
|
if (looperText.length > 0) {
|
|
if (helperText.length > 0) {
|
|
helperText += ". "
|
|
}
|
|
|
|
helperText += looperText
|
|
}
|
|
|
|
return helperText
|
|
}
|
|
|
|
const changeActionParameter = (event, count, data, viewForceUpdate) => {
|
|
//console.log("Action change: ", selectedAction, data)
|
|
if (data.name.startsWith("${") && data.name.endsWith("}")) {
|
|
// PARAM FIX - Gonna use the ID field, even though it's a hack
|
|
const paramcheck = selectedAction.parameters.find((param) => param.name === "body");
|
|
|
|
if (paramcheck !== undefined) {
|
|
// Escapes all double quotes
|
|
var toReplace = event.target.value.trim()
|
|
|
|
|
|
if (!toReplace.startsWith("{") && !toReplace.startsWith("[")) {
|
|
toReplace = toReplace.replaceAll('\\"', '"').replaceAll('"', '\\"')
|
|
}
|
|
|
|
console.log("REPLACE WITH: ", toReplace);
|
|
if (
|
|
paramcheck["value_replace"] === undefined ||
|
|
paramcheck["value_replace"] === null
|
|
) {
|
|
paramcheck["value_replace"] = [
|
|
{
|
|
key: data.name,
|
|
value: toReplace,
|
|
},
|
|
];
|
|
|
|
} else {
|
|
const subparamindex = paramcheck["value_replace"].findIndex(
|
|
(param) => param.key === data.name
|
|
);
|
|
if (subparamindex === -1) {
|
|
paramcheck["value_replace"].push({
|
|
key: data.name,
|
|
value: toReplace,
|
|
});
|
|
} else {
|
|
paramcheck["value_replace"][subparamindex]["value"] = toReplace;
|
|
}
|
|
}
|
|
|
|
if (selectedActionParameters[count].value_replace === undefined) {
|
|
selectedActionParameters[count].value_replace = paramcheck
|
|
}
|
|
|
|
if (selectedAction?.parameters[count] !== undefined && selectedAction?.parameters[count].value_replace === undefined) {
|
|
selectedAction.parameters[count].value_replace = paramcheck
|
|
}
|
|
|
|
if (paramcheck["value_replace"] === undefined) {
|
|
selectedActionParameters[count]["value_replace"] = paramcheck
|
|
|
|
if (selectedAction?.parameters[count] !== undefined) {
|
|
selectedAction.parameters[count]["value_replace"] = paramcheck
|
|
}
|
|
} else {
|
|
selectedActionParameters[count]["value_replace"] = paramcheck["value_replace"];
|
|
if (selectedAction?.parameters[count] !== undefined) {
|
|
selectedAction.parameters[count]["value_replace"] = paramcheck["value_replace"];
|
|
}
|
|
}
|
|
setSelectedAction(selectedAction);
|
|
//setUpdate(Math.random())
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
if (event.target.value[event.target.value.length - 1] === "$") {
|
|
if (!showDropdown) {
|
|
setShowAutocomplete(false);
|
|
setShowDropdown(true);
|
|
setShowDropdownNumber(count);
|
|
}
|
|
} else {
|
|
if (showDropdown) {
|
|
setShowDropdown(false);
|
|
}
|
|
}
|
|
|
|
// bad detection mechanism probably
|
|
if (event.target.value[event.target.value.length - 1] === "." && actionlist.length > 0) {
|
|
console.log("GET THE LAST ARGUMENT FOR NODE!");
|
|
// THIS IS AN EXAMPLE OF SHOWING IT
|
|
/*
|
|
|
|
const inputdata = {"data": "1.2.3.4", "dataType": "4.5.6.6"}
|
|
setJsonList(GetParsedPaths(inputdata, ""))
|
|
if (!showDropdown) {
|
|
setShowAutocomplete(false)
|
|
setShowDropdown(true)
|
|
setShowDropdownNumber(count)
|
|
}
|
|
console.log(jsonList)
|
|
*/
|
|
|
|
// Search for the item backwards
|
|
// 1. Reverse search backwards from . -> $
|
|
// 2. Search the actionlist for the item
|
|
// 3. Find the data for the specific item
|
|
|
|
var curstring = "";
|
|
var record = false;
|
|
for (let [key,keyval] in Object.entries(selectedActionParameters[count].value)) {
|
|
const item = selectedActionParameters[count].value[key];
|
|
if (record) {
|
|
curstring += item;
|
|
}
|
|
|
|
if (item === "$") {
|
|
record = true;
|
|
curstring = "";
|
|
}
|
|
}
|
|
|
|
//console.log("CURSTRING: ", curstring)
|
|
if (curstring.length > 0 && actionlist !== null) {
|
|
// Search back in the action list
|
|
curstring = curstring.split(" ").join("_").toLowerCase();
|
|
var actionItem = actionlist.find(
|
|
(data) =>
|
|
data.autocomplete.split(" ").join("_").toLowerCase() === curstring
|
|
);
|
|
if (actionItem !== undefined) {
|
|
console.log("Found item: ", actionItem);
|
|
|
|
//actionItem.example = actionItem.example.trim()
|
|
//actionItem.example = actionItem.example.split(" None").join(" \"None\"")
|
|
//actionItem.example = actionItem.example.split("\'").join("\"")
|
|
|
|
var jsonvalid = true;
|
|
try {
|
|
const tmp = String(JSON.parse(actionItem.example));
|
|
if (
|
|
!actionItem.example.includes("{") &&
|
|
!actionItem.example.includes("[")
|
|
) {
|
|
jsonvalid = false;
|
|
}
|
|
} catch (e) {
|
|
jsonvalid = false;
|
|
}
|
|
|
|
if (jsonvalid) {
|
|
setJsonList(GetParsedPaths(JSON.parse(actionItem.example), ""));
|
|
|
|
if (!showDropdown) {
|
|
setShowAutocomplete(false);
|
|
setShowDropdown(true);
|
|
setShowDropdownNumber(count);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if (jsonList.length > 0) {
|
|
setJsonList([]);
|
|
}
|
|
}
|
|
|
|
selectedActionParameters[count].autocompleted = false
|
|
selectedAction.parameters[count].autocompleted = false
|
|
selectedActionParameters[count].value = event.target.value;
|
|
selectedAction.parameters[count].value = event.target.value;
|
|
|
|
var forceUpdate = false
|
|
if (isCloud && (selectedAction.app_name === "Shuffle Tools" || selectedAction.app_name === "email") && (selectedAction.name === "send_email_shuffle" || selectedAction.name === "send_sms_shuffle") && data.name === "apikey") {
|
|
console.log("APIKEY - this shouldn't show up!")
|
|
}
|
|
|
|
if (selectedAction.app_name === "Shuffle Tools" && selectedAction.name === "filter_list" && data.name === "input_list") {
|
|
//console.log("FILTER LIST!: ", event, count, data)
|
|
const parsedvalue = event.target.value
|
|
if (parsedvalue.includes(".#")) {
|
|
const splitparsed = parsedvalue.split(".#.")
|
|
//console.log("Cant contain #: ", splitparsed)
|
|
if (splitparsed.length > 1) {
|
|
data.value = splitparsed[0]
|
|
|
|
selectedActionParameters[count].value = splitparsed[0]
|
|
selectedAction.parameters[count].value = splitparsed[0]
|
|
|
|
selectedActionParameters[1].value = splitparsed[1]
|
|
selectedAction.parameters[1].value = splitparsed[1]
|
|
} else {
|
|
// Remove .# and after
|
|
const splitparsed = parsedvalue.split(".#")
|
|
data.value = splitparsed[0]
|
|
selectedActionParameters[0].value = splitparsed[0]
|
|
selectedAction.parameters[0].value = splitparsed[0]
|
|
|
|
selectedActionParameters[1].value = ""
|
|
selectedAction.parameters[1].value = ""
|
|
|
|
toast.warn("No value found in the list. Please select an item in the list to filter based on.")
|
|
}
|
|
|
|
forceUpdate = true
|
|
selectedActionParameters[0].autocompleted = true
|
|
selectedAction.parameters[0].autocompleted = true
|
|
selectedActionParameters[1].autocompleted = true
|
|
selectedAction.parameters[1].autocompleted = true
|
|
}
|
|
}
|
|
|
|
setSelectedAction(selectedAction)
|
|
if (forceUpdate || viewForceUpdate === true) {
|
|
setUpdate(Math.random())
|
|
}
|
|
|
|
//console.log("END OF THIS THING")
|
|
//setUpdate(event.target.value)
|
|
};
|
|
|
|
|
|
const changeActionParameterCodeMirror = (event, count, data) => {
|
|
if (data.startsWith("${") && data.endsWith("}")) {
|
|
// PARAM FIX - Gonna use the ID field, even though it's a hack
|
|
const paramcheck = selectedAction.parameters.find(param => param.name === "body")
|
|
if (paramcheck !== undefined) {
|
|
// Escapes all double quotes
|
|
const toReplace = event.target.value.trim().replaceAll("\\\"", "\"").replaceAll("\"", "\\\"");
|
|
console.log("REPLACE WITH: ", toReplace)
|
|
if (paramcheck["value_replace"] === undefined || paramcheck["value_replace"] === null) {
|
|
paramcheck["value_replace"] = [{
|
|
"key": data.name,
|
|
"value": toReplace,
|
|
}]
|
|
|
|
console.log("IN IF: ", paramcheck)
|
|
|
|
} else {
|
|
const subparamindex = paramcheck["value_replace"].findIndex(param => param.key === data.name)
|
|
if (subparamindex === -1) {
|
|
paramcheck["value_replace"].push({
|
|
"key": data.name,
|
|
"value": toReplace,
|
|
})
|
|
} else {
|
|
paramcheck["value_replace"][subparamindex]["value"] = toReplace
|
|
}
|
|
}
|
|
//console.log("PARAM: ", paramcheck)
|
|
//if (paramcheck.id === undefined) {
|
|
// console.log("Normal paramcheck")
|
|
//} else {
|
|
// selectedActionParameters[count]["value_replace"] = paramcheck
|
|
// selectedAction.parameters[count]["value_replace"] = paramcheck
|
|
//}
|
|
|
|
if (paramcheck["value_replace"] === undefined) {
|
|
selectedActionParameters[count]["value_replace"] = paramcheck
|
|
selectedAction.parameters[count]["value_replace"] = paramcheck
|
|
} else {
|
|
selectedActionParameters[count]["value_replace"] = paramcheck["value_replace"]
|
|
selectedAction.parameters[count]["value_replace"] = paramcheck["value_replace"]
|
|
}
|
|
console.log("RESULT: ", selectedAction)
|
|
setSelectedAction(selectedAction)
|
|
//setUpdate(Math.random())
|
|
return
|
|
}
|
|
}
|
|
|
|
if (event.target.value[event.target.value.length-1] === "$") {
|
|
if (!showDropdown) {
|
|
setShowAutocomplete(false)
|
|
setShowDropdown(true)
|
|
setShowDropdownNumber(count)
|
|
}
|
|
} else {
|
|
if (showDropdown) {
|
|
setShowDropdown(false)
|
|
}
|
|
}
|
|
|
|
|
|
// bad detection mechanism probably
|
|
if (event.target.value[event.target.value.length-1] === "." && actionlist.length > 0) {
|
|
console.log("GET THE LAST ARGUMENT FOR NODE!")
|
|
// THIS IS AN EXAMPLE OF SHOWING IT
|
|
/*
|
|
const inputdata = {"data": "1.2.3.4", "dataType": "4.5.6.6"}
|
|
setJsonList(GetParsedPaths(inputdata, ""))
|
|
if (!showDropdown) {
|
|
setShowAutocomplete(false)
|
|
setShowDropdown(true)
|
|
setShowDropdownNumber(count)
|
|
}
|
|
console.log(jsonList)
|
|
*/
|
|
|
|
// Search for the item backwards
|
|
// 1. Reverse search backwards from . -> $
|
|
// 2. Search the actionlist for the item
|
|
// 3. Find the data for the specific item
|
|
|
|
var curstring = ""
|
|
var record = false
|
|
for (let [key,keyval] in Object.entries(selectedActionParameters[count].value)) {
|
|
const item = selectedActionParameters[count].value[key]
|
|
if (record) {
|
|
curstring += item
|
|
}
|
|
|
|
if (item === "$") {
|
|
record = true
|
|
curstring = ""
|
|
}
|
|
}
|
|
|
|
//console.log("CURSTRING: ", curstring)
|
|
if (curstring.length > 0 && actionlist !== null) {
|
|
// Search back in the action list
|
|
curstring = curstring.split(" ").join("_").toLowerCase()
|
|
var actionItem = actionlist.find(data => data.autocomplete.split(" ").join("_").toLowerCase() === curstring)
|
|
if (actionItem !== undefined) {
|
|
console.log("Found item: ", actionItem)
|
|
|
|
//actionItem.example = actionItem.example.trim()
|
|
//actionItem.example = actionItem.example.split(" None").join(" \"None\"")
|
|
//actionItem.example = actionItem.example.split("\'").join("\"")
|
|
|
|
var jsonvalid = true
|
|
try {
|
|
const tmp = String(JSON.parse(actionItem.example))
|
|
if (!actionItem.example.includes("{") && !actionItem.example.includes("[")) {
|
|
jsonvalid = false
|
|
}
|
|
} catch (e) {
|
|
jsonvalid = false
|
|
}
|
|
|
|
if (jsonvalid) {
|
|
setJsonList(GetParsedPaths(JSON.parse(actionItem.example), ""))
|
|
|
|
if (!showDropdown) {
|
|
setShowAutocomplete(false)
|
|
setShowDropdown(true)
|
|
setShowDropdownNumber(count)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if (jsonList.length > 0) {
|
|
setJsonList([])
|
|
}
|
|
}
|
|
|
|
setTimeout(() => {
|
|
selectedActionParameters[count].autocompleted = false
|
|
selectedAction.parameters[count].autocompleted = false
|
|
selectedActionParameters[count].value = data
|
|
selectedAction.parameters[count].value = data
|
|
}, 100);
|
|
setSelectedAction(selectedAction)
|
|
//setUpdate(Math.random())
|
|
//setUpdate(event.target.value)
|
|
}
|
|
|
|
|
|
const changeActionParameterVariable = (fieldvalue, count) => {
|
|
//console.log("CALLED THIS ONE WITH VALUE!", fieldvalue)
|
|
//if (selectedVariableParameter === fieldvalue) {
|
|
// return
|
|
//}
|
|
|
|
setSelectedVariableParameter(fieldvalue);
|
|
|
|
selectedActionParameters[count].action_field = fieldvalue;
|
|
selectedAction.parameters = selectedActionParameters;
|
|
|
|
setSelectedApp(selectedApp);
|
|
setSelectedAction(selectedAction);
|
|
setUpdate(fieldvalue);
|
|
};
|
|
|
|
// Sets ACTION_RESULT things
|
|
const changeActionParameterActionResult = (fieldvalue, count) => {
|
|
//cy.nodes().forEach(function( ele ) {
|
|
// if (ele.data()["label"] === fieldvalue) {
|
|
// selectedActionParameters[count].action_field = ele.id()
|
|
// return
|
|
// }
|
|
//});
|
|
|
|
selectedActionParameters[count].action_field = fieldvalue;
|
|
selectedAction.parameters = selectedActionParameters;
|
|
|
|
// FIXME - check if startnode
|
|
|
|
// Set value
|
|
setSelectedApp(selectedApp);
|
|
|
|
setSelectedAction(selectedAction);
|
|
setUpdate(Math.random());
|
|
};
|
|
|
|
const changeActionParameterVariant = (data, count) => {
|
|
selectedActionParameters[count].variant = data;
|
|
selectedActionParameters[count].value = "";
|
|
|
|
if (data === "ACTION_RESULT" && getParents !== undefined) {
|
|
var parents = getParents(selectedAction);
|
|
if (parents.length > 0) {
|
|
selectedActionParameters[count].action_field = parents[0].label;
|
|
} else {
|
|
selectedActionParameters[count].action_field = "";
|
|
}
|
|
} else if (data === "WORKFLOW_VARIABLE") {
|
|
if (
|
|
workflow.workflow_variables !== null &&
|
|
workflow.workflow_variables !== undefined &&
|
|
workflow.workflow_variables.length > 0
|
|
) {
|
|
selectedActionParameters[count].action_field =
|
|
workflow.workflow_variables[0].name;
|
|
}
|
|
}
|
|
|
|
selectedAction.parameters = selectedActionParameters;
|
|
|
|
// This is a stupid workaround to make it refresh rofl
|
|
setSelectedAction({});
|
|
|
|
if (setSelectedTrigger !== undefined) {
|
|
setSelectedTrigger({});
|
|
setSelectedApp({});
|
|
setSelectedEdge({});
|
|
}
|
|
// FIXME - check if startnode
|
|
|
|
// Set value
|
|
setSelectedApp(selectedApp);
|
|
setSelectedAction(selectedAction);
|
|
setUpdate(Math.random());
|
|
};
|
|
|
|
|
|
const returnHelperText = (name, value) => {
|
|
if (name === undefined || value === undefined || name === null || value === null || name === "" || value === "") {
|
|
//console.log("RETURNING EMPTY")
|
|
return ""
|
|
}
|
|
|
|
var helperText = ""
|
|
if (name.includes("url")) {
|
|
if (value.includes("localhost") || value.includes("127.0.0.1")) {
|
|
helperText = "Can't use localhost in Shuffle. Please change to server's IP."
|
|
}
|
|
}
|
|
|
|
// Helpertext for openapi fields
|
|
//if (helperText === "" && name === "body" && selectedApp.generated && selectedApp.activated) {
|
|
// helperText = <span style={{color: "white", marginBottom: 5, marginleft: 5}}>
|
|
//}
|
|
|
|
return helperText
|
|
}
|
|
|
|
const errorHelperText = (name, value, error) => {
|
|
return (
|
|
<div style={{ whiteSpace: 'pre-line' }}>
|
|
{error}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
|
|
const analyzeFields = () => {
|
|
|
|
if (selectedAction === undefined || selectedAction === null) {
|
|
return null
|
|
}
|
|
|
|
if (selectedActionParameters === undefined || selectedActionParameters === null || selectedActionParameters.length === 0) {
|
|
return null
|
|
}
|
|
|
|
// Only shuffle tools for now
|
|
if (selectedAction.app_name !== "Shuffle Tools") {
|
|
return null
|
|
}
|
|
|
|
// Custom rules
|
|
if (selectedAction.name === "set_cache_value") {
|
|
var actionKey = ""
|
|
var actionValue = ""
|
|
for (let [key,keyval] in Object.entries(selectedActionParameters)) {
|
|
const param = selectedActionParameters[key]
|
|
if (param.name === "key") {
|
|
actionKey = param.value
|
|
}
|
|
|
|
if (param.name === "value") {
|
|
actionValue = param.value
|
|
}
|
|
}
|
|
|
|
if (actionKey === "" || actionValue === "") {
|
|
return null
|
|
}
|
|
|
|
if (!actionKey.includes(".#") && actionValue.includes(".#")) {
|
|
return <span>When the key ({actionKey}) is static, but the value is a list ({actionValue}), it will overwrite the list. You may be looking for the <span onClick={() => {}} style={{cursor: "pointer", color: "#FF8544", }}>Check Cache Contains</span> action instead.</span>
|
|
}
|
|
}
|
|
|
|
return null
|
|
|
|
}
|
|
|
|
const suggestionInfo = () => {
|
|
const suggestionText = analyzeFields()
|
|
if (suggestionText === undefined || suggestionText === null) {
|
|
return null
|
|
}
|
|
|
|
if (selectedAction.errors === undefined || selectedAction.errors === null || selectedAction.errors.length === 0) {
|
|
selectedAction.errors = ["Suggestion: " + suggestionText]
|
|
}
|
|
|
|
return <Paper style={{padding: 10, backgroundColor: theme.palette.surfaceColor, border: "1px solid red",}}>
|
|
<Typography variant="body" style={{color: "white", }}>
|
|
<b>Tip:</b> {suggestionText}
|
|
</Typography>
|
|
</Paper>
|
|
}
|
|
|
|
// FIXME: Issue #40 - selectedActionParameters not reset
|
|
if (Object.getOwnPropertyNames(selectedAction)?.length > 0 && selectedActionParameters?.length > 0) {
|
|
var wrapperapp = {
|
|
"id": "",
|
|
"name": "noapp",
|
|
"large_image": "",
|
|
}
|
|
|
|
var actionname = selectedAction.name.toLowerCase()
|
|
if (actionname === "email" || actionname === "communication") {
|
|
actionname = "comms"
|
|
}
|
|
|
|
if (isIntegration) {
|
|
|
|
// Check if actionname uppercase is in the parsedDatatypeImages() dictionary
|
|
if (parsedDatatypeImages()[actionname.toUpperCase()] !== undefined) {
|
|
var newimage = parsedDatatypeImages()[actionname.toUpperCase()]
|
|
//newimage = <img src={newimage} style={{width: 35, height: 35, marginRight: 5, borderRadius: 5, cursor: "pointer", border: noAppSelected ? "3px solid #86c142" : "2px solid rgba(255,255,255,0.6)"}} />
|
|
wrapperapp.large_image = newimage
|
|
} else {
|
|
console.log("Couldn't find actionname: ", actionname)
|
|
}
|
|
}
|
|
|
|
var authWritten = false;
|
|
var noAppSelected = false
|
|
if (selectedAction.parameters !== undefined && selectedAction.parameters !== null && selectedAction.parameters.length > 0) {
|
|
var paramIndex = selectedAction.parameters.findIndex((param) => param.name === "app_name")
|
|
if (paramIndex === -1 || selectedAction.parameters[paramIndex].value === "" || selectedAction.parameters[paramIndex].value === "noapp") {
|
|
// Check the actual value and if it's the same
|
|
noAppSelected = true
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
const ActionSelectOption = (actionprops) => {
|
|
const { option, newActionname, newActiondescription, useIcon, extraDescription, } = actionprops;
|
|
const [hover, setHover] = React.useState(false);
|
|
|
|
return (
|
|
<Tooltip
|
|
color="secondary"
|
|
title={newActiondescription}
|
|
placement="left"
|
|
>
|
|
<div style={{
|
|
cursor: "pointer",
|
|
padding: 8,
|
|
paddingLeft: 14,
|
|
paddingBottom: 4,
|
|
backgroundColor: hover ? theme.palette.surfaceColor : theme.palette.inputColor,
|
|
}} onMouseEnter={() => setHover(true)} onMouseLeave={() => setHover(false)}
|
|
onClick={(event) => {
|
|
// event.preventDefault()
|
|
//setSelectedAction(actionprops)
|
|
//setShowActionList(false)
|
|
//setUpdate(Math.random())
|
|
//
|
|
if (option !== undefined && option !== null) {
|
|
setNewSelectedAction({
|
|
target: {
|
|
value: option.name
|
|
}
|
|
});
|
|
}
|
|
|
|
document.activeElement.blur();
|
|
|
|
const disabledUiBox = localStorage.getItem("disabled_ui_box")
|
|
if (disabledUiBox === "true") {
|
|
} else {
|
|
setHiddenDescription(false)
|
|
}
|
|
}}
|
|
>
|
|
<div style={{ display: "flex", marginBottom: 0,}}>
|
|
<span
|
|
style={{
|
|
marginRight: 10,
|
|
marginTop: "auto",
|
|
marginBottom: 0,
|
|
}}
|
|
>
|
|
{useIcon}
|
|
</span>
|
|
<span style={{marginBottom: 0, marginTop: 3, }}>{newActionname}</span>
|
|
</div>
|
|
{extraDescription.length > 0 ?
|
|
<Typography variant="body2" color="textSecondary" style={{marginTop: 0, overflow: "hidden", whiteSpace: "nowrap", display: "block",}}>
|
|
{extraDescription}
|
|
</Typography>
|
|
: null}
|
|
</div>
|
|
</Tooltip>
|
|
)
|
|
}
|
|
|
|
const sortByCategoryLabel = (a, b) => {
|
|
const aHasCategoryLabel = a.category_label !== undefined && a.category_label !== null && a.category_label.length > 0
|
|
const bHasCategoryLabel = b.category_label !== undefined && b.category_label !== null && b.category_label.length > 0
|
|
|
|
// Sort by existence and length of "category_label"
|
|
if (aHasCategoryLabel && !bHasCategoryLabel) {
|
|
return -1
|
|
} else if (!aHasCategoryLabel && bHasCategoryLabel) {
|
|
return 1
|
|
} else {
|
|
return 0
|
|
}
|
|
}
|
|
|
|
// Function to deduplicate based on the "name" field
|
|
const deduplicateByName = (array) => {
|
|
const uniqueNames = {};
|
|
return array.filter(item => {
|
|
if (!item.hasOwnProperty('name') || !item.name.length) {
|
|
return true
|
|
}
|
|
if (!uniqueNames[item.name]) {
|
|
uniqueNames[item.name] = true
|
|
return true
|
|
}
|
|
return false
|
|
})
|
|
}
|
|
|
|
// Gets the most important actions first
|
|
const renderedActionOptions = deduplicateByName((
|
|
selectedApp.actions === undefined || selectedApp.actions === null ? [] :
|
|
selectedApp.actions.filter((a) =>
|
|
a.category_label !== undefined && a.category_label !== null && a.category_label.length > 0).concat(sortByKey(selectedApp.actions, "label"))
|
|
).sort(sortByCategoryLabel))
|
|
|
|
|
|
const selectedAppIcon = selectedAction.large_image
|
|
return (
|
|
<div style={appApiViewStyle} id="parsed_action_view">
|
|
|
|
{hideExtraTypes === true ? null : (
|
|
<span>
|
|
<div style={{ display: "flex", minHeight: 40, marginBottom: 30 }}>
|
|
<div style={{ flex: 1 }}>
|
|
<div style={{ display: "flex", }}
|
|
onClick={() => {
|
|
//window.open("/apps/${selectedAction.app_id}", "_blank")
|
|
}}
|
|
>
|
|
<Tooltip title={"App: "+selectedAction.app_name} placement="top">
|
|
<img src={selectedAppIcon} style={{
|
|
width: 30,
|
|
height: 30,
|
|
marginRight: 10,
|
|
borderRadius: 5,
|
|
marginTop: 13,
|
|
border: "2px solid rgba(255,255,255,0.3)",
|
|
}} />
|
|
</Tooltip>
|
|
<h3 style={{ }}>
|
|
{(
|
|
selectedAction?.app_name?.charAt(0).toUpperCase() +
|
|
selectedAction?.app_name?.substring(1)
|
|
)?.replaceAll("_", " ")}
|
|
</h3>
|
|
</div>
|
|
<div style={{display: "flex", marginTop: 0, }}>
|
|
<IconButton
|
|
style={{
|
|
marginTop: "auto",
|
|
marginBottom: "auto",
|
|
height: 30,
|
|
paddingLeft: 0,
|
|
paddingRight: 0,
|
|
}}
|
|
onClick={() => {
|
|
if (workflowExecutions.length > 0) {
|
|
// Look for the ID
|
|
var 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 === selectedAction.id
|
|
)
|
|
|
|
if (foundResult === undefined || foundResult === null) {
|
|
continue
|
|
}
|
|
|
|
const oldstartnode = cy.getElementById(selectedAction.id);
|
|
if (oldstartnode !== undefined && oldstartnode !== null) {
|
|
const foundname = oldstartnode.data("label")
|
|
if (foundname !== undefined && foundname !== null) {
|
|
foundResult.action.label = foundname
|
|
}
|
|
}
|
|
|
|
setSelectedResult(foundResult);
|
|
if (setCodeModalOpen !== undefined) {
|
|
setCodeModalOpen(true)
|
|
|
|
found = true
|
|
}
|
|
|
|
break
|
|
}
|
|
|
|
if (!found) {
|
|
toast("No result for this action yet. Please run the workflow first.")
|
|
}
|
|
}
|
|
}}
|
|
>
|
|
<Tooltip
|
|
color="primary"
|
|
title="See previous results for this action"
|
|
placement="top"
|
|
>
|
|
<ArrowLeftIcon style={{ color: "rgba(255,255,255,0.7)" }} />
|
|
</Tooltip>
|
|
</IconButton>
|
|
<IconButton
|
|
style={{
|
|
marginTop: "auto",
|
|
marginBottom: "auto",
|
|
height: 30,
|
|
marginLeft: 15,
|
|
paddingRight: 0,
|
|
}}
|
|
onClick={() => {
|
|
setAuthenticationModalOpen(true)
|
|
}}
|
|
>
|
|
<Tooltip
|
|
color="primary"
|
|
title="Find app documentation"
|
|
placement="top"
|
|
>
|
|
<DescriptionIcon style={{ color: "rgba(255,255,255,0.7)" }} />
|
|
</Tooltip>
|
|
</IconButton>
|
|
|
|
<IconButton
|
|
style={{
|
|
marginTop: "auto",
|
|
marginBottom: "auto",
|
|
height: 30,
|
|
marginLeft: 15,
|
|
paddingRight: 0,
|
|
}}
|
|
disabled={autoCompleting}
|
|
onClick={() => {
|
|
if (setAiQueryModalOpen !== undefined) {
|
|
setAiQueryModalOpen(true)
|
|
} else {
|
|
aiSubmit("Fill based on previous values", undefined, undefined, selectedAction)
|
|
}
|
|
|
|
setAutocompleting(true)
|
|
setTimeout(() => {
|
|
setAutocompleting(false)
|
|
}, 3000)
|
|
}}
|
|
>
|
|
<Tooltip
|
|
color="primary"
|
|
title={"Autocomplete the action"}
|
|
placement="top"
|
|
>
|
|
{autoCompleting ?
|
|
<CircularProgress style={{height: 20, width: 20, }} />
|
|
:
|
|
<AutoFixHighIcon style={{ color: "rgba(255,255,255,0.7)", height: 24, }} />
|
|
}
|
|
</Tooltip>
|
|
</IconButton>
|
|
</div>
|
|
</div>
|
|
<div style={{ display: "flex", flexDirection: "column" }}>
|
|
{selectedApp.versions !== null &&
|
|
selectedApp.versions !== undefined &&
|
|
selectedApp.versions.length > 1 ? (
|
|
<Select
|
|
MenuProps={{
|
|
disableScrollLock: true,
|
|
}}
|
|
value={selectedAction.app_version}
|
|
onChange={(event) => {
|
|
const newversion = selectedApp.versions.find(
|
|
(tmpApp) => tmpApp.version == event.target.value
|
|
)
|
|
|
|
if (newversion !== undefined && newversion !== null) {
|
|
getApp(newversion.id, true)
|
|
}
|
|
|
|
// Change in all actions in the workflow at the same time and add a toast.success() about it
|
|
for (var actionkey in workflow.actions) {
|
|
const action = workflow.actions[actionkey]
|
|
if (action.app_name === selectedAction.app_name) {
|
|
workflow.actions[actionkey].app_version = event.target.value
|
|
}
|
|
}
|
|
|
|
toast.success("Changed version of all nodes to "+event.target.value)
|
|
}}
|
|
style={{
|
|
marginTop: 10,
|
|
backgroundColor: theme.palette.surfaceColor,
|
|
backgroundColor: theme.palette.inputColor,
|
|
color: "white",
|
|
height: 35,
|
|
marginleft: 10,
|
|
borderRadius: theme.palette?.borderRadius,
|
|
}}
|
|
SelectDisplayProps={{
|
|
style: {
|
|
},
|
|
}}
|
|
>
|
|
{selectedApp.versions.map((data, index) => {
|
|
return (
|
|
<MenuItem
|
|
key={index}
|
|
style={{
|
|
backgroundColor: theme.palette.inputColor,
|
|
color: "white",
|
|
}}
|
|
value={data.version}
|
|
>
|
|
{data.version}
|
|
</MenuItem>
|
|
);
|
|
})}
|
|
</Select>
|
|
) : null}
|
|
</div>
|
|
</div>
|
|
<div style={{display: "flex"}}>
|
|
<div style={{flex: 5}}>
|
|
<Typography style={{color: "rgba(255,255,255,0.7)"}}>Name</Typography>
|
|
<TextField
|
|
style={theme.palette.textFieldStyle}
|
|
InputProps={{
|
|
style: theme.palette.innerTextfieldStyle,
|
|
disableUnderline: true,
|
|
}}
|
|
fullWidth
|
|
color="primary"
|
|
placeholder={selectedAction.label}
|
|
value={appActionName}
|
|
onChange={
|
|
(event) => {
|
|
let newValue = event.target.value
|
|
newValue = newValue.replaceAll(" ", "_")
|
|
setAppActionName(newValue)
|
|
}
|
|
}
|
|
onBlur={(e) => {
|
|
// Copy the name value
|
|
const name = e.target.value
|
|
const parsedBaseLabel = "$"+prevActionName.toLowerCase().replaceAll(" ", "_")
|
|
const newname = "$"+name.toLowerCase().replaceAll(" ", "_")
|
|
|
|
// Check if it's the same as the current name in use
|
|
//if (name === selectedAction.label) {
|
|
// console.log("Returning from name thing")
|
|
// return
|
|
//}
|
|
|
|
// Change in actions, triggers & conditions
|
|
// Highlight the changes somehow with a glow?
|
|
if (workflow.branches !== undefined && workflow.branches !== null) {
|
|
for (let [key,keyval] in Object.entries(workflow.branches)) {
|
|
if (workflow.branches[key].conditions !== undefined && workflow.branches[key].conditions !== null) {
|
|
for (let [subkey,subkeyval] in Object.entries(workflow.branches[key].conditions)) {
|
|
const condition = workflow.branches[key].conditions[subkey]
|
|
const sourceparam = condition.source
|
|
const destinationparam = condition.destination
|
|
|
|
// Should have a smarter way of discovering node names
|
|
// Finding index(es) and replacing at the location
|
|
if (sourceparam.value.includes("$")) {
|
|
try {
|
|
var cnt = -1
|
|
var previous = 0
|
|
while (true) {
|
|
cnt += 1
|
|
// Need to make sure e.g. changing the first here doesn't change the 2nd
|
|
// $change_me
|
|
// $change_me_2
|
|
|
|
const foundindex = sourceparam.value.toLowerCase().indexOf(parsedBaseLabel, previous)
|
|
if (foundindex === previous && foundindex !== 0) {
|
|
break
|
|
}
|
|
|
|
if (foundindex >= 0) {
|
|
previous = foundindex+newname.length
|
|
// Need to add diff of length to word
|
|
|
|
// Check location:
|
|
// If it's a-zA-Z_ then don't replace
|
|
if (sourceparam.value.length > foundindex+parsedBaseLabel.length) {
|
|
const regex = /[a-zA-Z0-9_]/g;
|
|
const match = sourceparam.value[foundindex+parsedBaseLabel.length].match(regex);
|
|
if (match !== null) {
|
|
continue
|
|
}
|
|
}
|
|
|
|
console.log("Old found: ", workflow.branches[key].conditions[subkey].source.value)
|
|
const extralength = newname.length-parsedBaseLabel.length
|
|
sourceparam.value = sourceparam.value.substring(0, foundindex) + newname + sourceparam.value.substring(foundindex-extralength+newname.length, sourceparam.value.length)
|
|
|
|
console.log("New: ", workflow.branches[key].conditions[subkey].source.value)
|
|
} else {
|
|
break
|
|
}
|
|
|
|
// Break no matter what after 5 replaces. May need to increase
|
|
if (cnt >= 5) {
|
|
break
|
|
}
|
|
|
|
}
|
|
} catch (e) {
|
|
console.log("Failed value replacement based on index: ", e)
|
|
}
|
|
}
|
|
|
|
if (destinationparam.value.includes("$")) {
|
|
try {
|
|
var cnt = -1
|
|
var previous = 0
|
|
while (true) {
|
|
cnt += 1
|
|
// Need to make sure e.g. changing the first here doesn't change the 2nd
|
|
// $change_me
|
|
// $change_me_2
|
|
|
|
const foundindex = destinationparam.value.toLowerCase().indexOf(parsedBaseLabel, previous)
|
|
if (foundindex === previous && foundindex !== 0) {
|
|
break
|
|
}
|
|
|
|
if (foundindex >= 0) {
|
|
previous = foundindex+newname.length
|
|
// Need to add diff of length to word
|
|
|
|
// Check location:
|
|
// If it's a-zA-Z_ then don't replace
|
|
if (destinationparam.value.length > foundindex+parsedBaseLabel.length) {
|
|
const regex = /[a-zA-Z0-9_]/g;
|
|
const match = destinationparam.value[foundindex+parsedBaseLabel.length].match(regex);
|
|
if (match !== null) {
|
|
continue
|
|
}
|
|
}
|
|
|
|
console.log("Old found: ", workflow.branches[key].conditions[subkey].destination.value)
|
|
const extralength = newname.length-parsedBaseLabel.length
|
|
destinationparam.value = destinationparam.value.substring(0, foundindex) + newname + destinationparam.value.substring(foundindex-extralength+newname.length, destinationparam.value.length)
|
|
|
|
console.log("New: ", workflow.branches[key].conditions[subkey].destination.value)
|
|
} else {
|
|
break
|
|
}
|
|
|
|
// Break no matter what after 5 replaces. May need to increase
|
|
if (cnt >= 5) {
|
|
break
|
|
}
|
|
|
|
}
|
|
} catch (e) {
|
|
console.log("Failed value replacement based on index: ", e)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (let [key,keyval] in Object.entries(workflow.actions)) {
|
|
if (workflow.actions[key].id === selectedAction.id) {
|
|
continue
|
|
}
|
|
|
|
const params = workflow.actions[key].parameters
|
|
if (params === null || params === undefined) {
|
|
continue
|
|
}
|
|
|
|
for (let [subkey, subkeyval] in Object.entries(params)) {
|
|
const param = workflow.actions[key].parameters[subkey];
|
|
if (!param.value.includes("$")) {
|
|
continue
|
|
}
|
|
|
|
// Should have a smarter way of discovering node names
|
|
// Do regex?
|
|
// Finding index(es) and replacing at the location
|
|
//
|
|
|
|
try {
|
|
var cnt = -1
|
|
var previous = 0
|
|
while (true) {
|
|
cnt += 1
|
|
// Need to make sure e.g. changing the first here doesn't change the 2nd
|
|
// $change_me
|
|
// $change_me_2
|
|
const foundindex = param.value.toLowerCase().indexOf(parsedBaseLabel, previous)
|
|
if (foundindex === previous && foundindex !== 0) {
|
|
break
|
|
}
|
|
|
|
if (foundindex >= 0) {
|
|
previous = foundindex+newname.length
|
|
// Need to add diff of length to word
|
|
|
|
// Check location:
|
|
// If it's a-zA-Z_ then don't replace
|
|
if (param.value.length > foundindex+parsedBaseLabel.length) {
|
|
const regex = /[a-zA-Z0-9_]/g;
|
|
const match = param.value[foundindex+parsedBaseLabel.length].match(regex);
|
|
if (match !== null) {
|
|
continue
|
|
}
|
|
}
|
|
|
|
const extralength = newname.length-parsedBaseLabel.length
|
|
param.value = param.value.substring(0, foundindex) + newname + param.value.substring(foundindex-extralength+newname.length, param.value.length)
|
|
|
|
} else {
|
|
break
|
|
}
|
|
|
|
// Break no matter what after 5 replaces. May need to increase
|
|
if (cnt >= 5) {
|
|
break
|
|
}
|
|
|
|
}
|
|
} catch (e) {
|
|
console.log("Failed value replacement based on index: ", e)
|
|
}
|
|
}
|
|
}
|
|
|
|
setWorkflow(workflow);
|
|
setUpdate(Math.random());
|
|
setPrevActionName(name)
|
|
}}
|
|
/>
|
|
</div>
|
|
{/*!isCloud ? null :*/}
|
|
<div style={{flex: 1, marginLeft: 5,}}>
|
|
<Tooltip
|
|
color="primary"
|
|
title={"Delay before action executes (in seconds)"}
|
|
placement="top"
|
|
>
|
|
<span>
|
|
<Typography style={{color: "rgba(255,255,255,0.7)"}}>Delay</Typography>
|
|
<TextField
|
|
InputProps={{
|
|
style: theme.palette.innerTextfieldStyle,
|
|
disableUnderline: true,
|
|
}}
|
|
placeholder={selectedAction.execution_delay}
|
|
value={delay}
|
|
onChange={(event) => {
|
|
setDelay(event.target.value)
|
|
}}
|
|
/>
|
|
</span>
|
|
</Tooltip>
|
|
</div>
|
|
{/**/}
|
|
</div>
|
|
</span>
|
|
)}
|
|
|
|
{selectedApp.name !== undefined &&
|
|
selectedAction.authentication !== null &&
|
|
selectedAction.authentication !== undefined &&
|
|
selectedAction.authentication.length === 0 &&
|
|
requiresAuthentication ? (
|
|
<div style={{ marginTop: 15 }}>
|
|
<Tooltip
|
|
color="primary"
|
|
title={"Add authentication option"}
|
|
placement="top"
|
|
>
|
|
<span>
|
|
<Button
|
|
color="primary"
|
|
style={{
|
|
textTransform: "none",
|
|
}}
|
|
fullWidth
|
|
variant="contained"
|
|
onClick={() => {
|
|
//if (authenticationType.type === "oauth2" && authenticationType.redirect_uri !== undefined && authenticationType.redirect_uri !== null) {
|
|
// return null
|
|
//}
|
|
|
|
setAuthenticationModalOpen(true);
|
|
}}
|
|
>
|
|
<AddIcon style={{ marginRight: 10 }} /> Authenticate{" "}
|
|
{selectedApp.name.replaceAll("_", " ")}
|
|
</Button>
|
|
</span>
|
|
</Tooltip>
|
|
</div>
|
|
) : null}
|
|
|
|
{selectedAction.authentication !== undefined &&
|
|
selectedAction.authentication !== null &&
|
|
selectedAction.authentication.length > 0 ? (
|
|
<div style={{ marginTop: 15 }}>
|
|
<Typography style={{color: "rgba(255,255,255,0.7)"}}>Authentication</Typography>
|
|
<div style={{ display: "flex" }}>
|
|
<Select
|
|
MenuProps={{
|
|
disableScrollLock: true,
|
|
}}
|
|
labelId="select-app-auth"
|
|
value={
|
|
selectedAction.authentication_id === "authgroups" ? "authgroups" :
|
|
Object.getOwnPropertyNames(selectedAction.selectedAuthentication).length === 0
|
|
? "No selection"
|
|
: selectedAction.selectedAuthentication
|
|
}
|
|
SelectDisplayProps={{
|
|
style: {
|
|
maxWidth: 250,
|
|
},
|
|
}}
|
|
fullWidth
|
|
onChange={(e) => {
|
|
if (e.target.value === "No selection") {
|
|
selectedAction.selectedAuthentication = {};
|
|
selectedAction.authentication_id = "";
|
|
|
|
for (let [key,keyval] in Object.entries(selectedAction.parameters)) {
|
|
if (selectedAction.parameters[key].configuration === false) {
|
|
//console.log("FIELDSKIP: ", selectedAction.parameters[key].name)
|
|
continue
|
|
}
|
|
|
|
if (selectedAction.parameters[key].name === "url" && authenticationType?.type === "oauth2-app" && selectedAction.parameters[key].value.includes("http")) {
|
|
continue
|
|
}
|
|
|
|
if (selectedAction.parameters[key].example !== undefined && selectedAction.parameters[key].example !== null && selectedAction.parameters[key].example !== "") {
|
|
if (selectedAction.parameters[key].example.toLowerCase().includes("apik") || selectedAction.parameters[key].example.toLowerCase().includes("key") || selectedAction.parameters[key].example.toLowerCase().includes("pass") || selectedAction.parameters[key].example.toLowerCase().includes("****")) {
|
|
selectedAction.parameters[key].value = ""
|
|
} else {
|
|
selectedAction.parameters[key].value = selectedAction.parameters[key].example
|
|
}
|
|
|
|
} else {
|
|
selectedAction.parameters[key].value = ""
|
|
}
|
|
}
|
|
setSelectedAction(selectedAction)
|
|
setUpdate(Math.random())
|
|
|
|
} else if (e.target.value === "authgroups") {
|
|
if (authGroups !== undefined && authGroups !== null && authGroups.length === 0) {
|
|
toast("No auth groups created. Opening window to create one")
|
|
|
|
setTimeout(() => {
|
|
window.open("/admin?tab=app_auth", "_blank")
|
|
}, 2500)
|
|
} else {
|
|
selectedAction.selectedAuthentication = {};
|
|
selectedAction.authentication_id = "authgroups"
|
|
|
|
for (let [key,keyval] in Object.entries(selectedAction.parameters)) {
|
|
//console.log(selectedAction.parameters[key])
|
|
if (selectedAction.parameters[key].configuration) {
|
|
|
|
if (selectedAction.parameters[key].name === "url" && authenticationType?.type === "oauth2-app") {
|
|
} else {
|
|
selectedAction.parameters[key].value = "authgroup controlled"
|
|
}
|
|
}
|
|
}
|
|
|
|
setSelectedAction(selectedAction)
|
|
setUpdate(Math.random())
|
|
}
|
|
} else {
|
|
selectedAction.selectedAuthentication = e.target.value;
|
|
selectedAction.authentication_id = e.target.value.id;
|
|
setSelectedAction(selectedAction)
|
|
setUpdate(Math.random())
|
|
}
|
|
}}
|
|
style={{
|
|
backgroundColor: theme.palette.inputColor,
|
|
color: "white",
|
|
height: 35,
|
|
maxWidth: rightsidebarStyle.maxWidth - 80,
|
|
borderRadius: theme.palette?.borderRadius,
|
|
}}
|
|
>
|
|
<MenuItem
|
|
style={{
|
|
backgroundColor: theme.palette.inputColor,
|
|
color: "white",
|
|
}}
|
|
value="No selection"
|
|
>
|
|
<em>No selection</em>
|
|
</MenuItem>
|
|
{selectedAction.authentication.map((data) => {
|
|
if (data.last_modified === true) {
|
|
//console.log("LAST MODIFIED: ", data.label)
|
|
}
|
|
|
|
return (
|
|
<MenuItem
|
|
key={data.id}
|
|
style={{
|
|
backgroundColor: theme.palette.inputColor,
|
|
color: "white",
|
|
maxWidth: 500,
|
|
overflowX: "auto",
|
|
}}
|
|
value={data}
|
|
>
|
|
|
|
{data?.validation?.valid === true ?
|
|
<Tooltip title="Authentication has been validated" placement="top">
|
|
<Chip
|
|
style={{marginLeft: 0, padding: 0, marginRight: 10, cursor: "pointer", borderColor: green, maxHeight: 25, }}
|
|
label={"Valid"}
|
|
variant="outlined"
|
|
color="secondary"
|
|
/>
|
|
</Tooltip>
|
|
: null }
|
|
{data?.last_modified === true ?
|
|
<Chip
|
|
style={{marginLeft: 0, padding: 0, marginRight: 10, cursor: "pointer", maxHeight: 25, }}
|
|
label={"Latest"}
|
|
variant="outlined"
|
|
color="secondary"
|
|
/>
|
|
: null}
|
|
{/*data.app.app_version !== undefined && data.app.app_version !== null && data.app.app_version !== "" && data.app.app_version !== "undefined" ?
|
|
<Chip
|
|
style={{marginLeft: 0, padding: 0, marginRight: 10, cursor: "pointer",}}
|
|
label={data.app.app_version}
|
|
variant="outlined"
|
|
color="secondary"
|
|
/>
|
|
: null*/}
|
|
{data.label}
|
|
</MenuItem>
|
|
);
|
|
})}
|
|
|
|
<Divider style={{marginTop: 10, marginBottom: 10, }}/>
|
|
|
|
<MenuItem
|
|
style={{
|
|
backgroundColor: theme.palette.inputColor,
|
|
color: "white",
|
|
}}
|
|
value="authgroups"
|
|
>
|
|
<em>Auth Groups</em>
|
|
</MenuItem>
|
|
|
|
</Select>
|
|
|
|
<Tooltip
|
|
color="primary"
|
|
title={"Add authentication option"}
|
|
placement="top"
|
|
>
|
|
<IconButton
|
|
color="primary"
|
|
variant="outlined"
|
|
style={{}}
|
|
onClick={() => {
|
|
setAuthenticationModalOpen(true);
|
|
}}
|
|
>
|
|
<AddIcon />
|
|
</IconButton>
|
|
</Tooltip>
|
|
</div>
|
|
</div>
|
|
) : null}
|
|
|
|
{selectedAction.authentication_id === "authgroups" && (authGroups === undefined || authGroups === null || authGroups.length === 0) ?
|
|
<a href="/admin?tab=app_auth" target="_blank" style={{textDecoration: "none", color: "#FF8544",}}>
|
|
<Typography variant="body2" style={{marginTop: 5,}}>
|
|
Create your first Authentication group
|
|
</Typography>
|
|
</a>
|
|
: null}
|
|
|
|
|
|
{/*showEnvironment !== undefined && showEnvironment && environments.length > 1 && !isIntegration ? (
|
|
<div style={{ marginTop: "20px" }}>
|
|
<Typography style={{color: "rgba(255,255,255,0.7)"}}>Environment</Typography>
|
|
<Select
|
|
MenuProps={{
|
|
disableScrollLock: true,
|
|
}}
|
|
value={
|
|
selectedActionEnvironment === undefined || selectedActionEnvironment === null || selectedActionEnvironment.Name === undefined || selectedActionEnvironment.Name === null ? isCloud ? "Cloud" : "Shuffle" : selectedActionEnvironment.Name
|
|
}
|
|
SelectDisplayProps={{
|
|
style: {
|
|
},
|
|
}}
|
|
fullWidth
|
|
onChange={(e) => {
|
|
const env = environments.find((a) => a.Name === e.target.value);
|
|
setSelectedActionEnvironment(env);
|
|
selectedAction.environment = env.Name;
|
|
setSelectedAction(selectedAction);
|
|
|
|
for (let actionkey in workflow.actions) {
|
|
workflow.actions[actionkey].environment = env.Name
|
|
}
|
|
setWorkflow(workflow)
|
|
toast.success("Set environment for ALL actions to " + env.Name)
|
|
}}
|
|
style={{
|
|
backgroundColor: theme.palette.inputColor,
|
|
color: "white",
|
|
height: "50px",
|
|
borderRadius: theme.palette?.borderRadius,
|
|
}}
|
|
>
|
|
{environments.map((data, index) => {
|
|
if (data.archived === true) {
|
|
return null
|
|
}
|
|
|
|
const isRunning = data.running_ip !== ""
|
|
|
|
return (
|
|
<MenuItem
|
|
key={data.Name}
|
|
style={{
|
|
backgroundColor: theme.palette.inputColor,
|
|
color: "white",
|
|
}}
|
|
value={data.Name}
|
|
>
|
|
|
|
{data.Name === "cloud" || data.Name === "Cloud" ? null : !isRunning ?
|
|
<a href={`/admin?tab=environments&env=${data.Name}`} target="_blank" style={{textDecoration: "none",}}>
|
|
<Tooltip title={"Click to configure the environment"} placement="top">
|
|
<Chip
|
|
style={{marginLeft: 0, padding: 0, marginRight: 10, cursor: "pointer", backgroundColor: red, }}
|
|
label={"Stopped"}
|
|
variant="outlined"
|
|
color="secondary"
|
|
onClick={(e) => {
|
|
e.preventDefault()
|
|
e.stopPropagation()
|
|
window.open(`/admin?tab=environments&env=${data.Name}`, "_blank", "noopener,noreferrer")
|
|
}}
|
|
|
|
|
|
/>
|
|
</Tooltip>
|
|
</a>
|
|
: null}
|
|
|
|
{data.default === true ?
|
|
<Chip
|
|
style={{marginLeft: 0, padding: 0, marginRight: 10, cursor: "pointer",}}
|
|
label={"Default"}
|
|
variant="outlined"
|
|
color="secondary"
|
|
/>
|
|
: null}
|
|
|
|
|
|
{data.Name}
|
|
</MenuItem>
|
|
);
|
|
})}
|
|
</Select>
|
|
|
|
</div>
|
|
) : null*/}
|
|
|
|
{workflow.execution_variables !== undefined && workflow.execution_variables !== null && workflow.execution_variables.length > 0 ? (
|
|
<div style={{ marginTop: "20px" }}>
|
|
<Typography>Execution variable (optional)</Typography>
|
|
<Select
|
|
MenuProps={{
|
|
disableScrollLock: true,
|
|
}}
|
|
value={
|
|
selectedAction.execution_variable !== undefined
|
|
&& selectedAction.execution_variable !== null
|
|
&& selectedAction.execution_variable.name !== undefined
|
|
&& selectedAction.execution_variable.name !== null
|
|
&& selectedAction.execution_variable.name.length > 0
|
|
? selectedAction.execution_variable.name
|
|
: "No selection"
|
|
}
|
|
SelectDisplayProps={{
|
|
style: {
|
|
},
|
|
}}
|
|
fullWidth
|
|
onChange={(e) => {
|
|
if (e.target.value === "No selection") {
|
|
selectedAction.execution_variable = { name: "No selection" };
|
|
} else {
|
|
const value = workflow.execution_variables.find(
|
|
(a) => a.name === e.target.value
|
|
);
|
|
selectedAction.execution_variable = value;
|
|
}
|
|
setSelectedAction(selectedAction);
|
|
setUpdate(Math.random());
|
|
}}
|
|
style={{
|
|
backgroundColor: theme.palette.inputColor,
|
|
color: "white",
|
|
height: "50px",
|
|
borderRadius: theme.palette?.borderRadius,
|
|
}}
|
|
>
|
|
<MenuItem
|
|
style={{
|
|
backgroundColor: theme.palette.inputColor,
|
|
color: "white",
|
|
}}
|
|
value="No selection"
|
|
>
|
|
<em>No selection</em>
|
|
</MenuItem>
|
|
<Divider style={{ backgroundColor: theme.palette.inputColor }} />
|
|
{workflow.execution_variables.map((data) => (
|
|
<MenuItem
|
|
style={{
|
|
backgroundColor: theme.palette.inputColor,
|
|
color: "white",
|
|
}}
|
|
value={data.name}
|
|
>
|
|
{data.name}
|
|
</MenuItem>
|
|
))}
|
|
</Select>
|
|
</div>
|
|
) : null}
|
|
|
|
<Divider
|
|
style={{
|
|
marginTop: "20px",
|
|
height: "1px",
|
|
width: "100%",
|
|
backgroundColor: "rgb(91, 96, 100)",
|
|
}}
|
|
/>
|
|
<div style={{ flex: "6", marginTop: "20px" }}>
|
|
{/*hideExtraTypes ? null :
|
|
<div style={{marginBottom: 5}}>
|
|
<b>Actions</b>
|
|
</div>
|
|
*/}
|
|
|
|
{setNewSelectedAction !== undefined ? (
|
|
<Autocomplete
|
|
id="action_search"
|
|
autoHighlight
|
|
value={selectedAction}
|
|
classes={{ inputRoot: classes.inputRoot }}
|
|
groupBy={(option) => {
|
|
// FIXME: Sorting
|
|
// Most popular
|
|
// Is categorized
|
|
// Uncategorized
|
|
return option.category_label !== undefined && option.category_label !== null && option.category_label.length > 0 ? "Most used" : "All Actions";
|
|
}}
|
|
renderGroup={(params) => {
|
|
|
|
return (
|
|
<li key={params.key}>
|
|
<Typography variant="body1" style={{textAlign: "center", marginLeft: 10, marginTop: 25, marginBottom: 10, }}>{params.group}</Typography>
|
|
<Typography variant="body2">{params.children}</Typography>
|
|
</li>
|
|
)
|
|
}}
|
|
options={renderedActionOptions}
|
|
ListboxProps={{
|
|
style: {
|
|
backgroundColor: theme.palette.surfaceColor,
|
|
color: "white",
|
|
},
|
|
}}
|
|
filterOptions={(options, { inputValue }) => {
|
|
const lowercaseValue = inputValue.toLowerCase()
|
|
options = options.filter(x => x.name.replaceAll("_", " ").toLowerCase().includes(lowercaseValue) || x.description.toLowerCase().includes(lowercaseValue))
|
|
|
|
return options
|
|
}}
|
|
getOptionLabel={(option) => {
|
|
if (option === undefined || option === null || option.name === undefined || option.name === null ) {
|
|
return null;
|
|
}
|
|
|
|
const newname = (
|
|
option.name.charAt(0).toUpperCase() + option.name.substring(1)
|
|
).replaceAll("_", " ");
|
|
|
|
return newname;
|
|
}}
|
|
fullWidth
|
|
sx={{
|
|
'& .MuiOutlinedInput-root': {
|
|
height: 40, // Adjust the input height
|
|
},
|
|
'& .MuiAutocomplete-input': {
|
|
padding: '8px', // Adjust the text padding
|
|
},
|
|
}}
|
|
|
|
style={{
|
|
backgroundColor: theme.palette.backgroundColor,
|
|
height: 35,
|
|
borderRadius: theme.palette?.borderRadius,
|
|
}}
|
|
onChange={(event, newValue) => {
|
|
// Workaround with event lol
|
|
if (newValue !== undefined && newValue !== null) {
|
|
setNewSelectedAction({
|
|
target: {
|
|
value: newValue.name
|
|
}
|
|
});
|
|
}
|
|
}}
|
|
renderOption={(props, option, state) => {
|
|
var newActionname = option.name;
|
|
if (option.label !== undefined && option.label !== null && option.label.length > 0) {
|
|
newActionname = option.label;
|
|
}
|
|
|
|
var newActiondescription = option.description;
|
|
//console.log("DESC: ", newActiondescription)
|
|
if (option.description === undefined || option.description === null) {
|
|
newActiondescription = "Description: No description defined for this action"
|
|
} else {
|
|
newActiondescription = "Description: "+newActiondescription
|
|
}
|
|
|
|
const iconInfo = GetIconInfo({ name: option.name });
|
|
const useIcon = iconInfo.originalIcon;
|
|
|
|
if (newActionname === undefined || newActionname === null) {
|
|
newActionname = "No name"
|
|
option.name = "No name"
|
|
option.label = "No name"
|
|
}
|
|
|
|
newActionname = (newActionname.charAt(0).toUpperCase() + newActionname.substring(1)).replaceAll("_", " ");
|
|
|
|
var method = ""
|
|
var extraDescription = ""
|
|
if (option.name.includes("get_")) {
|
|
method = "GET"
|
|
} else if (option.name.includes("post_")) {
|
|
method = "POST"
|
|
} else if (option.name.includes("put_")) {
|
|
method = "PUT"
|
|
} else if (option.name.includes("patch_")) {
|
|
method = "PATCH"
|
|
} else if (option.name.includes("delete_")) {
|
|
method = "DELETE"
|
|
} else if (option.name.includes("options_")) {
|
|
method = "OPTIONS"
|
|
} else if (option.name.includes("connect_")) {
|
|
method = "CONNECT"
|
|
}
|
|
|
|
// FIXME: Should it require a base URL?
|
|
if (method.length > 0 && option.description !== undefined && option.description !== null && option.description.includes("http")) {
|
|
var extraUrl = ""
|
|
const descSplit = option.description.split("\n")
|
|
// Last line of descSplit
|
|
if (descSplit.length > 0) {
|
|
extraUrl = descSplit[descSplit.length-1]
|
|
}
|
|
|
|
if (extraUrl.length > 0) {
|
|
if (extraUrl.includes(" ")) {
|
|
extraUrl = extraUrl.split(" ")[0]
|
|
}
|
|
|
|
if (extraUrl.includes("#")) {
|
|
extraUrl = extraUrl.split("#")[0]
|
|
}
|
|
|
|
extraDescription = `${method} ${extraUrl}`
|
|
} else {
|
|
//console.log("No url found. Check again :)")
|
|
}
|
|
}
|
|
|
|
return (
|
|
<ActionSelectOption
|
|
{...props}
|
|
option={option}
|
|
newActiondescription={newActiondescription}
|
|
useIcon={useIcon}
|
|
newActionname={newActionname}
|
|
extraDescription={extraDescription}
|
|
/>
|
|
);
|
|
}}
|
|
renderInput={(params) => {
|
|
if (params.inputProps?.value) {
|
|
const prefixes = ["Post", "Put", "Patch"];
|
|
for (let prefix of prefixes) {
|
|
if (params.inputProps.value.startsWith(prefix)) {
|
|
let newValue = params.inputProps.value.replace(prefix + " ", "");
|
|
if (newValue.length > 1) {
|
|
newValue = newValue.charAt(0).toUpperCase() + newValue.substring(1);
|
|
}
|
|
// Set the new value without mutating inputProps
|
|
params = { ...params, inputProps: { ...params.inputProps, value: newValue } };
|
|
break;
|
|
}
|
|
}
|
|
// Check if it starts with "Get List" and method is "Get"
|
|
if (params.inputProps.value.startsWith("Get List")) {
|
|
console.log("Get List");
|
|
}
|
|
}
|
|
|
|
|
|
const actionDescription = null
|
|
|
|
return (
|
|
<Tooltip title={actionDescription}
|
|
placement="right"
|
|
open={!hiddenDescription}
|
|
PopperProps={{
|
|
sx: {
|
|
'& .MuiTooltip-tooltip': {
|
|
backgroundColor: 'transparent',
|
|
boxShadow: 'none',
|
|
},
|
|
'& .MuiTooltip-arrow': {
|
|
color: 'transparent',
|
|
},
|
|
},
|
|
}}
|
|
>
|
|
<TextField
|
|
{...params}
|
|
|
|
data-lpignore="true"
|
|
autocomplete="off"
|
|
dataLPIgnore="true"
|
|
autoComplete="off"
|
|
|
|
color="primary"
|
|
id="checkbox-search"
|
|
variant="body1"
|
|
style={theme.palette.textFieldStyle}
|
|
label={isIntegration ? "Choose a category" : "Find Actions"}
|
|
variant="outlined"
|
|
name={`disable_autocomplete_${Math.random()}`}
|
|
/>
|
|
</Tooltip>
|
|
);
|
|
}}
|
|
/>
|
|
) : null}
|
|
|
|
<div
|
|
style={{
|
|
marginTop: "10px",
|
|
borderColor: "white",
|
|
borderWidth: "2px",
|
|
marginBottom: hideExtraTypes ? 50 : 200,
|
|
}}
|
|
> {
|
|
selectedActionParameters !== undefined && selectedActionParameters !== null && Object.getOwnPropertyNames(selectedAction).length > 0 && selectedActionParameters.length > 0 ?
|
|
<div style={{ marginTop: hideExtraTypes ? 10 : 30 }}>
|
|
{isIntegration ?
|
|
apps !== undefined && apps !== null && apps.length > 0 ?
|
|
<div style={{display: "flex", maxWidth: 335, overflowX: "auto", overflowY: "hidden",}}>
|
|
<div onClick={() => {
|
|
|
|
selectedAction.example = "noapp"
|
|
selectedAction.large_image = newimage
|
|
if (cy !== undefined && cy !== null) {
|
|
const foundnode = cy.getElementById(selectedAction.id)
|
|
if (foundnode !== undefined && foundnode !== null) {
|
|
foundnode.data("large_image", newimage)
|
|
}
|
|
}
|
|
|
|
const iconInfo = GetIconInfo(selectedAction)
|
|
if (iconInfo !== undefined && iconInfo !== null) {
|
|
selectedAction.fillGradient = iconInfo.fillGradient
|
|
|
|
selectedAction.iconBackground = iconInfo.iconBackgroundColor
|
|
selectedAction.fillstyle = "linear-gradient"
|
|
}
|
|
|
|
if (paramIndex === -1) {
|
|
console.log("Couldn't find app_name parameter")
|
|
selectedAction.parameters.push({
|
|
name: "app_name",
|
|
value: wrapperapp.name,
|
|
autocompleted: false,
|
|
})
|
|
} else {
|
|
selectedAction.parameters[paramIndex].value = wrapperapp.name
|
|
}
|
|
|
|
setSelectedAction(selectedAction)
|
|
setUpdate(Math.random())
|
|
|
|
}}>
|
|
<Tooltip title={"Unselect which App to use"} placement="top">
|
|
<div style={{textAlign: "center", }}>
|
|
<img
|
|
src={wrapperapp.large_image}
|
|
style={{
|
|
minWidth: 30,
|
|
maxWidth: 30,
|
|
minHeight: 30,
|
|
maxHeight: 30,
|
|
|
|
marginRight: 5,
|
|
borderRadius: 5,
|
|
cursor: "pointer",
|
|
border: noAppSelected ? "3px solid #86c142" : "2px solid rgba(255,255,255,0.6)",
|
|
|
|
paddingLeft: 5,
|
|
paddingTop: 5,
|
|
}} />
|
|
</div>
|
|
</Tooltip>
|
|
</div>
|
|
{apps.map((app, appIndex) => {
|
|
if (app.categories === undefined || app.categories === null || app.categories.length === 0) {
|
|
return null
|
|
}
|
|
|
|
var found = false
|
|
|
|
|
|
for (var key in app.categories) {
|
|
if (app.categories[key].toLowerCase() !== actionname) {
|
|
continue
|
|
}
|
|
|
|
found = true
|
|
break
|
|
}
|
|
|
|
if (!found) {
|
|
return null
|
|
}
|
|
|
|
var isAppSelected = false
|
|
const paramIndex = selectedAction.parameters.findIndex((param) => param.name === "app_name")
|
|
if (paramIndex > -1) {
|
|
// Check the actual value and if it's the same
|
|
if (selectedAction.parameters[paramIndex].value === app.name) {
|
|
isAppSelected = true
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div onClick={() => {
|
|
selectedAction.example = ""
|
|
selectedAction.large_image = app.large_image
|
|
if (cy !== undefined && cy !== null) {
|
|
const foundnode = cy.getElementById(selectedAction.id)
|
|
if (foundnode !== undefined && foundnode !== null) {
|
|
foundnode.data("large_image", app.large_image)
|
|
}
|
|
}
|
|
|
|
if (paramIndex === -1) {
|
|
console.log("Couldn't find app_name parameter")
|
|
selectedAction.parameters.push({
|
|
name: "app_name",
|
|
value: app.name,
|
|
autocompleted: false,
|
|
})
|
|
} else {
|
|
selectedAction.parameters[paramIndex].value = app.name
|
|
}
|
|
|
|
setSelectedAction(selectedAction)
|
|
setUpdate(Math.random())
|
|
|
|
}}>
|
|
<Tooltip title={`Select ${app.name.replaceAll("_", " ")}`} placement="top">
|
|
<img
|
|
src={app.large_image}
|
|
style={{
|
|
width: 35,
|
|
height: 35,
|
|
marginRight: 5,
|
|
borderRadius: 5,
|
|
cursor: "pointer",
|
|
border: isAppSelected ? "3px solid #86c142" : "2px solid rgba(255,255,255,0.6)",
|
|
}} />
|
|
</Tooltip>
|
|
</div>
|
|
)
|
|
})}
|
|
</div>
|
|
: null
|
|
:
|
|
<Tooltip
|
|
color="secondary"
|
|
title={"Click to learn more about this action"}
|
|
placement="top"
|
|
>
|
|
<div style={{marginTop: 50, }} />
|
|
{/*
|
|
<Button
|
|
variant="text"
|
|
color="secondary"
|
|
style={{
|
|
justifyContent: "flex-start",
|
|
textAlign: "left",
|
|
textTransform: "none",
|
|
width: "100%",
|
|
marginTop: 15,
|
|
}}
|
|
fullWidth
|
|
disabled={selectedAction.description === undefined || selectedAction.description === null || selectedAction.description.length === 0}
|
|
onClick={() => {
|
|
setHiddenParameters(!hiddenParameters)
|
|
}}
|
|
>
|
|
<b>Parameters</b>
|
|
</Button>
|
|
*/}
|
|
</Tooltip>
|
|
}
|
|
|
|
{selectedAction.template === true && selectedAction.matching_actions !== undefined && selectedAction.matching_actions !== null && selectedAction.matching_actions.length > 0 ?
|
|
<div>
|
|
<Typography variant="body1">
|
|
Select an app you want to use
|
|
</Typography>
|
|
<Autocomplete
|
|
id="template_action_search"
|
|
autoHighlight
|
|
value={selectedAction}
|
|
classes={{ inputRoot: classes.inputRoot }}
|
|
ListboxProps={{
|
|
style: {
|
|
backgroundColor: theme.palette.inputColor,
|
|
color: "white",
|
|
},
|
|
}}
|
|
getOptionLabel={(option) => {
|
|
console.log("LABEL: ", option)
|
|
if (
|
|
option === undefined ||
|
|
option === null ||
|
|
option.app_name === undefined ||
|
|
option.app_name === null
|
|
) {
|
|
return null;
|
|
}
|
|
|
|
const newname = (
|
|
option.app_name.charAt(0).toUpperCase() + option.app_name.substring(1)
|
|
).replaceAll("_", " ");
|
|
return newname;
|
|
}}
|
|
options={selectedAction.matching_actions}
|
|
fullWidth
|
|
style={{
|
|
backgroundColor: theme.palette.inputColor,
|
|
height: 35,
|
|
borderRadius: theme.palette?.borderRadius,
|
|
}}
|
|
onChange={(event, newValue) => {
|
|
console.log("SELECT: ", event, newValue)
|
|
// Workaround with event lol
|
|
//if (newValue !== undefined && newValue !== null) {
|
|
// setNewSelectedAction({ target: { value: newValue.name } });
|
|
//}
|
|
}}
|
|
renderOption={(props, data, state) => {
|
|
var newActionname = data.app_name;
|
|
if (
|
|
data.label !== undefined &&
|
|
data.label !== null &&
|
|
data.label.length > 0
|
|
) {
|
|
newActionname = data.label;
|
|
}
|
|
|
|
const iconInfo = GetIconInfo({ name: data.app_name });
|
|
const useIcon = iconInfo.originalIcon;
|
|
|
|
console.log("Actionname 1: ", newActionname)
|
|
|
|
newActionname = (
|
|
newActionname.charAt(0).toUpperCase() +
|
|
newActionname.substring(1)
|
|
).replaceAll("_", " ");
|
|
|
|
return (
|
|
<div style={{ display: "flex" }}>
|
|
<span
|
|
style={{
|
|
marginRight: 10,
|
|
marginTop: "auto",
|
|
marginBottom: "auto",
|
|
}}
|
|
>
|
|
{useIcon}
|
|
</span>
|
|
<span style={{}}>{newActionname}</span>
|
|
</div>
|
|
);
|
|
}}
|
|
renderInput={(params) => {
|
|
if (params.inputProps !== undefined && params.inputProps !== null && params.inputProps.value !== undefined && params.inputProps.value !== null) {
|
|
const prefixes = ["Post", "Put", "Patch"]
|
|
for (let [key,keyval] in Object.entries(prefixes)) {
|
|
if (params.inputProps.value.startsWith(prefixes[key])) {
|
|
params.inputProps.value = params.inputProps.value.replace(prefixes[key]+" ", "", -1)
|
|
if (params.inputProps.value.length > 1) {
|
|
params.inputProps.value = params.inputProps.value.charAt(0).toUpperCase()+params.inputProps.value.substring(1)
|
|
}
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
return (
|
|
<TextField
|
|
style={{
|
|
backgroundColor: theme.palette.inputColor,
|
|
borderRadius: theme.palette?.borderRadius,
|
|
}}
|
|
{...params}
|
|
label="Find App to Translate"
|
|
variant="outlined"
|
|
/>
|
|
);
|
|
}}
|
|
/>
|
|
</div>
|
|
: null}
|
|
{selectedAction.description !== undefined && selectedAction.description !== null && selectedAction.description.length > 0 && hiddenParameters === false ? (
|
|
<div
|
|
style={{
|
|
border: "1px solid rgba(255,255,255,0.6)",
|
|
borderRadius: theme.palette?.borderRadius,
|
|
marginTop: 15,
|
|
marginBottom: 10,
|
|
maxHeight: 70,
|
|
overflow: "auto",
|
|
padding: 15,
|
|
}}
|
|
>
|
|
<Typography style={{}}>
|
|
<b>Description</b>
|
|
</Typography>
|
|
<Typography style={{}}>
|
|
{selectedAction.description}
|
|
</Typography>
|
|
</div>
|
|
) : null}
|
|
|
|
{suggestionInfo()}
|
|
{selectedActionParameters?.map((data, count) => {
|
|
if (data.variant === "") {
|
|
data.variant = "STATIC_VALUE";
|
|
}
|
|
|
|
if (isIntegration && data.name === "app_name") {
|
|
return null
|
|
}
|
|
|
|
if (data.value === "authgroup controlled") {
|
|
if (data?.name === "url" && authenticationType?.type === "oauth2-app") {
|
|
} else {
|
|
return null
|
|
}
|
|
}
|
|
|
|
if (selectedAction.parameters === undefined || selectedAction.parameters === null || selectedAction.parameters.length !== selectedActionParameters.length) {
|
|
|
|
//selectedAction.parameters = selectedActionParameters
|
|
//console.log("PARAM BUG - length change(?): ", selectedAction)
|
|
}
|
|
|
|
//!selectedAction.auth_not_required &&
|
|
if (selectedAction.selectedAuthentication !== undefined && selectedAction.selectedAuthentication.fields !== undefined && selectedAction.selectedAuthentication.fields[data.name] !== undefined) {
|
|
|
|
// This sets the placeholder in the frontend. (Replaced in backend)
|
|
if (selectedActionParameters[count] !== undefined) {
|
|
selectedActionParameters[count].value = selectedAction.selectedAuthentication.fields[data.name]
|
|
}
|
|
|
|
if (selectedAction.parameters[count] !== undefined) {
|
|
selectedAction.parameters[count].value = selectedAction.selectedAuthentication.fields[data.name]
|
|
}
|
|
|
|
setSelectedAction(selectedAction);
|
|
//setUpdate(Math.random())
|
|
|
|
if (authWritten) {
|
|
return null;
|
|
}
|
|
|
|
authWritten = true;
|
|
return (
|
|
<Typography
|
|
key={count}
|
|
id="skip_auth"
|
|
variant="body2"
|
|
color="textSecondary"
|
|
style={{ marginTop: 5 }}
|
|
>
|
|
Authentication fields are hidden
|
|
</Typography>
|
|
);
|
|
}
|
|
|
|
|
|
// Added autofill to make this ALOT simpler
|
|
if (isCloud && (selectedAction.app_name === "Shuffle Tools" || selectedAction.app_name === "email") && (selectedAction.name === "send_email_shuffle" || selectedAction.name === "send_sms_shuffle") && data.name === "apikey") {
|
|
if (selectedActionParameters[count].length === 0) {
|
|
selectedAction.parameters[count].value = "TMP: Will be replaced during execution if cloud"
|
|
setSelectedAction(selectedAction)
|
|
}
|
|
|
|
return null
|
|
}
|
|
|
|
var staticcolor = "inherit";
|
|
var actioncolor = "inherit";
|
|
var varcolor = "inherit";
|
|
var multiline
|
|
if (
|
|
data.multiline !== undefined &&
|
|
data.multiline !== null &&
|
|
data.multiline === true
|
|
) {
|
|
multiline = true;
|
|
}
|
|
|
|
// make data.value from array to comma separated string if it is an array
|
|
if (data.value !== undefined && data.value !== null && Array.isArray(data.value)) {
|
|
data.value = data.value.join(",")
|
|
}
|
|
|
|
if (data.value !== undefined && data.value !== null &&
|
|
data.value.startsWith("{") && data.value.endsWith("}")) {
|
|
multiline = true
|
|
}
|
|
|
|
var placeholder = "Value";
|
|
if (data.example !== undefined && data.example !== null && data.example.length > 0) {
|
|
|
|
placeholder = data.example;
|
|
|
|
// if (data.name === "url") {
|
|
// data.value = data.example;
|
|
// }
|
|
// In case of data.example
|
|
if (data.value === undefined || data.value === null) {
|
|
data.value = ""
|
|
}
|
|
|
|
if (data.value.length === 0) {
|
|
if (data.name.toLowerCase() === "headers") {
|
|
//console.log("Should show headers field instead with + and -!")
|
|
|
|
// Check if file ID exists
|
|
//
|
|
const fileFound = selectedActionParameters.find(param => param.name === "file_id")
|
|
if (fileFound === undefined || fileFound === null) {
|
|
data.value = data.example
|
|
} else {
|
|
// Purposely unset it if set by default when using files
|
|
data.value = ""
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
if (data.name !== "queries" && data.name !== "key" && data.name !== "value" ) {
|
|
data.value = data.example
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
|
|
if (selectedAction.name === "custom_action" && data.name === "body") {
|
|
for (var key in selectedActionParameters) {
|
|
const param = selectedActionParameters[key]
|
|
if (param.name === "method") {
|
|
if (param.value === "GET") {
|
|
return null
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (data.name.startsWith("${") && data.name.endsWith("}")) {
|
|
const paramcheck = selectedAction.parameters.find((param) => param.name === "body");
|
|
|
|
|
|
if (paramcheck !== undefined && paramcheck !== null) {
|
|
if (
|
|
paramcheck["value_replace"] !== undefined &&
|
|
paramcheck["value_replace"] !== null
|
|
) {
|
|
//console.log("IN THE VALUE REPLACE: ", paramcheck["value_replace"])
|
|
const subparamindex = paramcheck["value_replace"].findIndex(
|
|
(param) => param.key === data.name
|
|
);
|
|
if (subparamindex !== -1) {
|
|
data.value =
|
|
paramcheck["value_replace"][subparamindex]["value"];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
var showCacheConfig = false
|
|
if (data.name === "key" && selectedAction.name.includes("cache") && selectedAction.app_name === "Shuffle Tools") {
|
|
// Show a key popout button
|
|
showCacheConfig = true
|
|
}
|
|
|
|
var disabled = false;
|
|
var rows = "3";
|
|
var openApiHelperText = "This is an OpenAPI specific field";
|
|
|
|
|
|
if (selectedApp.generated && data.name === "headers") {
|
|
//console.log("HEADER: ", data)
|
|
//if (data.value.length === 0) {
|
|
//}
|
|
//setSelectedActionParameters(selectedActionParameters)
|
|
}
|
|
|
|
var hideBodyButtonValue = (
|
|
<div
|
|
key={data.name}
|
|
id="hide_body_button"
|
|
style={{
|
|
marginTop: 30,
|
|
borderRadius: theme.palette?.borderRadius,
|
|
alignItems: "center",
|
|
textAlign: "center",
|
|
}}
|
|
>
|
|
<Tabs
|
|
value={hideBody === true ? 1 : 0}
|
|
indicatorColor="primary"
|
|
style={{
|
|
width: "100%",
|
|
display: "flex",
|
|
textAlign: "center",
|
|
textTransform: "none",
|
|
}}
|
|
>
|
|
<Tab
|
|
label={
|
|
"Simple"
|
|
}
|
|
style={{
|
|
flex: 1,
|
|
textTransform: "none",
|
|
borderBottom: "1px solid rgba(255,255,255,0.3)",
|
|
}}
|
|
onClick={() => {
|
|
// Set localstorage
|
|
localStorage.setItem("hideBody", "true")
|
|
|
|
setHideBody(false)
|
|
const updatedParameters = selectedActionParameters.map((param) => {
|
|
if (param.name === "body") {
|
|
return {
|
|
...param,
|
|
id: "UNTOGGLED",
|
|
}
|
|
}
|
|
|
|
if (param.description === openApiFieldDesc) {
|
|
// Check required fields here
|
|
if (selectedAction.required_body_fields !== undefined && selectedAction.required_body_fields !== null && selectedAction.required_body_fields.length > 0) {
|
|
// Look for the field name in the required_body_fields
|
|
if (selectedAction.required_body_fields.includes(param.name)) {
|
|
param.required = true
|
|
} else {
|
|
param.required = false
|
|
}
|
|
}
|
|
|
|
return { ...param, field_active: true }
|
|
}
|
|
|
|
return param
|
|
})
|
|
|
|
setSelectedActionParameters(updatedParameters)
|
|
}}
|
|
/>
|
|
<Tab
|
|
label={
|
|
"Advanced"
|
|
}
|
|
style={{
|
|
flex: 1,
|
|
textTransform: "none",
|
|
borderBottom: "1px solid rgba(255,255,255,0.3)",
|
|
}}
|
|
onClick={() => {
|
|
localStorage.setItem("hideBody", "false")
|
|
setHideBody(true)
|
|
// Make sure the body field is shown
|
|
const updatedParameters = selectedActionParameters.map((param) => {
|
|
if (param.name === "body") {
|
|
return {
|
|
...param,
|
|
id: "TOGGLED",
|
|
}
|
|
}
|
|
|
|
if (param.description === openApiFieldDesc) {
|
|
return { ...param, field_active: false }
|
|
}
|
|
|
|
return param
|
|
})
|
|
|
|
setSelectedActionParameters(updatedParameters)
|
|
}}
|
|
/>
|
|
</Tabs>
|
|
{/*
|
|
<ButtonGroup fullWidth>
|
|
<Tooltip
|
|
color="secondary"
|
|
title={
|
|
"Show all body fields instead of the body itself"
|
|
}
|
|
placement="top"
|
|
>
|
|
<Button
|
|
variant={hideBody === true ? "outlined" : "contained"}
|
|
color="primary"
|
|
style={{textTransform: "none",}}
|
|
onClick={() => {
|
|
// Set localstorage
|
|
localStorage.setItem("hideBody", "true")
|
|
|
|
setHideBody(false)
|
|
const updatedParameters = selectedActionParameters.map((param) => {
|
|
if (param.name === "body") {
|
|
return {
|
|
...param,
|
|
id: "UNTOGGLED",
|
|
}
|
|
}
|
|
|
|
if (param.description === openApiFieldDesc) {
|
|
// Check required fields here
|
|
if (selectedAction.required_body_fields !== undefined && selectedAction.required_body_fields !== null && selectedAction.required_body_fields.length > 0) {
|
|
// Look for the field name in the required_body_fields
|
|
if (selectedAction.required_body_fields.includes(param.name)) {
|
|
param.required = true
|
|
} else {
|
|
param.required = false
|
|
}
|
|
}
|
|
|
|
return { ...param, field_active: true }
|
|
}
|
|
|
|
return param
|
|
})
|
|
|
|
setSelectedActionParameters(updatedParameters)
|
|
}}>
|
|
Simplified
|
|
</Button>
|
|
</Tooltip>
|
|
<Tooltip
|
|
color="secondary"
|
|
title={
|
|
"Show the body as it is"
|
|
}
|
|
placement="top"
|
|
>
|
|
<Button
|
|
variant={hideBody === true ? "contained" : "outlined" }
|
|
color="primary"
|
|
style={{textTransform: "none",}}
|
|
onClick={() => {
|
|
localStorage.setItem("hideBody", "false")
|
|
setHideBody(true)
|
|
// Make sure the body field is shown
|
|
const updatedParameters = selectedActionParameters.map((param) => {
|
|
if (param.name === "body") {
|
|
return {
|
|
...param,
|
|
id: "TOGGLED",
|
|
}
|
|
}
|
|
|
|
if (param.description === openApiFieldDesc) {
|
|
return { ...param, field_active: false }
|
|
}
|
|
|
|
return param
|
|
})
|
|
|
|
setSelectedActionParameters(updatedParameters)
|
|
}}>
|
|
Advanced
|
|
</Button>
|
|
</Tooltip>
|
|
</ButtonGroup>
|
|
*/}
|
|
</div>
|
|
)
|
|
|
|
var showButtonField = false
|
|
if (selectedApp.generated === true && data.name === "body") {
|
|
const regex = /\${(\w+)}/g;
|
|
const found = placeholder.match(regex);
|
|
|
|
var newhidebody = hideBody
|
|
showButtonField = true
|
|
if (found === undefined || found === null || found.length === 0) {
|
|
newhidebody = false
|
|
hideBodyButtonValue = null
|
|
|
|
if (hideBody === false) {
|
|
setHideBody(true)
|
|
}
|
|
}
|
|
|
|
if (newhidebody === true) {
|
|
//toast("BODYBUTTON TRUE")
|
|
} else {
|
|
|
|
rows = "1";
|
|
disabled = true;
|
|
openApiHelperText = "OpenAPI spec: fill the following fields.";
|
|
|
|
var changed = false;
|
|
var tempArray = []
|
|
for (let specKey in found) {
|
|
const tmpitem = found[specKey];
|
|
var skip = false;
|
|
|
|
for (let innerkey in selectedActionParameters) {
|
|
if (selectedActionParameters[innerkey].name === tmpitem) {
|
|
skip = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (skip) {
|
|
//console.log("SKIPPING ", tmpitem)
|
|
continue;
|
|
}
|
|
|
|
changed = true;
|
|
var isRequired = false
|
|
// Check if original field name is in the selectedAction.required_body_fields
|
|
if (selectedAction.required_body_fields !== undefined && selectedAction.required_body_fields !== null) {
|
|
for (let innerkey in selectedAction.required_body_fields) {
|
|
if (selectedAction.required_body_fields[innerkey] === tmpitem) {
|
|
isRequired = true
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
tempArray.push({
|
|
action_field: "",
|
|
configuration: false,
|
|
description: openApiFieldDesc,
|
|
example: "",
|
|
id: "",
|
|
multiline: false,
|
|
name: tmpitem,
|
|
options: null,
|
|
required: isRequired,
|
|
schema: { type: "string" },
|
|
skip_multicheck: false,
|
|
tags: null,
|
|
value: "",
|
|
variant: "STATIC_VALUE",
|
|
field_active: true,
|
|
|
|
autocompleted: false,
|
|
});
|
|
}
|
|
|
|
var required = selectedActionParameters.filter(item => item.required === true)
|
|
var notRequired = selectedActionParameters.filter(item => item.required === false)
|
|
|
|
if (tempArray.length > 0) {
|
|
// Sort tempArray based on tempArray.required
|
|
tempArray.sort((a, b) => (a.required < b.required) ? 1 : -1)
|
|
// Add all items to the selectedActionParameters array
|
|
for (let innerkey in tempArray) {
|
|
tempArray[innerkey].id = "ADDED"
|
|
|
|
if (tempArray[innerkey].required === true) {
|
|
required.push(tempArray[innerkey])
|
|
} else {
|
|
notRequired.push(tempArray[innerkey])
|
|
}
|
|
}
|
|
}
|
|
|
|
if (changed) {
|
|
// Sort selectedActionParameters based on selectedActionParameters.required
|
|
// Find the "headers" and "queries" field names and put them on the first indexes anyway
|
|
var newArray = required.concat(notRequired)
|
|
|
|
|
|
setSelectedActionParameters(newArray)
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
const clickedFieldId = "rightside_field_" + count;
|
|
|
|
var baseHelperText = ""
|
|
if (data !== undefined && data !== null && data.value !== undefined && data.value !== null && data.value.length > 0) {
|
|
baseHelperText = calculateHelpertext(data.value)
|
|
}
|
|
|
|
var tmpitem = data.name.valueOf();
|
|
if (data.name.startsWith("${") && data.name.endsWith("}")) {
|
|
tmpitem = tmpitem.slice(2, data.name.length - 1);
|
|
}
|
|
|
|
if (tmpitem === "from_shuffle") {
|
|
tmpitem = "from"
|
|
}
|
|
|
|
tmpitem = (
|
|
tmpitem.charAt(0).toUpperCase() + tmpitem.substring(1)
|
|
).replaceAll("_", " ");
|
|
|
|
if (tmpitem === "Username basic") {
|
|
tmpitem = "Username"
|
|
} else if (tmpitem === "Password basic") {
|
|
tmpitem = "Password"
|
|
}
|
|
|
|
// No longer multiline for new fields
|
|
//multiline = data.name.startsWith("${") && data.name.endsWith("}") ? true : multiline
|
|
|
|
if (data.name === "body") {
|
|
//console.log("BODY: ", data)
|
|
if (hideBody === false) {
|
|
return hideBodyButtonValue
|
|
}
|
|
|
|
rows = "4"
|
|
multiline = true
|
|
disabled = false
|
|
}
|
|
|
|
const description = data.description === undefined ? "" : data?.description;
|
|
|
|
const tooltipDescription = (
|
|
<Box
|
|
p={1.5}
|
|
borderRadius={3}
|
|
boxShadow={2}
|
|
backgroundColor={theme.palette.textFieldStyle}
|
|
display="flex"
|
|
flexDirection="column"
|
|
>
|
|
<Box display="flex" alignItems="center" justifyContent="space-between">
|
|
<Typography variant="body1" style={{ flexGrow: 1 }}>
|
|
{tmpitem.charAt(0).toUpperCase() + tmpitem.slice(1)}
|
|
</Typography>
|
|
<IconButton size="small"
|
|
onClick={() => {
|
|
setUiBox("closed")
|
|
}}
|
|
>
|
|
<CloseIcon fontSize="small" />
|
|
</IconButton>
|
|
</Box>
|
|
<Divider sx={{ backgroundColor: theme.palette.surfaceColor, marginTop: "5px", marginBottom : "10px", height: "3px" }}/>
|
|
<Box display="flex" flexDirection="column">
|
|
<Typography variant="body2" mb={0.5}>
|
|
<strong>Required:</strong> {data.required === true || data.configuration === true ? "True" : "False"}
|
|
</Typography>
|
|
<Typography variant="body2" mb={0.5}>
|
|
<strong>Description:</strong> {description}
|
|
</Typography>
|
|
<Typography variant="body2">
|
|
<strong>Ex. :</strong> {data?.example?.length > 0 ? data.example : "No example available"}
|
|
</Typography>
|
|
{
|
|
data?.configuration === true ?
|
|
(
|
|
<Typography variant="body2" mt={0.5}>
|
|
<strong>Auth: </strong>Use "\$" instead of "$"
|
|
</Typography>
|
|
) : null
|
|
}
|
|
|
|
<div onClick={(e) => {
|
|
e.preventDefault()
|
|
e.stopPropagation()
|
|
|
|
localStorage.setItem("disabled_ui_box", "true")
|
|
setUiBox("closed")
|
|
}}>
|
|
<Typography style={{marginTop: 10, color: "#FF8544", cursor: "pointer",}} variant="body2">
|
|
Don't show again
|
|
</Typography>
|
|
</div>
|
|
</Box>
|
|
</Box>
|
|
);
|
|
|
|
|
|
var datafield = (
|
|
<Tooltip
|
|
title={tooltipDescription}
|
|
placement="right"
|
|
open={clickedFieldId === uiBox}
|
|
style={{
|
|
zIndex: 0,
|
|
}}
|
|
PopperProps={{
|
|
sx: {
|
|
'& .MuiTooltip-tooltip': {
|
|
backgroundColor: 'transparent',
|
|
boxShadow: 'none',
|
|
},
|
|
'& .MuiTooltip-arrow': {
|
|
color: 'transparent',
|
|
},
|
|
},
|
|
}}
|
|
>
|
|
<TextField
|
|
autofill="off"
|
|
autoComplete="off"
|
|
id={clickedFieldId}
|
|
disabled={disabled}
|
|
style={{
|
|
backgroundColor: theme.palette.inputColor,
|
|
borderRadius: theme.palette?.borderRadius,
|
|
width: "100%",
|
|
maxHeight: multiline === true ? undefined : 40,
|
|
minHeight: 40,
|
|
|
|
border:
|
|
selectedActionParameters[count].required ||
|
|
selectedActionParameters[count].configuration
|
|
? "1px solid #FF8544"
|
|
: "",
|
|
}}
|
|
InputProps={{
|
|
style: {
|
|
maxHeight: multiline === true ? undefined : 40,
|
|
},
|
|
disableUnderline: true,
|
|
endAdornment: hideExtraTypes ? null : (
|
|
<InputAdornment position="end">
|
|
<ButtonGroup color="secondary" orientation={multiline ? "vertical" : "horizontal"}>
|
|
<Tooltip title="Autocomplete text" placement="bottom">
|
|
<AddCircleOutlineIcon
|
|
style={{ color: "rgba(255,255,255,0.7)", cursor: "pointer", margin: multiline ? 5 : 0, }}
|
|
onClick={(event) => {
|
|
event.preventDefault()
|
|
|
|
// Get cursor position
|
|
// This makes it so we can put it in the right location?
|
|
setMenuPosition({
|
|
top: event.pageY + 10,
|
|
left: event.pageX + 10,
|
|
});
|
|
setShowDropdownNumber(count);
|
|
setShowDropdown(true);
|
|
setShowAutocomplete(true);
|
|
}}
|
|
/>
|
|
</Tooltip>
|
|
</ButtonGroup>
|
|
</InputAdornment>
|
|
),
|
|
}}
|
|
multiline={multiline}
|
|
onClick={() => {
|
|
/*
|
|
setExpansionModalOpen(false);
|
|
*/
|
|
|
|
if (setScrollConfig !== undefined && scrollConfig !== null && scrollConfig !== undefined && scrollConfig.selected !== clickedFieldId) {
|
|
|
|
scrollConfig.selected = clickedFieldId
|
|
setScrollConfig(scrollConfig)
|
|
}
|
|
}}
|
|
rows={data.name.startsWith("${") && data.name.endsWith("}") ? 2 : rows}
|
|
color="primary"
|
|
// defaultValue={data.value}
|
|
value={
|
|
data?.value
|
|
}
|
|
error={
|
|
data?.error?.length > 0 ? true : false
|
|
}
|
|
helperText={data?.error?.length > 0 ? errorHelperText(data?.name,data?.value,data?.error) : returnHelperText(data.name, data.value)}
|
|
//options={{
|
|
// theme: 'gruvbox-dark',
|
|
// keyMap: 'sublime',
|
|
// mode: 'python',
|
|
//}}
|
|
//height={multiline ? 50 : 150}
|
|
type={
|
|
placeholder.includes("***") ||
|
|
(data.configuration &&
|
|
(data.name.toLowerCase().includes("api") ||
|
|
data.name.toLowerCase().includes("key") ||
|
|
data.name.toLowerCase().includes("pass")))
|
|
? "password"
|
|
: "text"
|
|
}
|
|
placeholder={placeholder}
|
|
onChange={(event) => {
|
|
//changeActionParameterCodemirror(event, count, data)
|
|
// changeActionParameter(event, count, data);
|
|
handleParamChange(event, count, data)
|
|
}}
|
|
onFocus={(event) => {
|
|
// Get local storage key "disabled_ui_box" and check if it's true
|
|
const disabledUiBox = localStorage.getItem("disabled_ui_box")
|
|
if (disabledUiBox === "true") {
|
|
} else {
|
|
//setUiBox(event.target.id)
|
|
}
|
|
}}
|
|
onBlur={(event) => {
|
|
baseHelperText = calculateHelpertext(event.target.value)
|
|
if (setLastSaved !== undefined) {
|
|
setLastSaved(false)
|
|
}
|
|
|
|
// Check if we clicked the tooltip or not
|
|
const tooltipid = "rightside_field_tooltip" + count
|
|
const foundElement = document.getElementById(tooltipid)
|
|
if (foundElement !== null && foundElement !== undefined) {
|
|
console.log("FOUND: ", foundElement)
|
|
} else {
|
|
//console.log("TOOLTIP -> NOT FOUND")
|
|
//setUiBox("closed")
|
|
}
|
|
}}
|
|
/>
|
|
</Tooltip>
|
|
);
|
|
|
|
// Finds headers from a string to be used for autocompletion
|
|
const findHeaders = (inputdata) => {
|
|
var splitdata = inputdata.split("\n")
|
|
|
|
var foundnewline = false
|
|
var allValues = []
|
|
for (let [key,keyval] in Object.entries(splitdata)) {
|
|
const line = splitdata[key]
|
|
if (line === "") {
|
|
foundnewline = true
|
|
continue
|
|
}
|
|
|
|
var splitvalue = ""
|
|
if (line.includes(":")) {
|
|
splitvalue = ":"
|
|
}
|
|
|
|
if (line.includes("=")) {
|
|
splitvalue = "="
|
|
}
|
|
|
|
if (splitvalue.length === 0){
|
|
allValues.push({
|
|
key: line,
|
|
value: "",
|
|
})
|
|
continue
|
|
}
|
|
|
|
var splitKeys = line.split(splitvalue)
|
|
if (splitKeys.length > 1) {
|
|
allValues.push({
|
|
key: splitKeys[0].trim(),
|
|
value: splitKeys[1].trim(),
|
|
})
|
|
} else {
|
|
console.log("No keys for ", line)
|
|
}
|
|
}
|
|
|
|
// Just add one
|
|
if (foundnewline) {
|
|
allValues.push({
|
|
key: "",
|
|
value: "",
|
|
})
|
|
}
|
|
|
|
return allValues
|
|
}
|
|
|
|
if (data.name.toLowerCase() === "headers") {
|
|
//var tmpheaders = findHeaders(data.value)
|
|
var tmpheaders = findHeaders(selectedActionParameters[count].value)
|
|
const tmpdatafield =
|
|
<div>
|
|
{tmpheaders.map((inputdata, index) => {
|
|
const oldkey = inputdata.key
|
|
const oldval = inputdata.value
|
|
|
|
return (
|
|
<span key={index}>
|
|
<div style={{display: "flex"}}>
|
|
<TextField
|
|
placeholder="Key"
|
|
style={{flex: 5}}
|
|
defaultValue={inputdata.key}
|
|
onBlur={(e) => {
|
|
console.log("Change from oldkey to new: ", oldkey, e.target.value)
|
|
|
|
// Find the right line to replace!
|
|
//const newval = selectedActionParameters[count].value.replace(oldval, e.target.value, 1)
|
|
const tmpsplit = selectedActionParameters[count].value.split("\n")
|
|
var valsplit = []
|
|
var add_empty = false
|
|
for (let [key,keyval] in Object.entries(tmpsplit)) {
|
|
if (tmpsplit[key] === "") {
|
|
add_empty = true
|
|
continue
|
|
}
|
|
|
|
valsplit.push(tmpsplit[key])
|
|
}
|
|
|
|
if (add_empty) {
|
|
valsplit.push("")
|
|
}
|
|
console.log("Split: ", valsplit)
|
|
|
|
var newarr = []
|
|
for (let [key,keyval] in Object.entries(valsplit)) {
|
|
var line = valsplit[key]
|
|
|
|
if (key == index) {
|
|
if (oldkey === "") {
|
|
if (line.includes("=") || line.includes(":")) {
|
|
newarr.push(e.target.value + line)
|
|
} else {
|
|
newarr.push(e.target.value + ": " + line)
|
|
}
|
|
} else {
|
|
newarr.push(line.replace(oldkey, e.target.value, 1))
|
|
}
|
|
|
|
} else {
|
|
newarr.push(line)
|
|
}
|
|
}
|
|
|
|
var newval = newarr.join("\n")
|
|
console.log("Fixed: ", newval)
|
|
|
|
selectedActionParameters[count].value = newval
|
|
selectedAction.parameters[count].value = newval
|
|
setSelectedAction(selectedAction)
|
|
setSelectedActionParameters(selectedActionParameters)
|
|
}}
|
|
/>
|
|
<TextField
|
|
placeholder="Value"
|
|
style={{flex: 6}}
|
|
defaultValue={inputdata.value}
|
|
onBlur={(e) => {
|
|
console.log("Change from oldval to new: ", oldval, e.target.value)
|
|
|
|
// Find the right line to replace!
|
|
//const newval = selectedActionParameters[count].value.replace(oldval, e.target.value, 1)
|
|
var tmpsplit = selectedActionParameters[count].value.split("\n")
|
|
var valsplit = []
|
|
var add_empty = false
|
|
for (let [key,keyval] in Object.entries(tmpsplit)) {
|
|
if (tmpsplit[key] === "") {
|
|
add_empty = true
|
|
continue
|
|
}
|
|
|
|
valsplit.push(tmpsplit[key])
|
|
}
|
|
|
|
if (add_empty) {
|
|
valsplit.push("")
|
|
}
|
|
console.log("Split: ", valsplit)
|
|
|
|
var newarr = []
|
|
for (let [key,keyval] in Object.entries(valsplit)) {
|
|
var line = valsplit[key]
|
|
|
|
if (key == index) {
|
|
if (oldval === "") {
|
|
if (line.includes("=") || line.includes(":")) {
|
|
newarr.push(line + e.target.value)
|
|
} else {
|
|
newarr.push(line + ": " + e.target.value)
|
|
}
|
|
} else {
|
|
newarr.push(line.replace(oldval, e.target.value, 1))
|
|
}
|
|
|
|
} else {
|
|
newarr.push(line)
|
|
}
|
|
}
|
|
|
|
var newval = newarr.join("\n")
|
|
console.log("Fixed: ", newval)
|
|
|
|
selectedActionParameters[count].value = newval
|
|
selectedAction.parameters[count].value = newval
|
|
setSelectedAction(selectedAction)
|
|
setSelectedActionParameters(selectedActionParameters)
|
|
}}
|
|
/>
|
|
</div>
|
|
</span>
|
|
)
|
|
})}
|
|
<Button variant="outlined" fullWidth onClick={() => {
|
|
console.log("Add header")
|
|
|
|
selectedActionParameters[count].value += "\n"
|
|
selectedAction.parameters[count].value += "\n"
|
|
setSelectedActionParameters(selectedActionParameters)
|
|
setSelectedAction(selectedAction)
|
|
setUpdate(Math.random())
|
|
}}>
|
|
<AddIcon style={{ marginRight: 10 }} /> Add header
|
|
</Button>
|
|
</div>
|
|
}
|
|
|
|
//const regexp = new RegExp("\W+\.", "g")
|
|
//let match
|
|
//while ((match = regexp.exec(data.value)) !== null) {
|
|
// console.log(`Found ${match[0]} start=${match.index} end=${regexp.lastIndex}.`);
|
|
//}
|
|
|
|
//const str = = data.value.search(submatch)
|
|
//console.log("FOUND? ", n)
|
|
//for (var key in keywords) {
|
|
// const keyword = keywords[key]
|
|
// if (data.value.includes(keyword)) {
|
|
// console.log("INCLUDED: ", keyword)
|
|
// }
|
|
//}
|
|
|
|
if (files !== undefined && files !== null && data.name.toLowerCase() === "file_category") {
|
|
//selectedActionParameters[count].options.length > 0
|
|
console.log("FileS: ", files)
|
|
if (files.namespaces !== undefined && files.namespaces !== null && files.namespaces.length > 0) {
|
|
data.options = files.namespaces
|
|
}
|
|
}
|
|
|
|
//const keywords = ["len", "lower", "upper", "trim", "split", "length", "number", "parse", "join"]
|
|
if (
|
|
selectedActionParameters[count].schema !== undefined &&
|
|
selectedActionParameters[count].schema !== null &&
|
|
selectedActionParameters[count].schema.type === "file"
|
|
) {
|
|
datafield = (
|
|
<TextField
|
|
style={{
|
|
backgroundColor: theme.palette.inputColor,
|
|
borderRadius: theme.palette?.borderRadius,
|
|
}}
|
|
InputProps={{
|
|
endAdornment: hideExtraTypes ? null : (
|
|
<InputAdornment position="end">
|
|
<Tooltip title="Autocomplete text" placement="top">
|
|
<AddCircleOutlineIcon
|
|
style={{ cursor: "pointer" }}
|
|
onClick={(event) => {
|
|
setMenuPosition({
|
|
top: event.pageY + 10,
|
|
left: event.pageX + 10,
|
|
});
|
|
setShowDropdownNumber(count);
|
|
setShowDropdown(true);
|
|
setShowAutocomplete(true);
|
|
}}
|
|
/>
|
|
</Tooltip>
|
|
</InputAdornment>
|
|
),
|
|
}}
|
|
helperText={returnHelperText(data.name, data.value)}
|
|
fullWidth
|
|
multiline={multiline}
|
|
rows={"3"}
|
|
color="primary"
|
|
defaultValue={data.value}
|
|
type={"text"}
|
|
placeholder={"The file ID to get"}
|
|
id={"rightside_field_" + count}
|
|
onChange={(event) => {
|
|
changeActionParameter(event, count, data);
|
|
}}
|
|
onBlur={(event) => {}}
|
|
/>
|
|
)
|
|
} else if (
|
|
(data.options !== undefined && data.options !== null && data.options.length > 0) ||
|
|
(selectedActionParameters[count].options !== undefined && selectedActionParameters[count].options !== null && selectedActionParameters[count].options.length > 0)) {
|
|
const parsedoptions = data.options !== undefined && data.options !== null && data.options.length > 0 ? data.options : selectedActionParameters[count].options
|
|
|
|
if (selectedActionParameters[count].value === "") {
|
|
// && selectedActionParameters[count].required) {
|
|
// Rofl, dirty workaround :)
|
|
const e = {
|
|
target: {
|
|
value: parsedoptions[0],
|
|
},
|
|
};
|
|
|
|
changeActionParameter(e, count, data);
|
|
}
|
|
|
|
var multi = false
|
|
if (selectedActionParameters[count].multiselect !== undefined && selectedActionParameters[count].multiselect !== null && selectedActionParameters[count].multiselect === true) {
|
|
multi = true
|
|
|
|
selectedActionParameters[count].value = selectedActionParameters[count].value.split(",")
|
|
}
|
|
|
|
datafield = (
|
|
<Select
|
|
MenuProps={{
|
|
disableScrollLock: true,
|
|
}}
|
|
SelectDisplayProps={{
|
|
style: {
|
|
},
|
|
}}
|
|
multiple={multi}
|
|
value={selectedActionParameters[count].value}
|
|
fullWidth
|
|
id={"rightside_field_" + count}
|
|
onChange={(e) => {
|
|
console.log("MULTI SELECT: ", multi, e.target.value)
|
|
|
|
changeActionParameter(e, count, data);
|
|
setUpdate(Math.random());
|
|
}}
|
|
style={{
|
|
backgroundColor: theme.palette.surfaceColor,
|
|
color: "white",
|
|
height: "50px",
|
|
borderRadius: theme.palette?.borderRadius,
|
|
}}
|
|
>
|
|
{parsedoptions.map(
|
|
(data, index) => {
|
|
const split_data = data.split("||");
|
|
var viewed_data = data
|
|
if (split_data.length > 1) {
|
|
viewed_data = split_data[0]
|
|
}
|
|
|
|
viewed_data = (viewed_data.charAt(0).toUpperCase() + viewed_data.slice(1)).replaceAll("_", " ")
|
|
|
|
return (
|
|
<MenuItem
|
|
key={data}
|
|
style={{
|
|
backgroundColor: theme.palette.inputColor,
|
|
color: "white",
|
|
}}
|
|
value={data}
|
|
>
|
|
{viewed_data}
|
|
</MenuItem>
|
|
);
|
|
}
|
|
)}
|
|
</Select>
|
|
);
|
|
} else if (data.variant === "STATIC_VALUE") {
|
|
staticcolor = "#FF8544";
|
|
}
|
|
|
|
if (data.field_active === false) {
|
|
console.log("Field not active: ", data?.name)
|
|
return null
|
|
}
|
|
|
|
// Shows nested list of nodes > their JSON lists
|
|
const ActionlistWrapper = (props) => {
|
|
const handleMenuClose = () => {
|
|
setShowAutocomplete(false);
|
|
|
|
if (
|
|
!selectedActionParameters[count].value[
|
|
selectedActionParameters[count].value.length - 1
|
|
] === "$"
|
|
) {
|
|
setShowDropdown(false);
|
|
}
|
|
|
|
setUpdate(Math.random());
|
|
setMenuPosition(null);
|
|
};
|
|
|
|
const handleItemClick = (values) => {
|
|
if (values === undefined ||values === null ||values.length === 0) {
|
|
return;
|
|
}
|
|
|
|
var toComplete = selectedActionParameters[count].value.trim()
|
|
.endsWith("$")
|
|
? values[0].autocomplete
|
|
: "$" + values[0].autocomplete;
|
|
|
|
toComplete = toComplete.toLowerCase().replaceAll(" ", "_");
|
|
for (let [key,keyval] in Object.entries(values)) {
|
|
if (key == 0 || values[key].autocomplete.length === 0) {
|
|
continue;
|
|
}
|
|
|
|
toComplete += values[key].autocomplete;
|
|
}
|
|
|
|
|
|
|
|
// Handles the fields under OpenAPI body to be parsed.
|
|
if (data.name.startsWith("${") && data.name.endsWith("}")) {
|
|
const paramcheck = selectedAction.parameters.find(
|
|
(param) => param.name === "body"
|
|
)
|
|
|
|
if (paramcheck !== undefined) {
|
|
if (paramcheck["value_replace"] === undefined || paramcheck["value_replace"] === null) {
|
|
paramcheck["value_replace"] = [
|
|
{
|
|
key: data.name,
|
|
value: toComplete,
|
|
},
|
|
]
|
|
} else {
|
|
const subparamindex = paramcheck["value_replace"]
|
|
.findIndex((param) => param.key === data.name);
|
|
|
|
if (subparamindex === -1) {
|
|
paramcheck["value_replace"].push({
|
|
key: data.name,
|
|
value: toComplete,
|
|
})
|
|
|
|
} else {
|
|
paramcheck["value_replace"][subparamindex]["value"] +=
|
|
toComplete;
|
|
}
|
|
}
|
|
|
|
selectedActionParameters[count]["value_replace"] = paramcheck;
|
|
|
|
selectedAction.parameters = selectedActionParameters
|
|
//selectedAction.parameters[count]["value_replace"] = paramcheck;
|
|
setSelectedAction(selectedAction);
|
|
setUpdate(Math.random());
|
|
|
|
setShowDropdown(false);
|
|
setMenuPosition(null);
|
|
return;
|
|
}
|
|
}
|
|
|
|
console.log("In nestedclick!!")
|
|
var newValue = selectedActionParameters[count].value + toComplete
|
|
changeActionParameter({target: {value: newValue}}, count, data, true)
|
|
//selectedActionParameters[count].value += toComplete;
|
|
//selectedAction.parameters[count].value = selectedActionParameters[count].value;
|
|
//setSelectedAction(selectedAction);
|
|
//setUpdate(Math.random());
|
|
|
|
setShowDropdown(false);
|
|
setMenuPosition(null);
|
|
};
|
|
|
|
const iconStyle = {
|
|
marginRight: 15,
|
|
};
|
|
|
|
return (
|
|
<Menu
|
|
anchorReference="anchorPosition"
|
|
anchorPosition={menuPosition}
|
|
onClose={() => {
|
|
handleMenuClose();
|
|
}}
|
|
open={!!menuPosition}
|
|
style={{
|
|
color: "white",
|
|
marginTop: 2,
|
|
maxHeight: 650,
|
|
}}
|
|
>
|
|
{actionlist.map((innerdata) => {
|
|
const icon =
|
|
innerdata.type === "action" ? (
|
|
<AppsIcon style={{ marginRight: 10 }} />
|
|
) : innerdata.type === "workflow_variable" ||
|
|
innerdata.type === "execution_variable" ? (
|
|
<FavoriteBorderIcon style={{ marginRight: 10 }} />
|
|
) : (
|
|
<ScheduleIcon style={{ marginRight: 10 }} />
|
|
);
|
|
|
|
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 #FF8544";
|
|
} else {
|
|
exec_text_field.style.border = "";
|
|
}
|
|
}
|
|
|
|
// Also doing arguments
|
|
if (
|
|
workflow.triggers !== undefined &&
|
|
workflow.triggers !== null &&
|
|
workflow.triggers.length > 0
|
|
) {
|
|
for (let [key,keyval] in Object.entries(workflow.triggers)) {
|
|
const item = workflow.triggers[key];
|
|
|
|
if (cy !== undefined) {
|
|
var node = cy.getElementById(item.id);
|
|
if (node.length > 0) {
|
|
if (inside) {
|
|
node.addClass("shuffle-hover-highlight");
|
|
} else {
|
|
node.removeClass("shuffle-hover-highlight");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
const handleActionHover = (inside, actionId) => {
|
|
if (cy !== undefined) {
|
|
var node = cy.getElementById(actionId);
|
|
if (node.length > 0) {
|
|
if (inside) {
|
|
node.addClass("shuffle-hover-highlight");
|
|
} else {
|
|
node.removeClass("shuffle-hover-highlight");
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
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 ? (
|
|
<NestedMenuItem
|
|
key={innerdata.name}
|
|
label={
|
|
<div style={{ display: "flex", marginLeft: 0, }}>
|
|
{icon} {innerdata.name}
|
|
</div>
|
|
}
|
|
parentMenuOpen={!!menuPosition}
|
|
style={{
|
|
color: "white",
|
|
minWidth: 250,
|
|
maxWidth: 250,
|
|
maxHeight: 50,
|
|
overflow: "hidden",
|
|
}}
|
|
onClick={() => {
|
|
console.log("CLICKED: ", innerdata);
|
|
console.log(innerdata.example)
|
|
handleItemClick([innerdata]);
|
|
}}
|
|
>
|
|
<Paper style={{minHeight: 500, maxHeight: 500, minWidth: 275, maxWidth: 275, position: "fixed", top: menuPosition1.top-200, left: menuPosition1.left-450, padding: "10px 0px 10px 10px", overflow: "hidden", overflowY: "auto", border: "1px solid rgba(255,255,255,0.3)",}}>
|
|
<MenuItem
|
|
key={innerdata.name}
|
|
style={{
|
|
marginLeft: 15,
|
|
color: "white",
|
|
minWidth: 250,
|
|
maxWidth: 250,
|
|
padding: 0,
|
|
position: "relative",
|
|
}}
|
|
value={innerdata}
|
|
onMouseOver={() => {
|
|
//console.log("HOVER: ", pathdata);
|
|
}}
|
|
onClick={() => {
|
|
handleItemClick([innerdata]);
|
|
}}
|
|
>
|
|
<Typography variant="h6" style={{paddingBottom: 5}}>
|
|
{innerdata.name}
|
|
</Typography>
|
|
</MenuItem>
|
|
{parsedPaths.map((pathdata, index) => {
|
|
// FIXME: Should be recursive in here
|
|
//<VpnKeyIcon style={iconStyle} />
|
|
const icon =
|
|
pathdata.type === "value" ? (
|
|
<span style={{marginLeft: 9, }} />
|
|
) : pathdata.type === "list" ? (
|
|
<FormatListNumberedIcon style={{marginLeft: 9, marginRight: 10, }} />
|
|
) : (
|
|
<CircleIcon style={{marginLeft: 9, marginRight: 10, color: coverColor}}/>
|
|
);
|
|
//<ExpandMoreIcon style={iconStyle} />
|
|
|
|
const indentation_count = (pathdata.name.match(/\./g) || []).length+1
|
|
const baseIndent = <div style={{marginLeft: 20, height: 30, width: 1, backgroundColor: coverColor,}} />
|
|
//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 (
|
|
<MenuItem
|
|
key={pathdata.name}
|
|
style={{
|
|
color: "white",
|
|
minWidth: 250,
|
|
maxWidth: 250,
|
|
padding: boxPadding,
|
|
}}
|
|
value={pathdata}
|
|
onMouseOver={() => {
|
|
//console.log("HOVER: ", pathdata);
|
|
}}
|
|
onClick={() => {
|
|
handleItemClick([innerdata, pathdata]);
|
|
}}
|
|
>
|
|
<Tooltip
|
|
color="primary"
|
|
title={`Ex. value: ${pathdata.value}`}
|
|
placement="left"
|
|
>
|
|
<div style={{ display: "flex", height: 30, }}>
|
|
{Array(indentation_count).fill().map((subdata, subindex) => {
|
|
return (
|
|
baseIndent
|
|
)
|
|
})}
|
|
{icon} {newname}
|
|
{pathdata.type === "list" ? <SquareFootIcon style={{marginleft: 10, }} onClick={(e) => {
|
|
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 }}`
|
|
selectedActionParameters[count].value += `$${innerdata.name}.${newname}`
|
|
selectedAction.parameters[count].value = selectedActionParameters[count].value;
|
|
setSelectedAction(selectedAction);
|
|
setUpdate(Math.random());
|
|
setShowDropdown(false);
|
|
setMenuPosition(null);
|
|
}} /> : null}
|
|
</div>
|
|
</Tooltip>
|
|
</MenuItem>
|
|
);
|
|
})}
|
|
</Paper>
|
|
</NestedMenuItem>
|
|
) : (
|
|
<MenuItem
|
|
key={innerdata.name}
|
|
style={{
|
|
backgroundColor: theme.palette.inputColor,
|
|
color: "white",
|
|
minWidth: 250,
|
|
maxWidth: 250,
|
|
marginRight: 0,
|
|
}}
|
|
value={innerdata}
|
|
onMouseOver={() => handleMouseover()}
|
|
onMouseOut={() => {
|
|
handleMouseOut();
|
|
}}
|
|
onClick={() => {
|
|
handleItemClick([innerdata]);
|
|
}}
|
|
>
|
|
<Tooltip
|
|
color="primary"
|
|
title={`Value: ${innerdata.value}`}
|
|
placement="left"
|
|
>
|
|
<div style={{ display: "flex" }}>
|
|
{icon} {innerdata.name}
|
|
</div>
|
|
</Tooltip>
|
|
</MenuItem>
|
|
);
|
|
})}
|
|
</Menu>
|
|
);
|
|
}
|
|
|
|
const buttonTitle = `Authenticate API ${selectedApp.name.replaceAll("_", " ")}`
|
|
const hasAutocomplete = data?.autocompleted === true
|
|
if (data.variant === undefined || data.variant === null) {
|
|
data.variant = "STATIC_VALUE"
|
|
}
|
|
|
|
return (
|
|
<div key={data.name}>
|
|
{showButtonField === true ? hideBodyButtonValue : null}
|
|
<div
|
|
style={{ marginTop: 20, marginBottom: 0, display: "flex" }}
|
|
>
|
|
{data.configuration === true ? (
|
|
<Tooltip
|
|
title={buttonTitle}
|
|
placement="top"
|
|
>
|
|
<LockOpenIcon
|
|
style={{
|
|
cursor: "pointer",
|
|
width: 24,
|
|
height: 24,
|
|
marginRight: 10,
|
|
color: "rgba(255,255,255,0.6)",
|
|
}}
|
|
onClick={() => {
|
|
setAuthenticationModalOpen(true);
|
|
}}
|
|
/>
|
|
</Tooltip>
|
|
) : null}
|
|
|
|
{hasAutocomplete === true ?
|
|
<Tooltip
|
|
color="primary"
|
|
title={"Field was autocompleted by Shuffle based on previous actions (same fields or parent nodes)"}
|
|
placement="top"
|
|
>
|
|
<AutoFixHighIcon style={{
|
|
color: "rgba(255,255,255,0.7)" ,
|
|
marginRight: 10,
|
|
}}/>
|
|
</Tooltip>
|
|
:
|
|
null}
|
|
|
|
{showCacheConfig === true ?
|
|
<Tooltip
|
|
color="primary"
|
|
title={"Explore your keys in Datastore"}
|
|
placement="top"
|
|
>
|
|
<a href="/admin?tab=cache" target="_blank" style={{textDecoration: "none"}}>
|
|
<StorageIcon style={{
|
|
color: "#FF8544",
|
|
marginRight: 10,
|
|
}}/>
|
|
</a>
|
|
</Tooltip>
|
|
: null}
|
|
|
|
<div
|
|
style={{
|
|
flex: "10",
|
|
marginTop: "auto",
|
|
marginBottom: "auto",
|
|
color: "#C5C5C5",
|
|
}}
|
|
>
|
|
{tmpitem} <span style={{color: theme.palette.main}}>{selectedActionParameters[count].required || selectedActionParameters[count].configuration ? "*" : ""}</span>
|
|
</div>
|
|
|
|
<Tooltip title="Expand editor window" placement="top">
|
|
|
|
<OpenInFullIcon
|
|
style={{ color: "rgba(255,255,255,0.7)", cursor: "pointer", margin: multiline ? 5 : 0, height: 20, width: 20, }}
|
|
onClick={(event) => {
|
|
event.preventDefault()
|
|
setFieldCount(count)
|
|
setExpansionModalOpen(true)
|
|
setActiveDialog("codeeditor")
|
|
//setcodedata(data.value)
|
|
var parsedvalue = data.value
|
|
if (parsedvalue === undefined || parsedvalue === null) {
|
|
parsedvalue = ""
|
|
}
|
|
|
|
setEditorData({
|
|
"name": data.name,
|
|
"value": parsedvalue,
|
|
"field_number": count,
|
|
"actionlist": actionlist,
|
|
"field_id": clickedFieldId,
|
|
|
|
"example": selectedActionParameters[count].example,
|
|
})
|
|
}}
|
|
/>
|
|
</Tooltip>
|
|
|
|
</div>
|
|
{datafield}
|
|
{/*shufflecode*/}
|
|
{showDropdown &&
|
|
showDropdownNumber === count &&
|
|
data.variant === "STATIC_VALUE" &&
|
|
jsonList.length > 0 ? (
|
|
<FormControl fullWidth style={{ marginTop: 0 }}>
|
|
<InputLabel
|
|
id="action-autocompleter"
|
|
style={{ marginLeft: 10, color: "white" }}
|
|
>
|
|
Autocomplete
|
|
</InputLabel>
|
|
<Select
|
|
MenuProps={{
|
|
disableScrollLock: true,
|
|
}}
|
|
labelId="action-autocompleter"
|
|
SelectDisplayProps={{
|
|
style: {
|
|
},
|
|
}}
|
|
onClose={() => {
|
|
setShowAutocomplete(false);
|
|
|
|
if (!selectedActionParameters[count].value[selectedActionParameters[count].value.length - 1] === ".") {
|
|
setShowDropdown(false);
|
|
}
|
|
|
|
setUpdate(Math.random());
|
|
}}
|
|
onClick={() => {
|
|
setShowAutocomplete(true)
|
|
}}
|
|
fullWidth
|
|
open={showAutocomplete}
|
|
style={{
|
|
color: "white",
|
|
height: 35,
|
|
marginTop: 2,
|
|
borderRadius: theme.palette?.borderRadius,
|
|
}}
|
|
onChange={(e) => {
|
|
console.log("SELECT ONCHANGE DONE")
|
|
|
|
if (selectedActionParameters[count].value[selectedActionParameters[count].value.length - 1] === ".") {
|
|
e.target.value.autocomplete = e.target.value.autocomplete.slice(1,e.target.value.autocomplete.length);
|
|
}
|
|
|
|
selectedActionParameters[count].value += e.target.value.autocomplete;
|
|
selectedAction.parameters[count].value = selectedActionParameters[count].value;
|
|
setSelectedAction(selectedAction);
|
|
setUpdate(Math.random());
|
|
|
|
setShowDropdown(false);
|
|
}}
|
|
>
|
|
{jsonList.map((data) => {
|
|
const iconStyle = {
|
|
marginRight: 15,
|
|
};
|
|
|
|
const icon =
|
|
data.type === "value" ? (
|
|
<VpnKeyIcon style={iconStyle} />
|
|
) : data.type === "list" ? (
|
|
<FormatListNumberedIcon style={iconStyle} />
|
|
) : (
|
|
<ExpandMoreIcon style={iconStyle} />
|
|
)
|
|
|
|
return (
|
|
<MenuItem
|
|
key={data.name}
|
|
style={{
|
|
backgroundColor: theme.palette.inputColor,
|
|
color: "white",
|
|
}}
|
|
value={data}
|
|
onMouseOver={() => {}}
|
|
>
|
|
<Tooltip
|
|
color="primary"
|
|
title={`Ex. value: ${data.value}`}
|
|
placement="left"
|
|
>
|
|
<div style={{ display: "flex" }}>
|
|
{icon} {data.name}
|
|
</div>
|
|
</Tooltip>
|
|
</MenuItem>
|
|
);
|
|
})}
|
|
</Select>
|
|
</FormControl>
|
|
) : null}
|
|
{showDropdown &&
|
|
showDropdownNumber === count &&
|
|
data.variant === "STATIC_VALUE" &&
|
|
jsonList.length === 0 ? (
|
|
<ActionlistWrapper actionlist={actionlist} />
|
|
) : null}
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
: null
|
|
}
|
|
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default ParsedAction;
|