diff --git a/frontend/src/components/AppFramework.jsx b/frontend/src/components/AppFramework.jsx
index 3d786443..093c9550 100644
--- a/frontend/src/components/AppFramework.jsx
+++ b/frontend/src/components/AppFramework.jsx
@@ -53,13 +53,15 @@ export const findSpecificApp = (framework, inputcategory) => {
}
const category = inputcategory.toLowerCase().split(":")[0].trim()
-
- //console.log("findSpecificApp: ", category, framework)
if (category === "edr" || category === "eradication" || category === "edr & av") {
- if (framework["EDR & AV"] !== undefined && framework["EDR & AV"].name !== undefined) {
+ if (framework["EDR & AV"] !== undefined && framework["EDR & AV"].name !== undefined && framework["EDR & AV"].name !== "") {
return framework["EDR & AV"]
}
+ if (framework["edr"] !== undefined && framework["edr"].name !== undefined && framework["edr"].name !== "") {
+ return framework["edr"]
+ }
+
return {
name: "EDR :default",
large_image: parsedDatatypeImages()["EDR & AV"],
@@ -68,10 +70,14 @@ export const findSpecificApp = (framework, inputcategory) => {
id: "",
}
} else if (category === "communication" || category === "comms") {
- if (framework["Comms"] !== undefined && framework["Comms"].name !== undefined) {
+ if (framework["Comms"] !== undefined && framework["Comms"].name !== undefined && framework["Comms"].name !== "") {
return framework["Comms"]
}
+ if (framework["communication"] !== undefined && framework["communication"].name !== undefined && framework["communication"].name !== "") {
+ return framework["communication"]
+ }
+
return {
name: "COMMS :default",
large_image: parsedDatatypeImages()["COMMS"],
@@ -80,10 +86,14 @@ export const findSpecificApp = (framework, inputcategory) => {
id: "",
}
} else if (category === "email") {
- if (framework["Email"] !== undefined && framework["Email"].name !== undefined) {
+ if (framework["Email"] !== undefined && framework["Email"].name !== undefined && framework["Email"].name !== "") {
return framework["Email"]
}
+ if (framework["email"] !== undefined && framework["email"].name !== undefined && framework["email"].name !== "") {
+ return framework["email"]
+ }
+
return {
name: "COMMS :default",
large_image: parsedDatatypeImages()["COMMS"],
@@ -92,10 +102,14 @@ export const findSpecificApp = (framework, inputcategory) => {
id: "",
}
} else if (category === "assets") {
- if (framework["Assets"] !== undefined && framework["Assets"].name !== undefined) {
+ if (framework["Assets"] !== undefined && framework["Assets"].name !== undefined && framework["Assets"].name !== "") {
return framework["Assets"]
}
+ if (framework["assets"] !== undefined && framework["assets"].name !== undefined && framework["assets"].name !== "") {
+ return framework["assets"]
+ }
+
return {
name: "ASSETS :default",
large_image: parsedDatatypeImages()["ASSETS"],
@@ -104,10 +118,14 @@ export const findSpecificApp = (framework, inputcategory) => {
id: "",
}
} else if (category === "cases") {
- if (framework["Cases"] !== undefined && framework["Cases"].name !== undefined) {
+ if (framework["Cases"] !== undefined && framework["Cases"].name !== undefined && framework["Cases"].name !== "") {
return framework["Cases"]
}
+ if (framework["cases"] !== undefined && framework["cases"].name !== undefined && framework["cases"].name !== "") {
+ return framework["cases"]
+ }
+
return {
name: "CASES :default",
large_image: parsedDatatypeImages()["CASES"],
@@ -116,10 +134,14 @@ export const findSpecificApp = (framework, inputcategory) => {
id: "",
}
} else if (category === "iam") {
- if (framework["IAM"] !== undefined && framework["IAM"].name !== undefined) {
+ if (framework["IAM"] !== undefined && framework["IAM"].name !== undefined && framework["IAM"].name !== "") {
return framework["IAM"]
}
+ if (framework["iam"] !== undefined && framework["iam"].name !== undefined && framework["iam"].name !== "") {
+ return framework["iam"]
+ }
+
return {
name: "IAM :default",
large_image: parsedDatatypeImages()["IAM"],
@@ -128,10 +150,14 @@ export const findSpecificApp = (framework, inputcategory) => {
id: "",
}
} else if (category === "network") {
- if (framework["Network"] !== undefined && framework["Network"].name !== undefined) {
+ if (framework["Network"] !== undefined && framework["Network"].name !== undefined && framework["Network"].name !== "") {
return framework["Network"]
}
+ if (framework["network"] !== undefined && framework["network"].name !== undefined && framework["network"].name !== "") {
+ return framework["network"]
+ }
+
return {
name: "Network :default",
large_image: parsedDatatypeImages()["NETWORK"],
@@ -140,10 +166,14 @@ export const findSpecificApp = (framework, inputcategory) => {
id: "",
}
} else if (category === "intel") {
- if (framework["Intel"] !== undefined && framework["Intel"].name !== undefined) {
+ if (framework["Intel"] !== undefined && framework["Intel"].name !== undefined && framework["Intel"].name !== "") {
return framework["Intel"]
}
+ if (framework["intel"] !== undefined && framework["intel"].name !== undefined && framework["intel"].name !== "") {
+ return framework["intel"]
+ }
+
return {
name: "INTEL :default",
large_image: parsedDatatypeImages()["INTEL"],
@@ -152,9 +182,13 @@ export const findSpecificApp = (framework, inputcategory) => {
id: "",
}
} else if (category === "siem") {
- if (framework["SIEM"] !== undefined && framework["SIEM"].name !== undefined) {
+ if (framework["SIEM"] !== undefined && framework["SIEM"].name !== undefined && framework["SIEM"].name !== "") {
return framework["SIEM"]
}
+
+ if (framework["siem"] !== undefined && framework["siem"].name !== undefined && framework["siem"].name !== "") {
+ return framework["siem"]
+ }
return {
name: "SIEM :default",
diff --git a/frontend/src/components/AppSearchButtons.jsx b/frontend/src/components/AppSearchButtons.jsx
index 6bc3179a..4a4eef4e 100644
--- a/frontend/src/components/AppSearchButtons.jsx
+++ b/frontend/src/components/AppSearchButtons.jsx
@@ -47,6 +47,8 @@ const AppSearchButtons = (props) => {
const [newSelectedApp, setNewSelectedApp] = useState(undefined)
useEffect(() => {
+ console.log("UPDATED APP: ", newSelectedApp)
+
if (newSelectedApp !== undefined && setMissing != undefined) {
const submitAppFramework = {
"description": newSelectedApp.description,
@@ -136,8 +138,14 @@ const AppSearchButtons = (props) => {
const icon = foundApp.large_image
var foundAppImage = AppImage
- if (foundApp.name !== undefined && foundApp.name !== null && !foundApp.name.includes(":default")) {
- foundAppImage = foundApp.large_image
+ if (foundApp.name !== undefined && foundApp.name !== null && foundApp.name.length > 0 && !foundApp.name.includes(":default")) {
+
+ if (AppImage === undefined || AppImage === null || AppImage.length < 10) {
+ foundAppImage = foundApp.large_image
+ }
+ } else {
+ const newapp = findSpecificApp(appFramework, appType)
+ // const { userdata, globalUrl, appFramework, moreButton, finishedApps, appType, totalApps, index, onNodeSelect, setDiscoveryData, appName, AppImage, setDefaultSearch, discoveryData, checkLogin, setMissing, getAppFramework, } = props
}
let xsValue = 12;
@@ -204,9 +212,14 @@ const AppSearchButtons = (props) => {
{
+ {/*
{
+ */}
{
userdata,
globalUrl,
appFramework,
+ setAppFramework,
setActiveStep,
defaultSearch,
setDefaultSearch,
checkLogin,
+
} = props;
const [discoveryData, setDiscoveryData] = React.useState({})
const [selectionOpen, setSelectionOpen] = React.useState(false)
@@ -57,6 +60,7 @@ const AppSelection = props => {
const [moreButton, setMoreButton] = useState(false);
// const [mouseHoverIndex, setMouseHoverIndex] = useState(-1)
+ document.title = "Choose your apps"
const ref = useRef()
let navigate = useNavigate();
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
@@ -98,74 +102,75 @@ const AppSelection = props => {
else if (discoveryData === "IAM") {
appFramework.iam = submitNewApp
}
- setFrameworkItem(submitNewApp);
- setSelectionOpen(false);
- console.log("Selected app changed (effect)");
+
+ setFrameworkItem(submitNewApp)
+ setSelectionOpen(false)
+
+ if (setAppFramework !== undefined) {
+ setAppFramework(appFramework)
+ }
+ GetApps()
}, [newSelectedApp]);
+ const reloadAppButtons = (framework) => {
+ var tempApps = []
+ const lastApps = {}
+ let endTypes = ["network", "assets", "iam"]
+
+ if (framework === undefined || framework === null || Object.keys(framework).length === 0) {
+ //window.location.href = "/welcome"
+ return
+ }
+
+ Object.entries(framework).forEach(([key, value]) => {
+ // Overwrrite email properly
+ if (key.toLowerCase() === "communication") {
+ value["type"] = "email"
+ framework["email"] = value
+ return
+ }
+
+ if (key.toLowerCase() === "other") {
+ return
+ }
+
+ value.type = key;
+ if (endTypes.includes(value.type.toLowerCase())) {
+ lastApps[value.type] = value
+ return
+ }
+
+ if (lastPosted.type === value.type) {
+ value = lastPosted
+ }
+
+ tempApps.push(JSON.parse(JSON.stringify(value)));
+ });
+
+ tempApps.sort((a, b) => {
+ if (a.type.length > b.type.length) {
+ return -1;
+ } else if (a.type.length < b.type.length) {
+ return 1;
+ }
+ });
+
+ let lastType = lastPosted.type === undefined ? "" : lastPosted.type.toLowerCase()
+ if (endTypes.includes(lastType)) {
+ lastApps[lastPosted.type] = lastPosted
+ }
+
+ if (moreButton) {
+ tempApps.push(JSON.parse(JSON.stringify(lastApps["network"])))
+ tempApps.push(JSON.parse(JSON.stringify(lastApps["assets"])))
+ tempApps.push(JSON.parse(JSON.stringify(lastApps["iam"])))
+ }
+
+ setAppButtons(tempApps)
+ }
+
useEffect(() => {
- var tempApps = []
- if (tempApps.length === 0) {
- // Object.entries(appFramework).forEach(([key, value]) => {
- // value.type = key;
- // tempApps.push(value);
- // });
-
- // // Define the custom sorting order
- // const customSortingOrder = ["CASES", "SIEM", "ENDPOINT", "INTEL", "EMAIL"];
-
- const lastApps = {}
- let endTypes = ["network", "assets", "iam"]
-
- if (appFramework === undefined || appFramework === null || Object.keys(appFramework).length === 0) {
- //window.location.href = "/welcome"
- return
- }
-
-
- Object.entries(appFramework).forEach(([key, value]) => {
- if (key.toLowerCase() === "other" || key.toLowerCase() === "communication") {
- return
- }
-
- value.type = key;
-
- if (endTypes.includes(value.type.toLowerCase())) {
- lastApps[value.type] = value
- return
- }
-
- if (lastPosted.type === value.type) {
- value = lastPosted
- }
-
- tempApps.push(JSON.parse(JSON.stringify(value)));
- });
-
- tempApps.sort((a, b) => {
- if (a.type.length > b.type.length) {
- return -1;
- } else if (a.type.length < b.type.length) {
- return 1;
- }
- });
-
- let lastType = lastPosted.type === undefined ? "" : lastPosted.type.toLowerCase()
-
- if (endTypes.includes(lastType)) {
- lastApps[lastPosted.type] = lastPosted
- }
-
- if (moreButton) {
- tempApps.push(JSON.parse(JSON.stringify(lastApps["network"])))
- tempApps.push(JSON.parse(JSON.stringify(lastApps["assets"])))
- tempApps.push(JSON.parse(JSON.stringify(lastApps["iam"])))
- }
-
- setAppButtons(tempApps)
- console.log("Updated appButtons: ", appButtons)
- GetApps()
- }
+ reloadAppButtons(appFramework)
}, [lastPosted, moreButton])
if (appFramework === undefined || appFramework === null || Object.keys(appFramework).length === 0) {
@@ -174,11 +179,6 @@ const AppSelection = props => {
}
const setFrameworkItem = (data) => {
- console.log("Setting framework item: ", data, isCloud)
- // if (!isCloud) {
- // activateApp(data.id)
- // }
-
fetch(globalUrl + "/api/v1/apps/frameworkConfiguration", {
method: "POST",
headers: {
@@ -241,31 +241,39 @@ const AppSelection = props => {
body: JSON.stringify(data),
credentials: "include",
})
- .then((responseJson) => {
- if (responseJson === null) {
- console.log("null-response from server")
- const pretend_apps = [{
- "description": "TBD",
- "id": "TBD",
- "large_image": "",
- "name": "TBD",
- "type": "TBD"
- }]
+ .then((response) => {
+ return response.json()
+ })
+ .then((responseJson) => {
+ if (responseJson === null || responseJson === undefined) {
+ console.log("null-response from server")
+ const pretend_apps = [{
+ "description": "TBD",
+ "id": "TBD",
+ "large_image": "",
+ "name": "TBD",
+ "type": "TBD"
+ }]
- setApps(pretend_apps)
- return
- }
+ setApps(pretend_apps)
+ return
+ }
- if (responseJson.success === false) {
- console.log("error loading apps: ", responseJson)
- return
- }
+ if (responseJson.success === false) {
+ console.log("error loading apps: ", responseJson)
+ return
+ }
- setApps(responseJson);
- })
- .catch((error) => {
- console.log("App loading error: " + error.toString());
- })
+ if (setAppFramework !== undefined) {
+ setAppFramework(responseJson)
+ }
+
+ setApps(responseJson)
+ reloadAppButtons(responseJson)
+ })
+ .catch((error) => {
+ console.log("App loading error: " + error.toString());
+ })
}
const onNodeSelect = (label) => {
@@ -277,8 +285,6 @@ const AppSelection = props => {
});
}
- console.log("NODESELECT: ", label)
-
setDiscoveryData(label)
setSelectionOpen(true)
setDefaultSearch(label.charAt(0).toUpperCase() + (label.substring(1)).toLowerCase())
@@ -304,209 +310,225 @@ const AppSelection = props => {
};
return (
-
-
- {
- navigate('/welcome');
- window.location.reload();
- }}
- >
-
- Back
-
-
-
- {selectionOpen ? (
-
-
-
- {discoveryData}
-
-
-
- {
- setSelectionOpen(false)
- }}
- >
-
-
-
-
- {
- e.preventDefault();
- setSelectionOpen(false)
- setDefaultSearch("")
- const submitDeletedApp = {
- "description": "",
- "id": "remove",
- "name": "",
- "type": discoveryData
- }
- setFrameworkItem(submitDeletedApp)
- setNewSelectedApp({})
- setTimeout(() => {
- setDiscoveryData({})
- setFrameworkItem(submitDeletedApp)
- setNewSelectedApp({})
- }, 1000)
- //setAppName(discoveryData.cases.name)
- }}
- >
-
-
-
-
-
-
-
-
- ) : null}
-
- Find your apps
-
-
- Select the apps you work with and we will connect them for you.
-
-
- {appButtons.map((appData, index) => {
- // This is here due to a memory issue with setting apps properly
- if (appData.id === "remove") {
- console.log("Removed as appdata is overridden: ", appData)
+
+
+ {/*
+
+ {
+ navigate('/welcome');
+ window.location.reload();
+ }}
+ >
+
+ Back
+
+
+ */}
+
+ {selectionOpen ? (
+
+
+
+ {discoveryData}
+
+
+
+ {
+ setSelectionOpen(false)
+ }}
+ >
+
+
+
+
+ {
+ e.preventDefault();
+ setSelectionOpen(false)
+ setDefaultSearch("")
+ const submitDeletedApp = {
+ "description": "",
+ "id": "remove",
+ "name": "",
+ "type": discoveryData
+ }
- const appName = appData.name
- const AppImage = appData.large_image
- const appType = appData.type
+ setFrameworkItem(submitDeletedApp)
+ setNewSelectedApp({})
+ setTimeout(() => {
+ setDiscoveryData({})
+ setFrameworkItem(submitDeletedApp)
+ setNewSelectedApp({})
+ }, 200)
+ //setAppName(discoveryData.cases.name)
+ }}
+ >
+
+
+
+
+
+
+
+
+ ) : null}
+
+ Find your apps
+
+
+ Select the apps you work with and we will connect them for you.
+
+
+ {appButtons.map((appData, index) => {
+ // This is here due to a memory issue with setting apps properly
+ if (appData.id === "remove") {
+ appData = {
+ "count": 0,
+ "description": "",
+ "id": "",
+ "large_image": "",
+ "name": "",
+ "type": appData.type,
+ }
+ }
- return (
-
- )
- })}
-
-
- {!moreButton ? (
-
- {
- setMoreButton(true)
- setTimeout(() => {
- navigate("/welcome?tab=2")
- }, 250)
- }}
- >See More Apps
-
) : ""}
-
- {
- navigate("/welcome?tab=3")
- setActiveStep(2)
- }}>
- Continue
-
-
-
+ if (appData === undefined || appData === null || appData.name === undefined || appData.name === "") {
+ appData = {
+ "count": 0,
+ "description": "",
+ "id": "",
+ "large_image": "",
+ "name": "",
+ "type": appData.type,
+ }
+ }
+
+ //console.log("APP: ", appData)
+
+ const appName = appData.name
+ const AppImage = appData.large_image
+ const appType = appData.type
+
+ return (
+
+ )
+ })}
+
+
+ {!moreButton ? (
+
+ {
+ setMoreButton(true)
+
+ setTimeout(() => {
+ navigate("/welcome?tab=2")
+ }, 250)
+ }}
+ >See More Apps
+
) : ""}
+
+
+ {
+ navigate("/usecases2")
+ setActiveStep(2)
+ }}>
+ See usecases
+
+
+
+
)
}
diff --git a/frontend/src/components/Branding.jsx b/frontend/src/components/Branding.jsx
index de38ef86..39734084 100644
--- a/frontend/src/components/Branding.jsx
+++ b/frontend/src/components/Branding.jsx
@@ -122,7 +122,7 @@ const Branding = (props) => {
return (
- Branding
+ Partner Status & Branding
You can customize your organization's branding by uploading a logo, changing the color scheme and a lot more.
diff --git a/frontend/src/components/ConfigureWorkflow.jsx b/frontend/src/components/ConfigureWorkflow.jsx
index 6c8ae0ee..f7bbb7ed 100755
--- a/frontend/src/components/ConfigureWorkflow.jsx
+++ b/frontend/src/components/ConfigureWorkflow.jsx
@@ -792,8 +792,6 @@ const ConfigureWorkflow = (props) => {
}
parsedName = (parsedName.charAt(0).toUpperCase() + parsedName.slice(1)).replaceAll("_", " ");
-
- console.log("AUTH Action: ", action)
return (
{
- const { globalUrl, workflow, setWorkflow, modalOpen, setModalOpen, showUpload, usecases, setNewWorkflow, appFramework, isEditing, userdata, apps, } = props
+ const { globalUrl, workflow, setWorkflow, modalOpen, setModalOpen, showUpload, usecases, setNewWorkflow, appFramework, isEditing, userdata, apps, saveWorkflow, expanded, } = props
const [_, setUpdate] = React.useState(""); // Used for rendering, don't remove
const [submitLoading, setSubmitLoading] = React.useState(false);
- const [showMoreClicked, setShowMoreClicked] = React.useState(false);
+ const [showMoreClicked, setShowMoreClicked] = React.useState(expanded === true ? true : false);
const [innerWorkflow, setInnerWorkflow] = React.useState(workflow)
const [newWorkflowTags, setNewWorkflowTags] = React.useState(workflow.tags !== undefined && workflow.tags !== null ? JSON.parse(JSON.stringify(workflow.tags)) : [])
@@ -80,6 +80,8 @@ const EditWorkflow = (props) => {
const [dueDate, setDueDate] = React.useState(workflow.due_date !== undefined && workflow.due_date !== null && workflow.due_date !== 0 ? dayjs(workflow.due_date*1000) : dayjs().subtract(1, 'day'))
const [inputQuestions, setInputQuestions] = React.useState(workflow.input_questions !== undefined && workflow.input_questions !== null ? JSON.parse(JSON.stringify(workflow.input_questions)) : [])
+ const [inputMarkdown, setInputMarkdown] = React.useState(workflow.input_markdown !== undefined && workflow.input_markdown !== null ? workflow.input_markdown : "")
+ const [outputMarkdown, setOutputMarkdown] = React.useState(workflow.output_markdown !== undefined && workflow.output_markdown !== null ? workflow.output_markdown : "")
const classes = useStyles();
@@ -283,6 +285,7 @@ const EditWorkflow = (props) => {
}
innerWorkflow.input_questions = validfields
+ innerWorkflow.input_markdown = inputMarkdown
innerWorkflow.name = name
innerWorkflow.description = description
@@ -298,7 +301,13 @@ const EditWorkflow = (props) => {
innerWorkflow.due_date = new Date(`${dueDate["$y"]}-${dueDate["$M"]+1}-${dueDate["$D"]}`).getTime()/1000
}
- if (setNewWorkflow !== undefined) {
+ if (saveWorkflow !== undefined) {
+ saveWorkflow(innerWorkflow)
+
+ if (setWorkflow !== undefined) {
+ setWorkflow(innerWorkflow)
+ }
+ } else if (setNewWorkflow !== undefined) {
setNewWorkflow(
innerWorkflow.name,
innerWorkflow.description,
@@ -804,6 +813,40 @@ const EditWorkflow = (props) => {
+
+ {inputQuestions.length === 0 ? null :
+
+
+ Input Markdown
+
+ {
+ setInputMarkdown(e.target.value)
+ workflow.input_markdown = e.target.value
+ setWorkflow(workflow)
+ setUpdate(Math.random())
+ }}
+ />
+
+ {/*
+
+ Output Markdown
+
+
+ */}
+
+ }
+
diff --git a/frontend/src/components/NewHeader.jsx b/frontend/src/components/NewHeader.jsx
index e196c2cf..2afba2b0 100644
--- a/frontend/src/components/NewHeader.jsx
+++ b/frontend/src/components/NewHeader.jsx
@@ -171,7 +171,7 @@ const Header = (props) => {
setTooltipOpen(true);
};
- const topbar_var = "topbar_closed2"
+ const topbar_var = "topbar_closed4"
useEffect(() => {
const topbar = localStorage.getItem(topbar_var)
@@ -575,15 +575,15 @@ const Header = (props) => {
//globalUrl = responseJson.region_url
}
if (responseJson["reason"] === "SSO_REDIRECT") {
- setTimeout(() => {
- toast.info("Redirecting to SSO login page as SSO is required for this organization.")
- window.location.href = responseJson["url"]
- return
- }, 2000)
+ setTimeout(() => {
+ toast.info("Redirecting to SSO login page as SSO is required for this organization.")
+ window.location.href = responseJson["url"]
+ return
+ }, 2000)
} else {
- setTimeout(() => {
- window.location.reload()
- }, 2000);
+ setTimeout(() => {
+ window.location.reload()
+ }, 2000);
}
toast("Successfully changed active organization - refreshing!");
} else {
@@ -711,7 +711,7 @@ const Header = (props) => {
*/}
-
+
{
handleClose();
@@ -858,7 +858,7 @@ const Header = (props) => {
-
+
{
@@ -898,7 +898,7 @@ const Header = (props) => {
variant="text"
color="secondary"
className={classes.menuButton}
- style={{width:200}}
+ style={{ width: 200 }}
onClick={handleMenuOpen}
>
Pricing & Services
@@ -932,11 +932,10 @@ const Header = (props) => {
Training Courses
-
-
- handleMenuItemClick('/partners')}>
+
+ handleMenuItemClick('/partners')}>
- Partner Program
+ Partner Program
@@ -1031,7 +1030,7 @@ const Header = (props) => {
margin: "auto",
}}
>
-
+
{
- {/*
+ {/*
{
{/* {upgradeHovered ?
"Upgrade License"
: */}
- Upgrade
+ Upgrade
{/* } */}
@@ -1639,12 +1638,12 @@ const Header = (props) => {
const topbarHeight = showTopbar ? 40 : 0
const topbar = !isCloud || !showTopbar ? null :
- curpath === "/" || curpath.includes("/docs") || curpath === "/pricing" || curpath === "/contact" || curpath === "/search" || curpath === "/usecases" || curpath === "/training" || curpath === "/professional-services" ?
+ curpath === "/" || curpath.includes("/docs") || curpath === "/pricing" || curpath === "/contact" || curpath === "/search" || curpath === "/usecases" || curpath === "/usecases2" || curpath === "/training" || curpath === "/professional-services" ?
-
+
{/* Shuffle 1.4.0 is out! Read more about */}
- New Public
+ New
{
ReactGA.event({
@@ -1655,11 +1654,11 @@ const Header = (props) => {
navigate("/training")
- }} style={{ cursor: "pointer", textDecoration: "none", fontWeight: 600, color: "rgba(255,255,255,0.8)" }}>
- Training Dates Released
+ }} style={{ cursor: "pointer", textDecoration: "none", fontWeight: 600, color: "rgba(255,255,255,0.9)" }}>
+ Public Training
- Ahead!
+ Dates Released!
{
setShowTopbar(false)
diff --git a/frontend/src/components/Oauth2Auth.jsx b/frontend/src/components/Oauth2Auth.jsx
index e9e794b2..45324bd6 100755
--- a/frontend/src/components/Oauth2Auth.jsx
+++ b/frontend/src/components/Oauth2Auth.jsx
@@ -299,12 +299,11 @@ const AuthenticationOauth2 = (props) => {
const handleOauth2Request = (client_id, client_secret, oauth_url, scopes, admin_consent, prompt, skipScopeReplace) => {
- console.log("SKIP SCOPE: ", skipScopeReplace)
if (skipScopeReplace === false || skipScopeReplace === undefined) {
console.log("Selected scopes: ", selectedScopes)
if (selectedScopes !== undefined && selectedScopes !== null && selectedScopes.length > 0) {
- toast("Using your scopes instead of the default ones")
+ //toast("Using your scopes instead of the default ones")
scopes = selectedScopes
}
}
@@ -484,7 +483,27 @@ const AuthenticationOauth2 = (props) => {
url = `${authenticationType.redirect_uri}?client_id=${client_id}&redirect_uri=${redirectUri}&response_type=code&prompt=admin_consent&scope=${resources}&state=${state}&access_type=offline`;
}
+ if (url !== undefined && url !== null && url.length > 0) {
+ if (url.toLowerCase().includes("{tenant")) {
+ // Check location of {tenant, then find the next } and replace with 'common'. Make sure next } is AFTER {tenant
+ try {
+ const tenantIndex = url.toLowerCase().indexOf("{tenant")
+ const substring = url.substring(tenantIndex)
+ const nextBracket = substring.indexOf("}")
+ const newUrl = url.substring(0, tenantIndex) + "common" + url.substring(tenantIndex + nextBracket + 1)
+ url = newUrl
+ } catch (e) {
+ console.log("Failed to replace {tenant} with common: ", e)
+ }
+
+ }
+ }
+
+ /*
console.log("OAUTH2 URL: ", url)
+ return
+ */
+
// Force new consent
//const url = `${authenticationType.redirect_uri}?client_id=${client_id}&redirect_uri=${redirectUri}&response_type=code&scope=${resources}&prompt=consent&state=${state}&access_type=offline`;
diff --git a/frontend/src/components/ParsedAction.jsx b/frontend/src/components/ParsedAction.jsx
index d4cf0541..fb462566 100755
--- a/frontend/src/components/ParsedAction.jsx
+++ b/frontend/src/components/ParsedAction.jsx
@@ -600,8 +600,9 @@ const ParsedAction = (props) => {
let errorVars = [];
if(paramvalue.includes("$")){
let actions = workflow.actions?.map((action) => {
- return "$"+action.label.toLowerCase();
+ return "$"+action.label?.toLowerCase();
})
+
if(newActionList?.length > 0){
let appParentActions = parentActionList?.map(action => "$" + action.name.toLowerCase());
let notPresentAction = actions?.filter((action) => !appParentActions?.includes(action))
@@ -629,9 +630,9 @@ const ParsedAction = (props) => {
let regex = /(^|[^\\])\$/;
if (regex.test(paramvalue)) {
if(message.length > 0){
- message += "\nUse \"\\$\" instead of \"$\".";
+ message += "\nUse \"\\$\" instead of \"$\".";
}else{
- message = "Use \"\\$\" instead of \"$\".";
+ message = "Use \"\\$\" instead of \"$\" to escape variables.";
}
}
}
@@ -640,7 +641,7 @@ const ParsedAction = (props) => {
setSelectedActionParameters(newParameters);
setActionlist(newActionList);
}, [workflow.execution_variables,paramUpdate, workflow.workflow_variables, workflowExecutions, workflow, selectedAction, listCache, getParents,setNewSelectedAction]);
- console.log("Selected Action:", selectedAction)
+
useEffect(() => {
selectedNameChange(appActionName)
@@ -1280,12 +1281,14 @@ const ParsedAction = (props) => {
}
}
- var authWritten = false;
+ var authWritten = false;
var noAppSelected = false
- 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
+ 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
+ }
}
}
@@ -1320,8 +1323,14 @@ const ParsedAction = (props) => {
}
});
}
- setHiddenDescription(false)
- document.activeElement.blur();
+
+ document.activeElement.blur();
+
+ const disabledUiBox = localStorage.getItem("disabled_ui_box")
+ if (disabledUiBox === "true") {
+ } else {
+ setHiddenDescription(false)
+ }
}}
>
@@ -1874,11 +1883,9 @@ const ParsedAction = (props) => {
}
}
- console.log("Old found: ", workflow.actions[key].parameters[subkey].value)
const extralength = newname.length-parsedBaseLabel.length
param.value = param.value.substring(0, foundindex) + newname + param.value.substring(foundindex-extralength+newname.length, param.value.length)
- console.log("New: ", workflow.actions[key].parameters[subkey].value)
} else {
break
}
@@ -1990,18 +1997,25 @@ const ParsedAction = (props) => {
selectedAction.authentication_id = "";
for (let [key,keyval] in Object.entries(selectedAction.parameters)) {
- if (selectedAction.parameters[key].configuration) {
+ if (selectedAction.parameters[key].configuration === false) {
+ console.log("FIELDSKIP: ", selectedAction.parameters[key].name)
+ continue
+ }
- if (selectedAction.parameters[key].example !== undefined && selectedAction.parameters[key].example !== null && selectedAction.parameters[key].example !== "") {
- if (selectedAction.parameters[key].example.toLowerCase().includes("api") || selectedAction.parameters[key].example.toLowerCase().includes("key") || selectedAction.parameters[key].example.toLowerCase().includes("pass")) {
- selectedAction.parameters[key].value = ""
- } else {
- selectedAction.parameters[key].value = selectedAction.parameters[key].example
- }
- } else {
- selectedAction.parameters[key].value = ""
- }
- }
+ 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());
@@ -2020,7 +2034,11 @@ const ParsedAction = (props) => {
for (let [key,keyval] in Object.entries(selectedAction.parameters)) {
//console.log(selectedAction.parameters[key])
if (selectedAction.parameters[key].configuration) {
- selectedAction.parameters[key].value = "authgroup controlled"
+
+ if (selectedAction.parameters[key].name === "url" && authenticationType?.type === "oauth2-app") {
+ } else {
+ selectedAction.parameters[key].value = "authgroup controlled"
+ }
}
}
@@ -2130,6 +2148,14 @@ const ParsedAction = (props) => {
) : null}
+ {selectedAction.authentication_id === "authgroups" && (authGroups === undefined || authGroups === null || authGroups.length === 0) ?
+
+
+ Create your first Authentication group
+
+
+ : null}
+
{showEnvironment !== undefined && showEnvironment && environments.length > 1 && !isIntegration ? (
@@ -2611,7 +2637,7 @@ const ParsedAction = (props) => {
marginBottom: hideExtraTypes ? 50 : 200,
}}
> {
- Object.getOwnPropertyNames(selectedAction).length > 0 && selectedActionParameters.length > 0 ?
+ selectedActionParameters !== undefined && selectedActionParameters !== null && Object.getOwnPropertyNames(selectedAction).length > 0 && selectedActionParameters.length > 0 ?
{isIntegration ?
apps !== undefined && apps !== null && apps.length > 0 ?
@@ -2907,20 +2933,59 @@ const ParsedAction = (props) => {
}
if (data.value === "authgroup controlled") {
- return null
+ if (data?.name === "url" && authenticationType?.type === "oauth2-app") {
+ } else {
+ return null
+ }
}
- // selectedAction.selectedAuthentication = e.target.value
- // selectedAction.authentication_id = e.target.value.id
+ /*
if (
(selectedAction.auth_not_required !== undefined && !selectedAction.auth_not_required) &&
+ selectedActionParameters[count] !== undefined &&
+ selectedActionParameters[count] !== null &&
selectedActionParameters[count].value !== undefined &&
+ selectedAction.parameters[count] !== undefined &&
+ selectedAction.parameters[count] !== null &&
selectedAction.parameters[count].value !== undefined &&
selectedAction.selectedAuthentication !== undefined &&
selectedAction.selectedAuthentication.fields !== undefined &&
selectedAction.selectedAuthentication.fields[data.name] !==
undefined
) {
+ */
+
+ /*
+ if (selectedAction.selectedAuthentication !== undefined && selectedAction.selectedAuthentication.fields !== undefined && selectedAction.selectedAuthentication.fields[data.name] !== undefined) {
+
+ // This sets the placeholder in the frontend. (Replaced in backend)
+ selectedActionParameters[count].value = selectedAction.selectedAuthentication.fields[data.name];
+ selectedAction.parameters[count].value = selectedAction.selectedAuthentication.fields[data.name];
+ setSelectedAction(selectedAction);
+ //setUpdate(Math.random())
+
+ if (authWritten) {
+ return null
+ }
+
+ authWritten = true
+ return (
+
+ Authentication fields are hidden
+
+ )
+ }
+ */
+
+
+ //!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)
selectedActionParameters[count].value =
selectedAction.selectedAuthentication.fields[data.name];
@@ -2947,6 +3012,7 @@ const ParsedAction = (props) => {
);
}
+
// 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) {
@@ -3063,6 +3129,7 @@ const ParsedAction = (props) => {
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) {
@@ -3278,7 +3345,7 @@ const ParsedAction = (props) => {
const description = data.description === undefined ? "" : data?.description;
const tooltipDescription = (
-
{
{tmpitem.charAt(0).toUpperCase() + tmpitem.slice(1)}
{
- event.preventDefault();
- event.stopPropagation();
- }}
-
- onClick={() => {
- setUiBox("closed")
- const inputElement = document.getElementById(uiBox);
- if (inputElement) {
- inputElement.focus();
- }
- }}>
+ onClick={() => {
+ setUiBox("closed")
+
+ /*
+ const inputElement = document.getElementById(uiBox);
+ if (inputElement) {
+ inputElement.focus();
+ }
+ */
+ }}
+ >
@@ -3316,7 +3381,7 @@ const ParsedAction = (props) => {
Description: {description}
- Ex. : {data?.example.length > 0 ? data.example : "No example available"}
+ Ex. : {data?.example?.length > 0 ? data.example : "No example available"}
{
data?.configuration === true ?
@@ -3326,15 +3391,31 @@ const ParsedAction = (props) => {
) : null
}
+
+
{
+ e.preventDefault()
+ e.stopPropagation()
+
+ localStorage.setItem("disabled_ui_box", "true")
+ setUiBox("closed")
+ }}>
+
+ Don't show again
+
+
);
+
var datafield = (
{
}}
>
{
setScrollConfig(scrollConfig)
}
}}
- id={clickedFieldId}
rows={data.name.startsWith("${") && data.name.endsWith("}") ? 2 : rows}
color="primary"
// defaultValue={data.value}
@@ -3458,15 +3539,28 @@ const ParsedAction = (props) => {
handleParamChange(event, count, data)
}}
onFocus={(event) => {
- setUiBox(event.target.id)
-
+ // 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)
}
- setUiBox("closed")
+
+ // 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")
+ }
}}
/>
@@ -3664,7 +3758,6 @@ const ParsedAction = (props) => {
}
- //console.log("FIELD VALUE: ", data.value)
//const regexp = new RegExp("\W+\.", "g")
//let match
//while ((match = regexp.exec(data.value)) !== null) {
@@ -3680,15 +3773,13 @@ const ParsedAction = (props) => {
// }
//}
- // Basic helpertext
-
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
- }
- }
+ //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 (
@@ -3736,26 +3827,11 @@ const ParsedAction = (props) => {
}}
onBlur={(event) => {}}
/>
- );
- //const fileId = "6daabec1-892b-469c-b603-c902e47223a9"
- //datafield = `SHOW FILES FROM OTHER NODES? Filename: ${selectedActionParameters[count].value}`
- /*
- if (selectedActionParameters[count].value != fileId) {
- changeActionParameter(fileId, count, data)
- setUpdate(Math.random())
-
- }
- */
+ )
} 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
+ (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) {
@@ -3833,7 +3909,7 @@ const ParsedAction = (props) => {
}
if (data.field_active === false) {
- return null;
+ return null;
}
@@ -4212,12 +4288,18 @@ const ParsedAction = (props) => {
const buttonTitle = `Authenticate ${selectedApp.name.replaceAll("_", " ")}`
const hasAutocomplete = data?.autocompleted === true
-
-
if (data.variant === undefined || data.variant === null) {
data.variant = "STATIC_VALUE"
}
+ /*
+ if (data?.configuration === true) {
+ if (data?.name === "url" && authenticationType?.type === "oauth2-app") {
+ data.configuration = false
+ }
+ }
+ */
+
return (
{/* {hideBodyButton} */}
diff --git a/frontend/src/components/WelcomeForm2.jsx b/frontend/src/components/WelcomeForm2.jsx
index 3c9200e0..47c040db 100644
--- a/frontend/src/components/WelcomeForm2.jsx
+++ b/frontend/src/components/WelcomeForm2.jsx
@@ -64,6 +64,7 @@ const WelcomeForm = (props) => {
discoveryWrapper,
setDiscoveryWrapper,
appFramework,
+ setAppFramework,
getFramework,
activeStep,
setActiveStep,
@@ -435,20 +436,6 @@ const WelcomeForm = (props) => {
setSkipped(newSkipped);
};
- const handleBack = () => {
- setActiveStep((prevActiveStep) => prevActiveStep - 1);
-
- if (activeStep === 2) {
- setDiscoveryWrapper({});
-
- if (getFramework !== undefined) {
- getFramework();
- }
- navigate("/welcome?tab=2");
- } else if (activeStep === 1) {
- navigate("/welcome?tab=1");
- }
- };
const handleReset = () => {
setActiveStep(0);
@@ -645,6 +632,7 @@ const WelcomeForm = (props) => {
globalUrl={globalUrl}
userdata={userdata}
appFramework={appFramework}
+ setAppFramework={setAppFramework}
setActiveStep={setActiveStep}
defaultSearch={defaultSearch}
setDefaultSearch={setDefaultSearch}
diff --git a/frontend/src/components/WorkflowValidationTimeline.jsx b/frontend/src/components/WorkflowValidationTimeline.jsx
index 2b4b2c13..04eb2314 100644
--- a/frontend/src/components/WorkflowValidationTimeline.jsx
+++ b/frontend/src/components/WorkflowValidationTimeline.jsx
@@ -4,21 +4,28 @@ import { toast } from "react-toastify"
import {
Tooltip,
+ Chip,
Typography,
+ IconButton,
Avatar,
AvatarGroup,
} from "@mui/material"
+import {
+ ErrorOutline as ErrorOutlineIcon,
+} from "@mui/icons-material"
+
import {
green,
yellow,
red,
+ grey,
} from "../views/AngularWorkflow.jsx"
import { validateJson, GetIconInfo } from "../views/Workflows.jsx";
import theme from "../theme.jsx";
-const itemHeight = 40
+const itemHeight = 24
export const getParentNodes = (workflow, action) => {
if (action === undefined || action === null) {
@@ -123,6 +130,8 @@ export const getParentNodes = (workflow, action) => {
const WorkflowValidationTimeline = (props) => {
const { workflow, originalWorkflow, apps, getParents, execution} = props
+ const showMiddle = false
+
if (workflow === undefined || workflow === null) {
return null
@@ -196,6 +205,15 @@ const WorkflowValidationTimeline = (props) => {
parents = getParentNodes(workflow, action)
}
+ if (action.app_name === "Integration Framework" || action.app_name === "Integration") {
+ for (var paramkey in action.parameters) {
+ const param = action.parameters[paramkey]
+ if (param.name === "app_name") {
+ action.app_name = param.value.charAt(0).toUpperCase() + param.value.slice(1)
+ }
+ }
+ }
+
//const parents = getParentNodes(workflow, action)
//console.log("PARENTS", key, parents)
if (parents !== undefined && parents !== null && parents.length > 0) {
@@ -219,6 +237,20 @@ const WorkflowValidationTimeline = (props) => {
continue
}
+ for (var branchkey in workflow.branches) {
+ const branch = workflow.branches[branchkey]
+
+ // Checking for OUTBOUND branches from it.
+ // This will mean it's NOT the last node and is easy to visualize
+ if (branch.source_id !== trigger.id) {
+ continue
+ }
+
+ trigger.order = 2
+ }
+
+
+ // Just in case (:
if (workflow.actions.find((element) => element.id === trigger.id) === undefined) {
newactions.push(trigger)
//workflow.actions.push(trigger)
@@ -242,14 +274,19 @@ const WorkflowValidationTimeline = (props) => {
})
// FIXME: Add other relevant items as well from subflows (?)
- var nodecolor = "grey"
- var branchcolor = "grey"
+ var nodecolor = grey
+ var branchcolor = grey
var skipped = false
var previousTools = false
+ // Use this variable to control visualization
+ //const showMiddle = false
+ // border: workflow.validation.valid ? `2px solid ${green}` : "1px solid rgba(255,255,255,0.4)",
+ var middleError = ""
+ var startBranchColor = ""
return (
-
+
{relevantactions.map((action, index) => {
action.result = {}
@@ -274,13 +311,13 @@ const WorkflowValidationTimeline = (props) => {
if (validate.result.success === true) {
branchcolor = green
} else {
- branchcolor = "grey"
+ branchcolor = grey
}
}
} else if (action.status === "SKIPPED") {
- branchcolor = "grey"
+ branchcolor = grey
} else {
if (action.status === undefined) {
branchcolor = green
@@ -292,13 +329,13 @@ const WorkflowValidationTimeline = (props) => {
previousTools = true
if (startnodeId !== action.id) {
- return null
+ //return null
}
} else {
if (action.status === "SUCCESS") {
nodecolor = green
} else if (action.status === "SKIPPED") {
- nodecolor = "grey"
+ nodecolor = grey
} else {
if (action.status === undefined) {
nodecolor = green
@@ -308,7 +345,7 @@ const WorkflowValidationTimeline = (props) => {
}
}
} else {
- nodecolor = "grey"
+ nodecolor = grey
}
if (action.status === "SKIPPED") {
@@ -340,18 +377,44 @@ const WorkflowValidationTimeline = (props) => {
}
var founderror = ""
+ //console.log("WORKFLOW VALIDATION: ", workflow.validation)
if (workflow.validation !== undefined && workflow.validation !== null && workflow.validation.errors !== undefined && workflow.validation.errors !== null) {
const foundError = workflow.validation.errors.find((element) => element.action_id === action.id)
if (foundError !== undefined) {
founderror = foundError.error
- nodecolor = yellow
- branchcolor = yellow
+ nodecolor = red
+ branchcolor = red
+ } else {
+ //console.log("NO ERROR: ", action.id)
+ }
+ }
+
+ if (!showMiddle && relevantactions.length > 2 && index > 0 && index === relevantactions.length - 2) {
+ if (founderror.length > 0) {
+ middleError += founderror+"\n"
+ }
+
+ if (index === relevantactions.length-2 && relevantactions.length > 2) {
+
+ const selectedIcon = middleError.length > 0 ?
+
+
+
+
+
+ : null
+
+ return (
+ selectedIcon
+ )
+ } else {
+ return null
}
}
if (skipped && !lastitem) {
- nodecolor = "grey"
- branchcolor = "grey"
+ nodecolor = grey
+ branchcolor = grey
}
if (previousTools) {
@@ -379,14 +442,60 @@ const WorkflowValidationTimeline = (props) => {
}
+
var flex = index !== 0 && index !== relevantactions.length - 1 ? 1 : 3
+
+ if (nodecolor === green) {
+ branchcolor = green
+ } else if (nodecolor === yellow) {
+ branchcolor = yellow
+ } else if (nodecolor === red) {
+ branchcolor = red
+ }
+
+ if (index === 0) {
+ startBranchColor = branchcolor
+ }
+
+ if (lastitem && middleError.length === 0) {
+ branchcolor = startBranchColor
+ }
+
const branchTooltip = branchcolor === yellow ? "Check nodes for errors" : ""
+ const appname = action.app_name.replaceAll('_', ' ').slice(0, 16)
+
+ const chipBackground = nodecolor === green ? "rgba(2,203,112, 0.2)" : nodecolor === grey ? "#494949": "rgba(245,52,52,0.8)"
+ const chipColor = nodecolor === green ? "#02cb70" : nodecolor === grey ? "#CDCDCD" : "white"
+
+ //const ballcolor = lastitem ? nodecolor : branchcolor
+ const ballcolor = branchcolor
+ const ballsize = 8
+ const topMargin = 20
+
+ const chipStyle = {
+ height: 40,
+ minWidth: 125,
+ maxWidth: 125,
+ borderRadius: 50,
+ color: chipColor,
+ backgroundColor: chipBackground,
+
+ //border: `2px solid ${chipBackground}`,
+ //backgroundColor: "rgba(0,0,0,0.0)",
+ }
+
+ if (image === "") {
+ console.log("MISSING IMAGE: ", appname, image, action)
+ }
return (
{lastitem ?
-
+
: null}
@@ -419,30 +528,36 @@ const WorkflowValidationTimeline = (props) => {
:
- {founderror.length > 0 ? founderror : `${action.app_name.replaceAll('_', ' ')}`}
+ {founderror.length > 0 ? founderror : `App: ${appname}`}
} placement="top">
-
- {image !== "" ?
-
- : null}
-
+
+ {image !== "" ?
+
+ : null}
+
+ } />
+
}
{lastitem ? null :
-
+
}
diff --git a/frontend/src/views/Admin.jsx b/frontend/src/views/Admin.jsx
index 050e139c..d166a9cf 100755
--- a/frontend/src/views/Admin.jsx
+++ b/frontend/src/views/Admin.jsx
@@ -2361,7 +2361,7 @@ If you're interested, please let me know a time that works for you, or set up a
1: "cloud_sync",
2: "priorities",
3: "billing",
- 4: "branding",
+ 4: "partner",
};
const setConfig = (event, inputValue) => {
@@ -3753,7 +3753,7 @@ If you're interested, please let me know a time that works for you, or set up a
>
Edit Details />
Limits & Cloud Sync />
- Priorities />
+ Notifications />
Billing & Stats />
Partner />
@@ -4383,9 +4383,11 @@ If you're interested, please let me know a time that works for you, or set up a
{
+ onChange={(event) => {
setIpSelected(event.target.value);
- await getLogs(event.target.value, userLogViewing.id);
+ getLogs(event.target.value, userLogViewing.id);
+
+
}}
>
{(() => {
@@ -4410,6 +4412,7 @@ If you're interested, please let me know a time that works for you, or set up a
})()}
+
{logsLoading && ipSelected.length !== 0 ? (
- {logs.map((data, index) => (
+
+
+
+
+
+ {logs.map((data, index) => {
+ //console.log("LOG: ", data)
+
+ return (
// redirect user to logs
// using request id or trace id
- ))}
+ )})}
@@ -4567,6 +4601,11 @@ If you're interested, please let me know a time that works for you, or set up a
onClick={() => {
setLogsViewModal(true);
setUserLogViewing(data);
+
+ if (userLogViewing.login_info !== undefined && userLogViewing.login_info !== null && userLogViewing.login_info.length > 0) {
+ getLogs(userLogViewing.login_info[0].ip, userLogViewing.id)
+ setIpSelected(userLogViewing.login_info[0].ip);
+ }
}}
>
{data.username}
@@ -6077,7 +6116,7 @@ If you're interested, please let me know a time that works for you, or set up a
) : null;
- const getLogs = async (ip, userId) => {
+ const getLogs = (ip, userId) => {
setLogsLoading(true);
console.log("logs loading: ", logsLoading);
fetch(`${globalUrl}/api/v1/users/${userId}/audit?user_ip=${ip}`, {
diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx
index c6a1cb20..d95c0d9b 100755
--- a/frontend/src/views/AngularWorkflow.jsx
+++ b/frontend/src/views/AngularWorkflow.jsx
@@ -319,9 +319,12 @@ export function SetJsonDotnotation(jsonInput, inputKey) {
return jsonInput;
}
-export const green = "#86c142";
+//export const green = "#86c142";
+export const green = "#02CB70"
export const yellow = "#FECC00";
-export const red = "#ff3632";
+//export const red = "#ff3632";
+export const red = "#F53434";
+export const grey = "#b0b0b0";
export function removeParam(key, sourceURL) {
if (sourceURL === undefined) {
@@ -673,10 +676,16 @@ const releaseToConnectLabel = "Release to Connect"
const loadAppConfig = (appId, select) => {
if (appId === undefined || appId === null || appId.length === 0) {
+ console.log("No appId to load")
+ return
+ }
+
+ if (appId === "integration") {
return
}
if (loadedApps.includes(appId)) {
+ console.log("App already loaded: ", appId)
return
}
@@ -699,8 +708,6 @@ const releaseToConnectLabel = "Release to Connect"
if (responseJson.success === true && responseJson.app !== undefined && responseJson.app !== null && responseJson.app.length > 0) {
// Base64 decode into json
const foundapp = JSON.parse(atob(responseJson.app))
- console.log("Checked app: ", foundapp)
-
const selectedAppActions = selectedApp.actions === undefined || selectedApp.actions === null ? [] : selectedApp.actions
if (foundapp.actions !== undefined && foundapp.actions !== null && foundapp.actions.length > selectedAppActions.length) {
@@ -749,6 +756,7 @@ const releaseToConnectLabel = "Release to Connect"
if (selectedApp.actions === undefined || selectedApp.actions === null || selectedApp.actions.length > 1) {
return
} else {
+
if (selectedApp.id !== undefined && selectedApp.id !== null && selectedApp.id.length > 0) {
loadAppConfig(selectedApp.id, true)
}
@@ -1241,8 +1249,14 @@ const releaseToConnectLabel = "Release to Connect"
})
.then((response) => {
if (response.status !== 200) {
- console.log("Status not 200 for setting app auth :O!");
- }
+ console.log("Status not 200 for setting app auth :O!");
+
+ if (response.status === 400) {
+ toast.error("Failed setting new auth. Please try again", {
+ "autoClose": true,
+ })
+ }
+ }
return response.json();
})
@@ -1253,8 +1267,6 @@ const releaseToConnectLabel = "Release to Connect"
"autoClose": false,
})
-
-
} else {
if (refresh === true) {
getAppAuthentication(true, true, true)
@@ -2153,6 +2165,7 @@ const releaseToConnectLabel = "Release to Connect"
})
.catch((error) => {
setSavingState(0);
+ setExecutionRequestStarted(false)
console.log("Save workflow error: ", error.toString());
toast.warn("Failed to save the workflow. Is the network down?")
});
@@ -2346,6 +2359,7 @@ const releaseToConnectLabel = "Release to Connect"
//toast(error.toString());
setExecutionRequestStarted(false)
console.log("Execute workflow err: ", error.toString());
+ toast.warn("Failed to run the workflow. Is the network down?")
});
})
};
@@ -5092,6 +5106,10 @@ const releaseToConnectLabel = "Release to Connect"
}
*/
+ if (curapp !== undefined && curapp !== null && curapp.id !== undefined && curapp.id !== null && curapp.id.length > 0) {
+ loadAppConfig(curapp.id, true)
+ }
+
if (!curapp || curapp === undefined) {
const tmpapp = {
name: curaction.app_name,
@@ -5753,8 +5771,10 @@ const releaseToConnectLabel = "Release to Connect"
setLastSaved(false);
const edge = event.target.data();
- //console.log("edge added: ", edge)
+ console.log("edge added: ", edge)
if (edge.source === undefined && edge.target === undefined) {
+ console.log("Edge added without source or target")
+
return
}
@@ -5768,8 +5788,9 @@ const releaseToConnectLabel = "Release to Connect"
const sourcenode = cy.getElementById(edge.source)
const destinationnode = cy.getElementById(edge.target)
if (sourcenode === undefined || sourcenode === null || destinationnode === undefined || destinationnode === null) {
+ console.log("Source or destination node is undefined or null: ", sourcenode, destinationnode)
} else {
- //console.log("Edge added: Is it a trigger? If so, check if it already has a branch and remove it: ", sourcenode.data())
+ console.log("Edge added: Is it a trigger? If so, check if it already has a branch and remove it: ", sourcenode.data())
if (sourcenode.data("type") === "TRIGGER") {
if (sourcenode.data("app_name") !== "Shuffle Workflow" && sourcenode.data("app_name") !== "User Input") {
setTimeout(() => {
@@ -5778,7 +5799,6 @@ const releaseToConnectLabel = "Release to Connect"
(data) => data.data.source === edge.source && data.data.id !== edge.id
)
- console.log("Node: ", targetedge)
if (targetedge !== -1) {
event.target.remove()
@@ -5810,14 +5830,15 @@ const releaseToConnectLabel = "Release to Connect"
);
if (targetnode !== -1) {
if (workflow.triggers[targetnode].app_name === "User Input" || workflow.triggers[targetnode].app_name === "Shuffle Workflow" || workflow.triggers[targetnode].app_name === "Shuffle Subflow") {
+ console.log("User Input or Shuffle Workflow")
} else {
- toast("Can't have triggers as target of branch");
- event.target.remove();
+ toast("Can't have triggers as target of branch")
+ event.target.remove()
}
}
const eventTarget = event.target.target()
- //console.log("BUTTON ADDED! Find parent from: ", eventTarget)
+ console.log("BUTTON ADDED! Find parent from: ", eventTarget)
if (eventTarget.data("isButton") === true) {
const parentNode = cy.getElementById(eventTarget.data("attachedTo"))
event.target.remove()
@@ -5843,13 +5864,10 @@ const releaseToConnectLabel = "Release to Connect"
}
}
- if (
- eventTarget.data("isDescriptor") === true ||
- eventTarget.data("type") === "COMMENT"
- ) {
+ if (eventTarget.data("isDescriptor") === true || eventTarget.data("type") === "COMMENT") {
console.log("Removing because of descriptor or comment")
- event.target.remove();
- return;
+ event.target.remove()
+ return
}
targetnode = -1;
@@ -5858,26 +5876,33 @@ const releaseToConnectLabel = "Release to Connect"
// dest == source && source == dest
// dest == dest && source == source
// backend: check all children? to stop recursion
+ //
var found = false;
for (let branchkey in workflow.branches) {
- if (
- workflow.branches[branchkey].destination_id === edge.source &&
- workflow.branches[branchkey].source_id === edge.target
- ) {
- toast("A branch in the opposite direction already exists");
- event.target.remove();
- found = true;
- break;
- } else if (
- workflow.branches[branchkey].destination_id === edge.target &&
- workflow.branches[branchkey].source_id === edge.source
- ) {
- //toast("That branch already exists");
- event.target.remove();
+ if (workflow.branches[branchkey].destination_id === edge.source && workflow.branches[branchkey].source_id === edge.target) {
+ toast("A branch in the opposite direction already exists")
+ event.target.remove()
+ found = true
+ break
+ }
- found = true;
- break;
- } else if (edge.target === workflow.start) {
+ if (workflow.branches[branchkey].destination_id === edge.target && workflow.branches[branchkey].source_id === edge.source) {
+
+ console.log("That branch already exists: ", workflow.branches[branchkey])
+ const foundbranch = cy.getElementById(workflow.branches[branchkey].id)
+ if (foundbranch !== undefined && foundbranch !== null && foundbranch.data() !== undefined && foundbranch.data() !== null) {
+ console.log("Removing branch: ", foundbranch.data())
+
+ event.target.remove()
+
+ found = true
+ break
+ } else {
+ console.log("Old branch didn't exist afterall. Remove.")
+ }
+ }
+
+ if (edge.target === workflow.start) {
targetnode = workflow.triggers.findIndex(
(data) => data.id === edge.source
);
@@ -5890,7 +5915,9 @@ const releaseToConnectLabel = "Release to Connect"
found = true;
}
- } else if (edge.source === workflow.branches[branchkey].source_id) {
+ }
+
+ if (edge.source === workflow.branches[branchkey].source_id) {
// FIXME: Verify multi-target for triggers
// 1. Check if destination exists
// 2. Check if source is a trigger
@@ -7522,7 +7549,7 @@ const releaseToConnectLabel = "Release to Connect"
// Reset cytoscape nodes and branches
if (cy !== undefined && cy !== null) {
if (inputworkflow.actions !== undefined && inputworkflow.actions !== null && inputworkflow.actions.length > 0) {
- cy.remove('*')
+ //cy.remove('*')
}
}
@@ -9181,6 +9208,10 @@ const releaseToConnectLabel = "Release to Connect"
const actionIndex = startIndex < 0 ? 0 : startIndex
if (app.actions[actionIndex] === undefined || app.actions[actionIndex] === null) {
+ if (app.id !== undefined && app.id !== null) {
+ loadAppConfig(app.id, false)
+ }
+
console.log("No actions found for app: ", app)
return
}
@@ -9409,7 +9440,7 @@ const releaseToConnectLabel = "Release to Connect"
setHover(true)
- if (app.actions !== undefined && app.actions !== null && app.actions.length === 1) {
+ if (app.actions !== undefined && (app.actions === null || app.actions.length === 1)) {
console.log("HOVERING: ", app.id)
loadAppConfig(app.id, false)
}
@@ -10295,6 +10326,10 @@ const releaseToConnectLabel = "Release to Connect"
var handled = [];
var results = [];
+ if (cy === undefined || cy === null) {
+ return []
+ }
+
// maxiter = max amount of parent nodes to loop
// also handles breaks if there are issues
var iterations = 0;
@@ -10934,6 +10969,10 @@ const releaseToConnectLabel = "Release to Connect"
checked = workflow.auth_groups.includes(data.id)
}
+ if (data === undefined || data === null || data.app_auths === undefined || data.app_auths === null || data.app_auths.length === 0) {
+ return null
+ }
+
return (
0 ) {
console.log("WORKFLOW: ", e.target.value);
- const startnode = e.target.value.actions.find((action) => action.id === e.target.value.start);
+ const startnode = e?.target?.value?.actions?.find((action) => action.id === e.target.value.start);
if (startnode !== undefined && startnode !== null) {
@@ -16643,9 +16682,17 @@ const releaseToConnectLabel = "Release to Connect"
}
const foundnode = cy.nodes().filter((node) => {
- return node.data().label === word
+ const nodelabel = node.data("label")
+ if (nodelabel === undefined || nodelabel === null) {
+ return false
+ }
+
+ console.log("Node: ", nodelabel.toLowerCase(), "Word: ", word.toLowerCase())
+ return nodelabel.toLowerCase() === word.toLowerCase()
})
+ console.log("FOUND: ", foundnode)
+
if (foundnode === undefined || foundnode === null || foundnode.length === 0) {
return
}
@@ -18986,7 +19033,7 @@ const releaseToConnectLabel = "Release to Connect"
) : null}
- {executionData.workflow !== undefined && executionData.workflow !== null && executionData.status !== "EXECUTING" ?
+ {userdata.support === true && executionData.workflow !== undefined && executionData.workflow !== null && executionData.status !== "EXECUTING" ?
0) {
// FIXME: Find image from cytoscape action
@@ -20837,6 +20884,25 @@ const releaseToConnectLabel = "Release to Connect"
/>
{selectedApp.authentication.parameters.map((data, index) => {
+ // FIXME: Look for relevant fields in the action that may already be filled in with the same name
+ if (data.value === "" || data.value === null || data.value === undefined || data.name === "url") {
+ if (selectedAction !== undefined && selectedAction !== null && selectedAction.parameters !== undefined && selectedAction.parameters !== null) {
+ for (var fieldkey in selectedAction.parameters) {
+ const field = selectedAction.parameters[fieldkey]
+ if (field.name !== data.name) {
+ continue
+ }
+
+ if (field.value !== undefined && field.value !== null && field.value.length > 0) {
+ data.value = field.value
+ data.autocomplete = true
+ break
+ }
+ }
+ }
+ }
+
+
return (
@@ -21617,8 +21683,8 @@ const releaseToConnectLabel = "Release to Connect"
// Should render if it's not the same as workflow.edited
console.log("Clicked revision: ", newrevision)
- setLastSaved(false)
- setSelectedVersion(newrevision);
+ setLastSaved(false)
+ setSelectedVersion(newrevision);
setWorkflow(newrevision)
setSelectedAction({});
setSelectedApp({})
@@ -21638,18 +21704,19 @@ const releaseToConnectLabel = "Release to Connect"
cy.removeListener("free");
cy.removeListener("cxttap");
- //cy.remove('*')
setElements([])
- }
- // Remove all edges
+ cy.remove('*')
cy.edges().remove()
cy.nodes().remove()
+ }
+
// Remove all cy nodes
setTimeout(() => {
+ //toast("Running setupgraph with new revision. Actions: " + newrevision.actions.length)
setupGraph(newrevision)
- }, 100)
+ }, 250)
// Re-adding cytoscape triggers
diff --git a/frontend/src/views/AppCreator.jsx b/frontend/src/views/AppCreator.jsx
index e9db7736..c9e0b218 100755
--- a/frontend/src/views/AppCreator.jsx
+++ b/frontend/src/views/AppCreator.jsx
@@ -228,6 +228,8 @@ const parseCurl = (s) => {
};
// Basically CRUD for each category + special
+// These are already tracked in the shuffle/shuffle-shared/blobs.go file
+// as backend should be used for managing this long-term
export const appCategories = [
{
"name": "Communication",
diff --git a/frontend/src/views/Apps.jsx b/frontend/src/views/Apps.jsx
index 770e37c8..b79ad590 100755
--- a/frontend/src/views/Apps.jsx
+++ b/frontend/src/views/Apps.jsx
@@ -1542,12 +1542,15 @@ const Apps = (props) => {
flex: 1,
padding: 15,
margin: 10,
+ paddingTop: 25,
backgroundColor: hover ? theme.palette.surfaceColor : "transparent",
border: hover ? "1px solid #f85a3e" : "1px solid rgba(255,255,255,0.3)",
cursor: hover ? "pointer" : "default",
textAlign: "center",
minHeight: 150,
maxHeight: 150,
+
+ borderRadius: theme.palette.borderRadius,
}}
>
{icon}
@@ -1569,7 +1572,7 @@ const Apps = (props) => {
{
setOpenApiModal(true)
}}
diff --git a/frontend/src/views/RunWorkflow.jsx b/frontend/src/views/RunWorkflow.jsx
index 4d8dc1bf..8dbc019a 100644
--- a/frontend/src/views/RunWorkflow.jsx
+++ b/frontend/src/views/RunWorkflow.jsx
@@ -2,14 +2,17 @@
import React, {useState, useEffect} from 'react';
import ReactDOM from "react-dom"
-import { ToastContainer, toast } from "react-toastify"
-import { useInterval } from "react-powerhooks";
-import { makeStyles } from '@mui/material/styles';
+import { green, yellow, red, grey} from "./AngularWorkflow.jsx";
+import { CodeHandler, Img, OuterLink, } from "../views/Docs.jsx";
import { useNavigate, Link, useParams } from "react-router-dom";
-import {isMobile} from "react-device-detect";
-import theme from '../theme.jsx';
import { validateJson, GetIconInfo } from "./Workflows.jsx";
-import { green, yellow } from "./AngularWorkflow.jsx";
+import EditWorkflow from "../components/EditWorkflow.jsx"
+import { ToastContainer, toast } from "react-toastify"
+import { makeStyles } from '@mui/material/styles';
+import { useInterval } from "react-powerhooks";
+import { isMobile } from "react-device-detect";
+import Markdown from "react-markdown";
+import theme from '../theme.jsx';
import {
Tooltip,
@@ -21,6 +24,10 @@ import {
Paper,
Typography,
Divider,
+
+ Dialog,
+ DialogTitle,
+ DialogContent,
} from '@mui/material';
import {
@@ -35,14 +42,15 @@ const hrefStyle = {
const bodyDivStyle = {
margin: "auto",
- marginTop: 100,
width: isMobile? "100%":"500px",
position: "relative",
+
+ marginTop: 25,
}
const RunWorkflow = (defaultprops) => {
- const { globalUrl, isLoaded, isLoggedIn, setIsLoggedIn, setCookie, register, serverside } = defaultprops;
+ const { globalUrl, userdata, isLoaded, isLoggedIn, setIsLoggedIn, setCookie, register, serverside } = defaultprops;
let navigate = useNavigate();
const [_, setUpdate] = useState(""); // Used to force rendring, don't remove
@@ -58,15 +66,16 @@ const RunWorkflow = (defaultprops) => {
const [apps, setApps] = React.useState([]);
const [buttonClicked, setButtonClicked] = React.useState("");
const [foundSourcenode, setFoundSourcenode] = React.useState(undefined);
+ const [editWorkflowModalOpen, setEditWorkflowModalOpen] = React.useState(false)
+ const [sharingOpen, setSharingOpen] = React.useState(false)
const boxStyle = {
color: "white",
- paddingLeft: "30px",
- paddingRight: "30px",
- paddingBottom: "30px",
- paddingTop: "30px",
+ padding: 50,
backgroundColor: theme.palette.surfaceColor,
marginBottom: 150,
+ borderRadius: 25,
+ minHeight: 500,
}
const params = useParams();
@@ -74,7 +83,7 @@ const RunWorkflow = (defaultprops) => {
props.match = {}
props.match.params = params
- const defaultTitle = "Run Workflow"
+ const defaultTitle = workflow.name !== undefined ? workflow.name : "Run Workflow"
if (document != undefined && document.title != defaultTitle) {
document.title = defaultTitle
}
@@ -96,16 +105,42 @@ const RunWorkflow = (defaultprops) => {
return true
}
+ const saveWorkflow = (workflow) => {
+ const url = `${globalUrl}/api/v1/workflows/${workflow.id}`
+ fetch(url, {
+ method: "PUT",
+ headers: {
+ "Content-Type": "application/json",
+ Accept: "application/json",
+ },
+ credentials: "include",
+ body: JSON.stringify(workflow),
+ })
+ .then((response) => {
+ if (response.status !== 200) {
+ console.log("Status not 200 for workflows :O!");
+ }
+
+ return response.json();
+ })
+ .then((responseJson) => {
+ toast.success("Saved workflow")
+ })
+ .catch((error) => {
+ toast.error("Save workflow error: " + error)
+ });
+
+ }
const getApps = () => {
- fetch(globalUrl + "/api/v1/apps", {
- method: "GET",
- headers: {
- "Content-Type": "application/json",
- Accept: "application/json",
- },
- credentials: "include",
- })
+ fetch(globalUrl + "/api/v1/apps", {
+ method: "GET",
+ headers: {
+ "Content-Type": "application/json",
+ Accept: "application/json",
+ },
+ credentials: "include",
+ })
.then((response) => {
if (response.status !== 200) {
console.log("Status not 200 for apps :O!");
@@ -829,52 +864,75 @@ const RunWorkflow = (defaultprops) => {
const basedata =
- {
+ {
if (isCloud) {
ReactGA.event({
category: "welcome",