Workflow syncs
This commit is contained in:
@@ -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>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|||||||
@@ -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": `` },
|
||||||
|
|||||||
@@ -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 ? (
|
||||||
|
|||||||
@@ -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: {
|
||||||
|
|||||||
Reference in New Issue
Block a user