Workflow syncs

This commit is contained in:
Frikky
2025-03-03 10:52:41 +01:00
parent 3b16d94ebc
commit 410a61af1c
5 changed files with 569 additions and 104 deletions
+64 -18
View File
@@ -1,4 +1,4 @@
import React, { useCallback, useEffect, useState } from 'react'; import React, { memo, useCallback, useEffect, useState } from 'react';
import { useNavigate } from 'react-router'; import { useNavigate } from 'react-router';
import { import {
@@ -29,8 +29,15 @@ import theme from "../theme.jsx";
import YAML from 'yaml'; import YAML from 'yaml';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { InstantSearch, connectHits, connectSearchBox } from 'react-instantsearch-dom';
import algoliasearch from "algoliasearch/lite";
const AppModal = ({ open, onClose, app, globalUrl, getApps }) => { const searchClient = algoliasearch(
"JNSS5CFDZZ",
"db08e40265e2941b9a7d8f644b6e5240"
);;
const AppModal = ({ open, onClose, app, globalUrl, getApps}) => {
const [frameworkData, setFrameworkData] = useState({}) const [frameworkData, setFrameworkData] = useState({})
const [userdata, setUserdata] = useState({}) const [userdata, setUserdata] = useState({})
@@ -79,8 +86,7 @@ const AppModal = ({ open, onClose, app, globalUrl, getApps }) => {
}); });
}, [app]); }, [app]);
const getFramework = () => {
const getFramework = () => {
fetch(globalUrl + "/api/v1/apps/frameworkConfiguration", { fetch(globalUrl + "/api/v1/apps/frameworkConfiguration", {
method: "GET", method: "GET",
headers: { headers: {
@@ -295,8 +301,6 @@ const AppModal = ({ open, onClose, app, globalUrl, getApps }) => {
}) })
} }
useEffect(() => { useEffect(() => {
setUsecaseLoading(true) setUsecaseLoading(true)
getAvailableWorkflows() getAvailableWorkflows()
@@ -329,6 +333,7 @@ const AppModal = ({ open, onClose, app, globalUrl, getApps }) => {
//delete apps from local storage //delete apps from local storage
localStorage.removeItem("apps"); localStorage.removeItem("apps");
getApps(); getApps();
onClose();
}, 1000); }, 1000);
} else { } else {
toast("Failed deleting app. Does it still exist?"); toast("Failed deleting app. Does it still exist?");
@@ -720,18 +725,7 @@ const AppModal = ({ open, onClose, app, globalUrl, getApps }) => {
textAlign: "start", textAlign: "start",
flex: 1, flex: 1,
}}> }}>
<Typography <WorkflowCard app={app}/>
variant="h6"
sx={{
fontFamily: theme?.typography?.fontFamily,
fontSize: '24px',
fontWeight: 600,
mb: 0.3,
color: '#fff'
}}
>
20
</Typography>
<Typography <Typography
variant="body2" variant="body2"
sx={{ sx={{
@@ -930,3 +924,55 @@ const AppModal = ({ open, onClose, app, globalUrl, getApps }) => {
}; };
export default AppModal; export default AppModal;
const WorkflowCard = memo(({ app }) => {
const [name, setName] = useState("");
const [relatedWorkflows, setRelatedWorkflows] = useState(0);
const WorkflowHits = ({ hits }) => {
useEffect(() => {
setRelatedWorkflows(hits?.length || 0);
}, [hits]);
return null;
};
const CustomWorkflowHits = connectHits(WorkflowHits);
const SearchBox = ({ refine, currentRefinement }) => {
useEffect(() => {
if (name.length > 0 && currentRefinement !== name) {
refine(name?.split(" ")[0]);
}
}, [name, refine, currentRefinement]);
return null;
};
const CustomSearchBox = connectSearchBox(SearchBox);
useEffect(() => {
if (app?.name && app.name.trim().length > 0 && name !== app.name) {
const formattedName = app.name
.charAt(0)
.toUpperCase() + app.name.substring(1)
.replaceAll("_", " ");
setName(formattedName);
}
}, [app?.name]);
return (
<Typography variant="h4">
{name.length > 0 ? (
<InstantSearch key={name} searchClient={searchClient} indexName="workflows">
<CustomSearchBox defaultRefinement={name?.split(" ")[0]}/>
<CustomWorkflowHits />
{relatedWorkflows}
</InstantSearch>
) : (
relatedWorkflows
)}
</Typography>
);
});
+6
View File
@@ -1160,6 +1160,12 @@ const ParsedAction = (props) => {
selectedActionParameters[1].value = splitparsed[1] selectedActionParameters[1].value = splitparsed[1]
selectedAction.parameters[1].value = splitparsed[1] selectedAction.parameters[1].value = splitparsed[1]
if (splitparsed.length > 2) {
toast.warn("Filter list only supports filtering at the first level. If you want multi-level filtering, please use the 'execute python' action with the 'filter a list' function in the code editor.")
} else if (selectedAction.parameters[1].value.includes(".#")) {
toast.warn("This filter may not work due to using .# indexing. Please use the 'execute python' action and try the 'filter a list' function in the code editor.")
}
} else { } else {
// Remove .# and after // Remove .# and after
const splitparsed = parsedvalue.split(".#") const splitparsed = parsedvalue.split(".#")
@@ -89,6 +89,7 @@ const liquidFilters = [
const pythonFilters = [ const pythonFilters = [
{ "name": "Hello World", "value": `print("hello world")`, "example": `` }, { "name": "Hello World", "value": `print("hello world")`, "example": `` },
{ "name": "Using Shuffle variables", "value": `import json\nnodevalue = r\"\"\"$exec\"\"\"\nif not nodevalue:\n nodevalue = r\"\"\"{\"sample\": \"string\", \"int\": 1}\"\"\"\n \njsondata = json.loads(nodevalue)\nprint(jsondata)`, "example": `` }, { "name": "Using Shuffle variables", "value": `import json\nnodevalue = r\"\"\"$exec\"\"\"\nif not nodevalue:\n nodevalue = r\"\"\"{\"sample\": \"string\", \"int\": 1}\"\"\"\n \njsondata = json.loads(nodevalue)\nprint(jsondata)`, "example": `` },
{ "name": "Filter a list", "value": `import json\nnodevalue = r\"\"\"$exec\"\"\"\nif not nodevalue:\n nodevalue = r\"\"\"[{\"sample\": \"string\", \"int\": 1, "malicious": "no"}, {\"sample\": \"string2\", \"int\": 1, "malicious": "yes"}]\"\"\"\n \njsondata = json.loads(nodevalue)\nfiltered = []\nfor item in jsondata:\n try:\n if item[\"malicious\"] == \"yes\":\n filtered.append(item)\n except:\n pass\nprint(json.dumps(filtered))`, "example": `` },
{ "name": "Print Execution ID", "value": `print(self.current_execution_id)`, "example": `` }, { "name": "Print Execution ID", "value": `print(self.current_execution_id)`, "example": `` },
{ "name": "Get full execution details", "value": `print(self.full_execution)`, "example": `` }, { "name": "Get full execution details", "value": `print(self.full_execution)`, "example": `` },
{ "name": "Use files", "value": `# Create a sample file\nfiles = [{\n \"filename\": \"test.txt\",\n \"data\": \"Testdata\"\n}]\nret = self.set_files(files)\n\n# Get the content of the file from Shuffle storage\n# Originally a byte string in the \"data\" key\nfile_content = (self.get_file(ret[0])[\"data\"]).decode()\nprint(file_content)`, "example": `` }, { "name": "Use files", "value": `# Create a sample file\nfiles = [{\n \"filename\": \"test.txt\",\n \"data\": \"Testdata\"\n}]\nret = self.set_files(files)\n\n# Get the content of the file from Shuffle storage\n# Originally a byte string in the \"data\" key\nfile_content = (self.get_file(ret[0])[\"data\"]).decode()\nprint(file_content)`, "example": `` },
+496 -85
View File
@@ -74,6 +74,7 @@ import {
import { import {
Storage as StorageIcon,
Code as CodeIcon, Code as CodeIcon,
Folder as FolderIcon, Folder as FolderIcon,
VerifiedUser as VerifiedUserIcon, VerifiedUser as VerifiedUserIcon,
@@ -503,7 +504,7 @@ const AngularWorkflow = (defaultprops) => {
const [allRevisions, setAllRevisions] = useState([]) const [allRevisions, setAllRevisions] = useState([])
const [menuPosition, setMenuPosition] = useState(null); const [menuPosition, setMenuPosition] = useState(null);
const [showDropdown, setShowDropdown] = React.useState(false); const [showDropdown, setShowDropdown] = React.useState(false);
const [subflowActionList, setSubflowActionList] = React.useState([]); const [triggerActionList, setTriggerActionList] = React.useState([]);
const [apps, setApps] = React.useState([]); const [apps, setApps] = React.useState([]);
const [filteredApps, setFilteredApps] = React.useState([]); const [filteredApps, setFilteredApps] = React.useState([]);
@@ -574,11 +575,12 @@ const AngularWorkflow = (defaultprops) => {
}, [editWorkflowModalOpen]) }, [editWorkflowModalOpen])
useEffect(() => { useEffect(() => {
if (selectedTrigger !== undefined && selectedTrigger?.parameters !== undefined && selectedTrigger?.parameters !== null && selectedTrigger?.parameters?.length > 1) { if(selectedTrigger?.trigger_type === "SUBFLOW"){
// Right now just setting for the subflow setSelectedTriggerValue(workflow.triggers[selectedTriggerIndex].parameters[1].value || "")
setSelectedTriggerValue(selectedTrigger?.parameters[1]?.value) } else if(selectedTrigger?.trigger_type === "USERINPUT"){
} setSelectedTriggerValue(workflow.triggers[selectedTriggerIndex].parameters[0].value || "Do you want to continue the workflow? Start parameters: $exec")
}, [selectedTrigger]) }
}, [selectedTriggerIndex, selectedTrigger, workflow])
useEffect(() => { useEffect(() => {
if(selectedEdge && Object.keys(selectedEdge).length > 0){ if(selectedEdge && Object.keys(selectedEdge).length > 0){
@@ -1136,23 +1138,66 @@ const AngularWorkflow = (defaultprops) => {
useEffect(() => { useEffect(() => {
// if (subflowActionList.length === 0) { // if (subflowActionList.length === 0) {
const newActionList = []; const newActionList = [];
// FIXME: Have previous execution values in here
newActionList.push({
type: "Runtime Argument", if (workflowExecutions.length > 0) {
name: "Runtime Argument", for (let execution of workflowExecutions) {
value: "$exec", const execArg = execution.execution_argument;
highlight: "exec", if (execArg && execArg.length > 0) {
autocomplete: "exec", const valid = validateJson(execArg);
example: "hello", if (valid.valid) {
}) newActionList.push({
newActionList.push({ type: "Runtime Argument",
type: "Shuffle Database", name: "Runtime Argument",
name: "Shuffle Database", value: "$exec",
highlight: "exec",
autocomplete: "exec",
example: valid.result,
})
break
}
}
}
}
// Add default Runtime Argument if none were added
if (newActionList.length === 0) {
newActionList.push({
type: "Runtime Argument",
name: "Runtime Argument",
value: "$exec",
highlight: "exec",
autocomplete: "exec",
example: "",
})
}
// Add Shuffle DB with cache keys if available
let cacheKey = {
type: "Shuffle DB",
name: "Shuffle DB",
value: "$shuffle_cache", value: "$shuffle_cache",
highlight: "shuffle_db", highlight: "shuffle_cache",
autocomplete: "shuffle_cache", autocomplete: "shuffle_cache",
example: "hello", example: "Hello",
}) }
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);
// FIXME: Have previous execution values in here
if ( if (
workflow.workflow_variables !== null && workflow.workflow_variables !== null &&
workflow.workflow_variables !== undefined && workflow.workflow_variables !== undefined &&
@@ -1216,7 +1261,7 @@ const AngularWorkflow = (defaultprops) => {
const validated = validateJson(foundResult.result) const validated = validateJson(foundResult.result)
if (validated.valid) { if (validated.valid) {
exampledata = validateJson.result exampledata = validated.result
break break
} }
} }
@@ -1234,7 +1279,7 @@ const AngularWorkflow = (defaultprops) => {
} }
} }
setSubflowActionList(newActionList); setTriggerActionList(newActionList);
}, [workflow.workflow_variables, workflow.execution_variables, workflow, selectedTrigger]); }, [workflow.workflow_variables, workflow.execution_variables, workflow, selectedTrigger]);
@@ -13779,6 +13824,7 @@ const AngularWorkflow = (defaultprops) => {
setSourceValue({}); setSourceValue({});
setConditionValue({}); setConditionValue({});
setDestinationValue({}); setDestinationValue({});
navigate("")
}} }}
color="secondary" color="secondary"
> >
@@ -13789,6 +13835,7 @@ const AngularWorkflow = (defaultprops) => {
variant="contained" variant="contained"
onClick={() => { onClick={() => {
setSelectedEdge({}); setSelectedEdge({});
navigate("")
var data = { var data = {
condition: conditionValue, condition: conditionValue,
@@ -14613,43 +14660,62 @@ const AngularWorkflow = (defaultprops) => {
}; };
const handleItemClick = (values) => { const handleItemClick = (values) => {
console.log("VALUES: ", values) console.log("VALUES: ", values)
if (values === undefined || values === null || values.length === 0) { if (values === undefined || values === null || values.length === 0) {
return; return;
}
// Get the base autocomplete value and normalize it
let toComplete = values[0].autocomplete.toLowerCase().replaceAll(" ", "_");
// Add $ prefix if not already present
if (!toComplete.startsWith("$")) {
toComplete = "$" + toComplete;
}
// Add any additional path components
for (let key in values) {
if (key === "0" || !values[key].autocomplete) {
continue;
} }
toComplete += values[key].autocomplete;
}
/* var foundField;
workflow.triggers[selectedTriggerIndex].parameters[1].value // Update the field value
.trim() if(selectedTrigger?.trigger_type === "SUBFLOW"){
.endsWith("$") foundField = document.getElementById("subflow_exec_field");
? values[0].autocomplete }else if(selectedTrigger?.trigger_type === "USERINPUT"){
: "$" + values[0].autocomplete; foundField = document.getElementById("userinput_info_field");
}
for (var key in values) { if (foundField) {
if (key === 0 || values[key].autocomplete.length === 0) { // Get current cursor position
continue; const cursorPos = foundField.selectionStart;
} const currentValue = foundField.value;
toComplete += values[key].autocomplete // Insert the new value at cursor position
} const newValue = currentValue.slice(0, cursorPos) + toComplete + currentValue.slice(cursorPos);
*/ foundField.value = newValue;
if (selectedTrigger.name === "Shuffle Workflow") { // Update state
const toComplete = workflow?.triggers?.[selectedTriggerIndex]?.parameters?.[1]?.value + "$" + values[0]?.autocomplete setSelectedTriggerValue(newValue);
// selectedTrigger.parameters[1].value = toComplete if(selectedTrigger?.trigger_type === "SUBFLOW"){
workflow.triggers[selectedTriggerIndex].parameters[1].value = toComplete workflow.triggers[selectedTriggerIndex].parameters[1].value = newValue
const foundfield = document.getElementById("subflow_exec_field") }else if(selectedTrigger?.trigger_type === "USERINPUT"){
if (foundfield !== undefined && foundfield !== null) { workflow.triggers[selectedTriggerIndex].parameters[0].value = newValue
foundfield.value = toComplete
}
setWorkflow(workflow)
} }
setUpdate(Math.random()); // Set cursor position after inserted text
setShowDropdown(false); foundField.setSelectionRange(cursorPos + toComplete.length, cursorPos + toComplete.length);
setMenuPosition(null); }
};
setWorkflow(workflow);
setUpdate(Math.random());
setShowDropdown(false);
setMenuPosition(null);
};
const subflowtypes = [ const subflowtypes = [
{ {
@@ -14676,7 +14742,7 @@ const AngularWorkflow = (defaultprops) => {
workflow.triggers[triggerIndex].parameters[paramIndex].value = newData; workflow.triggers[triggerIndex].parameters[paramIndex].value = newData;
// Update workflow state to trigger re-render // Update workflow state to trigger re-render
setWorkflow({...workflow}); setWorkflow(workflow);
setSelectedTriggerValue(newData) setSelectedTriggerValue(newData)
setLastSaved(false); setLastSaved(false);
} }
@@ -15166,7 +15232,7 @@ const AngularWorkflow = (defaultprops) => {
"name": workflow.triggers[selectedTriggerIndex].parameters[1].name, "name": workflow.triggers[selectedTriggerIndex].parameters[1].name,
"value": parsedvalue, "value": parsedvalue,
"field_number": 1, "field_number": 1,
"actionlist": subflowActionList, "actionlist": triggerActionList,
"field_id": "subflow_exec_field", "field_id": "subflow_exec_field",
}) })
}} }}
@@ -15211,15 +15277,13 @@ const AngularWorkflow = (defaultprops) => {
fullWidth fullWidth
color="primary" color="primary"
placeholder="Some execution data" placeholder="Some execution data"
value={ value={selectedTriggerValue || ""}
selectedTriggerValue
}
onChange={(e) => { onChange={(e) => {
setLastSaved(false) setLastSaved(false)
setSelectedTriggerValue(e.target.value) setSelectedTriggerValue(e.target.value)
}} }}
onBlur={() => { onBlur={(e) => {
workflow.triggers[selectedTriggerIndex].parameters[1].value = selectedTriggerValue workflow.triggers[selectedTriggerIndex].parameters[1].value = e.target.value
setWorkflow(workflow) setWorkflow(workflow)
}} }}
/> />
@@ -15237,16 +15301,17 @@ const AngularWorkflow = (defaultprops) => {
marginTop: 2, marginTop: 2,
}} }}
> >
{subflowActionList.map((innerdata) => { {triggerActionList.map((innerdata) => {
const icon = const icon =
innerdata.type === "action" ? ( innerdata.type === "action" ? (
<AppsIcon style={{ marginRight: 10 }} /> <AppsIcon style={{ marginRight: 10 }} />
) : innerdata.type === "workflow_variable" || ) : innerdata.type === "workflow_variable" ||
innerdata.type === "execution_variable" ? ( innerdata.type === "execution_variable" ? (
<FavoriteBorderIcon style={{ marginRight: 10 }} /> <FavoriteBorderIcon style={{ marginRight: 10 }} />
) : ( ) : innerdata.type === "Shuffle DB" ?
<ScheduleIcon style={{ marginRight: 10 }} /> <StorageIcon style={{ marginRight: 10, }} />
); :
<ScheduleIcon style={{ marginRight: 10 }} />
const handleExecArgumentHover = (inside) => { const handleExecArgumentHover = (inside) => {
var exec_text_field = document.getElementById( var exec_text_field = document.getElementById(
@@ -16682,21 +16747,43 @@ const AngularWorkflow = (defaultprops) => {
</div> </div>
*/} */}
<div style={{ flex: "6", marginTop: 10, }}> <div style={{ flex: "6", marginTop: 10, }}>
<div <div style={{ marginTop: "20px", marginBottom: "5px", display: "flex", width: "100%", flexDirection: "column" }}>
style={{ <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", width: "100%" }}>
marginTop: "20px", <b>Information</b>
marginBottom: "7px", <Tooltip title="Expand editor window" placement="top">
display: "flex", <IconButton
}} onClick={(event) => {
> event.preventDefault()
<div style={{ flex: "10" }}> setCodeEditorModalOpen(true)
<b>Information</b> setActiveDialog("codeeditor")
<Typography variant="body2" color="textSecondary"> var parsedvalue = workflow?.triggers[selectedTriggerIndex]?.parameters[0]?.value
The information you want to show the user. Supports variables. Supports Markdown & HTML.
</Typography> navigate(`?trigger_id=${selectedTrigger.id}&trigger_field=${"alertinfo"}&trigger_name=${selectedTrigger.label}`)
</div> setEditorData({
"name": workflow.triggers[selectedTriggerIndex].parameters[0].name,
"value": parsedvalue,
"field_number": 0,
"actionlist": triggerActionList,
"field_id": "information_field",
})
}}
>
<OpenInFullIcon
style={{
color: "rgba(255,255,255,0.7)",
height: 20,
width: 20
}}
/>
</IconButton>
</Tooltip>
</div>
<Typography color="textSecondary" sx={{fontSize: "13px"}}>
The information you want to show the user. Supports variables. Supports Markdown & HTML.
</Typography>
</div> </div>
<TextField <TextField
id="userinput_info_field"
style={{ style={{
backgroundColor: theme.palette.inputColor, backgroundColor: theme.palette.inputColor,
borderRadius: theme.palette?.borderRadius, borderRadius: theme.palette?.borderRadius,
@@ -16704,19 +16791,347 @@ const AngularWorkflow = (defaultprops) => {
InputProps={{ InputProps={{
style: { style: {
}, },
endAdornment: (
<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(3)
setShowDropdown(true);
}}
/>
</Tooltip>
</InputAdornment>
),
}} }}
fullWidth fullWidth
rows="4" rows="4"
multiline multiline
defaultValue={ value={selectedTriggerValue}
workflow.triggers !== undefined && workflow.triggers !== null && workflow.triggers[selectedTriggerIndex].parameters !== undefined && workflow.triggers[selectedTriggerIndex].parameters.length > 0 && workflow.triggers[selectedTriggerIndex].parameters[0] !== undefined && workflow.triggers[selectedTriggerIndex].parameters[0].value !== undefined ? workflow.triggers[selectedTriggerIndex].parameters[0].value : "" onChange={(e) => {
} setLastSaved(false)
setSelectedTriggerValue(e.target.value)
}}
color="primary" color="primary"
placeholder="" placeholder=""
onBlur={(e) => { onBlur={(e) => {
setTriggerTextInformationWrapper(e.target.value); setTriggerTextInformationWrapper(e.target.value);
}} }}
/> />
{!showDropdown ? null :
<Menu
anchorReference="anchorPosition"
anchorPosition={menuPosition}
onClose={() => {
handleMenuClose();
}}
open={!!menuPosition}
style={{
border: `2px solid #FF8544`,
color: "white",
marginTop: 2,
}}
>
{triggerActionList.map((innerdata) => {
const icon =
innerdata.type === "action" ? (
<AppsIcon style={{ marginRight: 10 }} />
) : innerdata.type === "workflow_variable" ||
innerdata.type === "execution_variable" ? (
<FavoriteBorderIcon style={{ marginRight: 10 }} />
) : innerdata.type === "Shuffle DB" ?
<StorageIcon 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 triggerkey in workflow.triggers) {
const item = workflow.triggers[triggerkey];
if (cy !== undefined && cy !== null) {
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 && cy !== null) {
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 === "Runtime Argument") {
handleExecArgumentHover(true);
} else if (innerdata.type === "action") {
handleActionHover(true, innerdata.id);
}
};
const handleMouseOut = () => {
if (innerdata.type === "Runtime Argument") {
handleExecArgumentHover(false);
} else if (innerdata.type === "action") {
handleActionHover(false, innerdata.id);
}
};
var parsedPaths = [];
if (innerdata.type === "workflow_variable") {
// Try to parse the value if it's a string that could be JSON
if (typeof innerdata.value === "string") {
try {
const parsedValue = JSON.parse(innerdata.value)
if (typeof parsedValue === "object") {
parsedPaths = GetParsedPaths(parsedValue, "");
}
} catch (e) {
// Not valid JSON, use the value directly
parsedPaths = GetParsedPaths(innerdata.value, "");
}
} else if (typeof innerdata.value === "object") {
parsedPaths = GetParsedPaths(innerdata.value, "");
}
} else if (typeof innerdata.example === "object") {
parsedPaths = GetParsedPaths(innerdata.example, "");
}
const coverColor = "#82ccc3"
return parsedPaths.length > 0 ? (
<span>
{/*
<NestedMenuItem
key={innerdata.name}
label={
<div style={{ display: "flex" }}>
{icon} {innerdata.name}
</div>
}
parentMenuOpen={!!menuPosition}
style={{
backgroundColor: theme.palette.inputColor,
color: "white",
minWidth: 250,
}}
onClick={() => {
handleItemClick([innerdata]);
}}
>
{parsedPaths.map((pathdata, index) => {
// FIXME: Should be recursive in here
const icon =
pathdata.type === "value" ? (
<VpnKeyIcon style={iconStyle} />
) : pathdata.type === "list" ? (
<FormatListNumberedIcon style={iconStyle} />
) : (
<ExpandMoreIcon style={iconStyle} />
)
return (
<MenuItem
key={pathdata.name}
style={{
backgroundColor: theme.palette.inputColor,
color: "white",
minWidth: 250,
}}
value={pathdata}
onMouseOver={() => { }}
onClick={() => {
handleItemClick([innerdata, pathdata]);
}}
>
<Tooltip
color="primary"
title={`Ex. value: ${pathdata.value}`}
placement="left"
>
<div style={{ display: "flex" }}>
{icon} {pathdata.name}
</div>
</Tooltip>
</MenuItem>
);
})}
</NestedMenuItem>
*/}
<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(innerdata.example)
handleItemClick([innerdata]);
}}
>
<Paper style={{ minHeight: 500, maxHeight: 500, minWidth: 275, maxWidth: 275, position: "fixed", top: menuPosition?.top - 200, left: menuPosition?.left - 455, padding: "10px 0px 10px 10px", overflow: "hidden", overflowY: "auto", border: "1px solid rgba(255,255,255,0.3)", }}>
<MenuItem
key={innerdata.name}
style={{
// backgroundColor: theme.palette.inputColor,
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={{
// backgroundColor: theme.palette.inputColor,
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) => {
}} /> : null}
</div>
</Tooltip>
</MenuItem>
);
})}
</Paper>
</NestedMenuItem>
</span>
) : (
<MenuItem
key={innerdata.name}
style={{
// backgroundColor: theme.palette.inputColor,
color: "white",
}}
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>
}
<div <div
style={{ style={{
marginTop: "20px", marginTop: "20px",
@@ -19842,7 +20257,6 @@ const AngularWorkflow = (defaultprops) => {
src={validate.result} src={validate.result}
theme={theme.palette.jsonTheme} theme={theme.palette.jsonTheme}
style={theme.palette.reactJsonStyle} style={theme.palette.reactJsonStyle}
collapsed={false}
shouldCollapse={(jsonField) => { shouldCollapse={(jsonField) => {
return collapseField(jsonField) return collapseField(jsonField)
}} }}
@@ -20186,7 +20600,6 @@ const AngularWorkflow = (defaultprops) => {
src={validate.result} src={validate.result}
theme={theme.palette.jsonTheme} theme={theme.palette.jsonTheme}
style={theme.palette.reactJsonStyle} style={theme.palette.reactJsonStyle}
collapsed={parsedCollapse}
shouldCollapse={(jsonField) => { shouldCollapse={(jsonField) => {
return collapseField(jsonField) return collapseField(jsonField)
}} }}
@@ -21562,7 +21975,6 @@ const AngularWorkflow = (defaultprops) => {
src={validate.result} src={validate.result}
theme={theme.palette.jsonTheme} theme={theme.palette.jsonTheme}
style={theme.palette.reactJsonStyle} style={theme.palette.reactJsonStyle}
collapsed={false}
shouldCollapse={(jsonField) => { shouldCollapse={(jsonField) => {
return collapseField(jsonField) return collapseField(jsonField)
}} }}
@@ -21724,7 +22136,6 @@ const AngularWorkflow = (defaultprops) => {
src={checked.result} src={checked.result}
theme={theme.palette.jsonTheme} theme={theme.palette.jsonTheme}
style={theme.palette.reactJsonStyle} style={theme.palette.reactJsonStyle}
collapsed={data.value.length < 10000 ? false : true}
shouldCollapse={(jsonField) => { shouldCollapse={(jsonField) => {
return collapseField(jsonField) return collapseField(jsonField)
}} }}
@@ -22150,7 +22561,6 @@ const AngularWorkflow = (defaultprops) => {
src={validate.result} src={validate.result}
theme={theme.palette.jsonTheme} theme={theme.palette.jsonTheme}
style={theme.palette.reactJsonStyle} style={theme.palette.reactJsonStyle}
collapsed={selectedResult.result.length < 10000 ? false : true}
shouldCollapse={(jsonField) => { shouldCollapse={(jsonField) => {
return collapseField(jsonField) return collapseField(jsonField)
}} }}
@@ -22211,6 +22621,7 @@ const AngularWorkflow = (defaultprops) => {
</span> </span>
</div> </div>
)} )}
<div> <div>
{selectedResult.action.parameters !== null && {selectedResult.action.parameters !== null &&
selectedResult.action.parameters !== undefined ? ( selectedResult.action.parameters !== undefined ? (
+2 -1
View File
@@ -510,7 +510,6 @@ const Hits = ({
fontFamily: theme?.typography?.fontFamily fontFamily: theme?.typography?.fontFamily
}} }}
onClick={() => { onClick={() => {
console.log("App modal", data)
handleAppClick(data); handleAppClick(data);
}} }}
> >
@@ -1299,6 +1298,8 @@ const Apps2 = (props) => {
//console.log("Failed to get apps from localstorage: ", e) //console.log("Failed to get apps from localstorage: ", e)
} }
setIsLoading(true);
fetch(globalUrl + "/api/v1/apps", { fetch(globalUrl + "/api/v1/apps", {
method: "GET", method: "GET",
headers: { headers: {