A ton of workflow related & onboarding changes

This commit is contained in:
Frikky
2024-08-15 23:22:25 +02:00
parent eaf8acfec0
commit 7d8a52cfff
18 changed files with 1151 additions and 593 deletions
+45 -11
View File
@@ -53,13 +53,15 @@ export const findSpecificApp = (framework, inputcategory) => {
} }
const category = inputcategory.toLowerCase().split(":")[0].trim() const category = inputcategory.toLowerCase().split(":")[0].trim()
//console.log("findSpecificApp: ", category, framework)
if (category === "edr" || category === "eradication" || category === "edr & av") { 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"] return framework["EDR & AV"]
} }
if (framework["edr"] !== undefined && framework["edr"].name !== undefined && framework["edr"].name !== "") {
return framework["edr"]
}
return { return {
name: "EDR :default", name: "EDR :default",
large_image: parsedDatatypeImages()["EDR & AV"], large_image: parsedDatatypeImages()["EDR & AV"],
@@ -68,10 +70,14 @@ export const findSpecificApp = (framework, inputcategory) => {
id: "", id: "",
} }
} else if (category === "communication" || category === "comms") { } 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"] return framework["Comms"]
} }
if (framework["communication"] !== undefined && framework["communication"].name !== undefined && framework["communication"].name !== "") {
return framework["communication"]
}
return { return {
name: "COMMS :default", name: "COMMS :default",
large_image: parsedDatatypeImages()["COMMS"], large_image: parsedDatatypeImages()["COMMS"],
@@ -80,10 +86,14 @@ export const findSpecificApp = (framework, inputcategory) => {
id: "", id: "",
} }
} else if (category === "email") { } 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"] return framework["Email"]
} }
if (framework["email"] !== undefined && framework["email"].name !== undefined && framework["email"].name !== "") {
return framework["email"]
}
return { return {
name: "COMMS :default", name: "COMMS :default",
large_image: parsedDatatypeImages()["COMMS"], large_image: parsedDatatypeImages()["COMMS"],
@@ -92,10 +102,14 @@ export const findSpecificApp = (framework, inputcategory) => {
id: "", id: "",
} }
} else if (category === "assets") { } 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"] return framework["Assets"]
} }
if (framework["assets"] !== undefined && framework["assets"].name !== undefined && framework["assets"].name !== "") {
return framework["assets"]
}
return { return {
name: "ASSETS :default", name: "ASSETS :default",
large_image: parsedDatatypeImages()["ASSETS"], large_image: parsedDatatypeImages()["ASSETS"],
@@ -104,10 +118,14 @@ export const findSpecificApp = (framework, inputcategory) => {
id: "", id: "",
} }
} else if (category === "cases") { } 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"] return framework["Cases"]
} }
if (framework["cases"] !== undefined && framework["cases"].name !== undefined && framework["cases"].name !== "") {
return framework["cases"]
}
return { return {
name: "CASES :default", name: "CASES :default",
large_image: parsedDatatypeImages()["CASES"], large_image: parsedDatatypeImages()["CASES"],
@@ -116,10 +134,14 @@ export const findSpecificApp = (framework, inputcategory) => {
id: "", id: "",
} }
} else if (category === "iam") { } 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"] return framework["IAM"]
} }
if (framework["iam"] !== undefined && framework["iam"].name !== undefined && framework["iam"].name !== "") {
return framework["iam"]
}
return { return {
name: "IAM :default", name: "IAM :default",
large_image: parsedDatatypeImages()["IAM"], large_image: parsedDatatypeImages()["IAM"],
@@ -128,10 +150,14 @@ export const findSpecificApp = (framework, inputcategory) => {
id: "", id: "",
} }
} else if (category === "network") { } 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"] return framework["Network"]
} }
if (framework["network"] !== undefined && framework["network"].name !== undefined && framework["network"].name !== "") {
return framework["network"]
}
return { return {
name: "Network :default", name: "Network :default",
large_image: parsedDatatypeImages()["NETWORK"], large_image: parsedDatatypeImages()["NETWORK"],
@@ -140,10 +166,14 @@ export const findSpecificApp = (framework, inputcategory) => {
id: "", id: "",
} }
} else if (category === "intel") { } 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"] return framework["Intel"]
} }
if (framework["intel"] !== undefined && framework["intel"].name !== undefined && framework["intel"].name !== "") {
return framework["intel"]
}
return { return {
name: "INTEL :default", name: "INTEL :default",
large_image: parsedDatatypeImages()["INTEL"], large_image: parsedDatatypeImages()["INTEL"],
@@ -152,9 +182,13 @@ export const findSpecificApp = (framework, inputcategory) => {
id: "", id: "",
} }
} else if (category === "siem") { } 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"] return framework["SIEM"]
} }
if (framework["siem"] !== undefined && framework["siem"].name !== undefined && framework["siem"].name !== "") {
return framework["siem"]
}
return { return {
name: "SIEM :default", name: "SIEM :default",
+19 -4
View File
@@ -47,6 +47,8 @@ const AppSearchButtons = (props) => {
const [newSelectedApp, setNewSelectedApp] = useState(undefined) const [newSelectedApp, setNewSelectedApp] = useState(undefined)
useEffect(() => { useEffect(() => {
console.log("UPDATED APP: ", newSelectedApp)
if (newSelectedApp !== undefined && setMissing != undefined) { if (newSelectedApp !== undefined && setMissing != undefined) {
const submitAppFramework = { const submitAppFramework = {
"description": newSelectedApp.description, "description": newSelectedApp.description,
@@ -136,8 +138,14 @@ const AppSearchButtons = (props) => {
const icon = foundApp.large_image const icon = foundApp.large_image
var foundAppImage = AppImage var foundAppImage = AppImage
if (foundApp.name !== undefined && foundApp.name !== null && !foundApp.name.includes(":default")) { if (foundApp.name !== undefined && foundApp.name !== null && foundApp.name.length > 0 && !foundApp.name.includes(":default")) {
foundAppImage = foundApp.large_image
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; let xsValue = 12;
@@ -204,9 +212,14 @@ const AppSearchButtons = (props) => {
<IconButton <IconButton
style={{ style={{
flex: 1, flex: 1,
position: "absolute",
right: 0,
top: 10,
height: 10,
// width: 224, // width: 224,
marginLeft: discoveryData === ("communication") ? 112 : 200, //marginLeft: discoveryData === ("communication") ? 112 : 200,
width: "100%", //width: "100%",
marginBottom: 23, marginBottom: 23,
fontSize: 16, fontSize: 16,
background: "rgba(33, 33, 33, 1)", background: "rgba(33, 33, 33, 1)",
@@ -220,6 +233,7 @@ const AppSearchButtons = (props) => {
<Closeicon style={{ width: 16 }} /> <Closeicon style={{ width: 16 }} />
</IconButton> </IconButton>
</Tooltip> </Tooltip>
{/*
<Tooltip <Tooltip
title="Delete app" title="Delete app"
placement="bottom" placement="bottom"
@@ -259,6 +273,7 @@ const AppSearchButtons = (props) => {
<DeleteIcon style={{ height: 15, width: 15, }} /> <DeleteIcon style={{ height: 15, width: 15, }} />
</IconButton> </IconButton>
</Tooltip> </Tooltip>
*/}
</div> </div>
</div> </div>
<div <div
+315 -293
View File
@@ -23,6 +23,7 @@ import {
Paper, Paper,
TextField, TextField,
Collapse, Collapse,
Fade,
IconButton, IconButton,
Avatar, Avatar,
ButtonBase, ButtonBase,
@@ -41,10 +42,12 @@ const AppSelection = props => {
userdata, userdata,
globalUrl, globalUrl,
appFramework, appFramework,
setAppFramework,
setActiveStep, setActiveStep,
defaultSearch, defaultSearch,
setDefaultSearch, setDefaultSearch,
checkLogin, checkLogin,
} = props; } = props;
const [discoveryData, setDiscoveryData] = React.useState({}) const [discoveryData, setDiscoveryData] = React.useState({})
const [selectionOpen, setSelectionOpen] = React.useState(false) const [selectionOpen, setSelectionOpen] = React.useState(false)
@@ -57,6 +60,7 @@ const AppSelection = props => {
const [moreButton, setMoreButton] = useState(false); const [moreButton, setMoreButton] = useState(false);
// const [mouseHoverIndex, setMouseHoverIndex] = useState(-1) // const [mouseHoverIndex, setMouseHoverIndex] = useState(-1)
document.title = "Choose your apps"
const ref = useRef() const ref = useRef()
let navigate = useNavigate(); let navigate = useNavigate();
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
@@ -98,74 +102,75 @@ const AppSelection = props => {
else if (discoveryData === "IAM") { else if (discoveryData === "IAM") {
appFramework.iam = submitNewApp appFramework.iam = submitNewApp
} }
setFrameworkItem(submitNewApp);
setSelectionOpen(false); setFrameworkItem(submitNewApp)
console.log("Selected app changed (effect)"); setSelectionOpen(false)
if (setAppFramework !== undefined) {
setAppFramework(appFramework)
}
GetApps()
}, [newSelectedApp]); }, [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(() => { useEffect(() => {
var tempApps = [] reloadAppButtons(appFramework)
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()
}
}, [lastPosted, moreButton]) }, [lastPosted, moreButton])
if (appFramework === undefined || appFramework === null || Object.keys(appFramework).length === 0) { if (appFramework === undefined || appFramework === null || Object.keys(appFramework).length === 0) {
@@ -174,11 +179,6 @@ const AppSelection = props => {
} }
const setFrameworkItem = (data) => { const setFrameworkItem = (data) => {
console.log("Setting framework item: ", data, isCloud)
// if (!isCloud) {
// activateApp(data.id)
// }
fetch(globalUrl + "/api/v1/apps/frameworkConfiguration", { fetch(globalUrl + "/api/v1/apps/frameworkConfiguration", {
method: "POST", method: "POST",
headers: { headers: {
@@ -241,31 +241,39 @@ const AppSelection = props => {
body: JSON.stringify(data), body: JSON.stringify(data),
credentials: "include", credentials: "include",
}) })
.then((responseJson) => { .then((response) => {
if (responseJson === null) { return response.json()
console.log("null-response from server") })
const pretend_apps = [{ .then((responseJson) => {
"description": "TBD", if (responseJson === null || responseJson === undefined) {
"id": "TBD", console.log("null-response from server")
"large_image": "", const pretend_apps = [{
"name": "TBD", "description": "TBD",
"type": "TBD" "id": "TBD",
}] "large_image": "",
"name": "TBD",
"type": "TBD"
}]
setApps(pretend_apps) setApps(pretend_apps)
return return
} }
if (responseJson.success === false) { if (responseJson.success === false) {
console.log("error loading apps: ", responseJson) console.log("error loading apps: ", responseJson)
return return
} }
setApps(responseJson); if (setAppFramework !== undefined) {
}) setAppFramework(responseJson)
.catch((error) => { }
console.log("App loading error: " + error.toString());
}) setApps(responseJson)
reloadAppButtons(responseJson)
})
.catch((error) => {
console.log("App loading error: " + error.toString());
})
} }
const onNodeSelect = (label) => { const onNodeSelect = (label) => {
@@ -277,8 +285,6 @@ const AppSelection = props => {
}); });
} }
console.log("NODESELECT: ", label)
setDiscoveryData(label) setDiscoveryData(label)
setSelectionOpen(true) setSelectionOpen(true)
setDefaultSearch(label.charAt(0).toUpperCase() + (label.substring(1)).toLowerCase()) setDefaultSearch(label.charAt(0).toUpperCase() + (label.substring(1)).toLowerCase())
@@ -304,209 +310,225 @@ const AppSelection = props => {
}; };
return ( return (
<Collapse in={true}> <Fade in={true} timeout={1250}>
<Tooltip <div>
title="Back" {/*
placement="top" <Tooltip
style={{ zIndex: 10011 }} title="Back"
> placement="top"
<IconButton style={{ zIndex: 10011 }}
style={{ >
}} <IconButton
onClick={() => { style={{
navigate('/welcome'); }}
window.location.reload(); onClick={() => {
}} navigate('/welcome');
> window.location.reload();
<ArrowBackIcon style={{ width: 20 }} /> }}
<Typography style={{fontSize : 16, marginLeft : 2}}>Back</Typography> >
</IconButton> <ArrowBackIcon style={{ width: 20 }} />
</Tooltip> <Typography style={{fontSize : 16, marginLeft : 2}}>Back</Typography>
<div </IconButton>
style={{ </Tooltip>
// minHeight: sizing, */}
// maxHeight: sizing, <div
marginTop: 10, style={{
width: isMobile ? 350 : 500, // minHeight: sizing,
marginBottom: 25, // maxHeight: sizing,
textAlign: isMobile ? "center" : null, marginTop: 10,
}} width: isMobile ? 350 : 500,
> marginBottom: 25,
{selectionOpen ? ( textAlign: isMobile ? "center" : null,
<div }}
style={{ >
width: isMobile ? 225 : 319, {selectionOpen ? (
height: 395, <div
flexShrink: 0, style={{
marginLeft: 70, width: isMobile ? 225 : 319,
marginTop: 68, height: 395,
position: "absolute", flexShrink: 0,
zIndex: 100, marginLeft: 70,
borderRadius: 6, marginTop: 68,
border: "1px solid var(--Container-Stroke, #494949)", position: "absolute",
background: "var(--Container, #212121)", zIndex: 100,
boxShadow: "8px 8px 32px 24px rgba(0, 0, 0, 0.16)", borderRadius: 6,
}} border: "1px solid var(--Container-Stroke, #494949)",
> background: "var(--Container, #212121)",
<div style={{ display: "flex" }}> boxShadow: "8px 8px 32px 24px rgba(0, 0, 0, 0.16)",
<div style={{ display: "flex", textAlign: "center", textTransform: "capitalize" }}> }}
<Typography style={{ padding: 16, color: "#FFFFFF", textTransform: "capitalize" }}> {discoveryData} </Typography> >
</div> <div style={{ display: "flex" }}>
<div style={{ display: "flex" }}> <div style={{ display: "flex", textAlign: "center", textTransform: "capitalize" }}>
<Tooltip <Typography style={{ padding: 16, color: "#FFFFFF", textTransform: "capitalize" }}> {discoveryData} </Typography>
title="Close" </div>
placement="top" <div style={{ display: "flex" }}>
style={{ zIndex: 10011 }} <Tooltip
> title="Close"
<IconButton placement="top"
style={{ style={{ zIndex: 10011 }}
flex: 1, >
// width: 224, <IconButton
marginLeft: discoveryData === ("communication") ? 112 : 200, style={{
width: "100%", flex: 1,
marginBottom: 23,
fontSize: 16,
background: "rgba(33, 33, 33, 1)",
borderColor: "rgba(33, 33, 33, 1)",
borderRadius: 8,
}}
onClick={() => {
setSelectionOpen(false)
}}
>
<CloseIcon style={{ width: 16 }} />
</IconButton>
</Tooltip>
<Tooltip
title="Delete app"
placement="bottom"
style={{ zIndex: 10011 }}
>
<IconButton
style={{ zIndex: 12501, position: "absolute", top: 32, right: 16 }}
onClick={(e) => {
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)
}}
>
<DeleteIcon style={{ color: "white", height: 15, width: 15, }} />
</IconButton>
</Tooltip>
</div>
</div>
<div
style={{ width: "100%", border: "1px #494949 solid" }}
/>
<AppSearch
defaultSearch={defaultSearch}
newSelectedApp={newSelectedApp}
setNewSelectedApp={setNewSelectedApp}
userdata={userdata}
// cy={cy}
/>
</div>
) : null}
<Typography
variant="h4"
style={{
marginLeft: 8,
marginTop: isMobile ? null : 40,
marginRight: 30,
marginBottom: 0,
}}
color="rgba(241, 241, 241, 1)"
>
Find your apps
</Typography>
<Typography
variant="body2"
style={{
marginLeft: 8,
marginTop: 10,
marginRight: 30,
marginBottom: 40,
}}
color="rgba(158, 158, 158, 1)"
>
Select the apps you work with and we will connect them for you.
</Typography>
<Grid rowSpacing={1} columnSpacing={2} container >
{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)
appData = { position: "absolute",
"count": 0, top: 0,
"description": "", right: 6,
"id": "", }}
"large_image": "", onClick={() => {
"name": "", setSelectionOpen(false)
"type": appData.type, }}
} >
} <CloseIcon style={{ width: 16 }} />
</IconButton>
</Tooltip>
<Tooltip
title="Remove selection"
placement="bottom"
style={{ zIndex: 10011 }}
>
<IconButton
style={{ zIndex: 12501, position: "absolute", top: 26, right: 6, }}
onClick={(e) => {
e.preventDefault();
setSelectionOpen(false)
setDefaultSearch("")
const submitDeletedApp = {
"description": "",
"id": "remove",
"name": "",
"type": discoveryData
}
const appName = appData.name setFrameworkItem(submitDeletedApp)
const AppImage = appData.large_image setNewSelectedApp({})
const appType = appData.type setTimeout(() => {
setDiscoveryData({})
setFrameworkItem(submitDeletedApp)
setNewSelectedApp({})
}, 200)
//setAppName(discoveryData.cases.name)
}}
>
<DeleteIcon style={{ color: "white", height: 15, width: 15, }} />
</IconButton>
</Tooltip>
</div>
</div>
<div
style={{ width: "100%", border: "1px #494949 solid" }}
/>
<AppSearch
defaultSearch={defaultSearch}
newSelectedApp={newSelectedApp}
setNewSelectedApp={setNewSelectedApp}
userdata={userdata}
// cy={cy}
/>
</div>
) : null}
<Typography
variant="h4"
style={{
marginLeft: 8,
marginTop: isMobile ? null : 40,
marginRight: 30,
marginBottom: 0,
}}
color="rgba(241, 241, 241, 1)"
>
Find your apps
</Typography>
<Typography
variant="body2"
style={{
marginLeft: 8,
marginTop: 10,
marginRight: 30,
marginBottom: 40,
}}
color="rgba(158, 158, 158, 1)"
>
Select the apps you work with and we will connect them for you.
</Typography>
<Grid rowSpacing={1} columnSpacing={2} container >
{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 (
<AppSearchButtons
appFramework={appFramework}
index={index}
totalApps={appButtons.length}
appName={appName}
appType={appType}
AppImage={AppImage}
defaultSearch={defaultSearch}
finishedApps={finishedApps}
onNodeSelect={onNodeSelect}
discoveryData={discoveryData}
setDiscoveryData={setDiscoveryData}
setDefaultSearch={setDefaultSearch}
apps={apps}
setMoreButton={setMoreButton}
moreButton={moreButton}
/>
)
})}
</Grid>
</div>
{!moreButton ? (
<div style={{ width: "100%", marginLeft: isMobile ? 80 : 200, marginBottom: 20, textAlign: isMobile ? "center" : null }}>
<Link style={{ color: "#FF8444" }} onClick={() => {
setMoreButton(true)
setTimeout(() => { if (appData === undefined || appData === null || appData.name === undefined || appData.name === "") {
navigate("/welcome?tab=2") appData = {
}, 250) "count": 0,
}} "description": "",
>See More Apps</Link> "id": "",
</div>) : ""} "large_image": "",
<div style={{ flexDirection: "row", width: isMobile ? 340 : null, textAlign: isMobile ? "center" : null }}> "name": "",
<Button variant="contained" type="submit" fullWidth style={bottomButtonStyle} onClick={() => { "type": appData.type,
navigate("/welcome?tab=3") }
setActiveStep(2) }
}}>
Continue //console.log("APP: ", appData)
</Button>
</div> const appName = appData.name
</Collapse> const AppImage = appData.large_image
const appType = appData.type
return (
<AppSearchButtons
appFramework={appFramework}
index={index}
totalApps={appButtons.length}
appName={appName}
appType={appType}
AppImage={AppImage}
defaultSearch={defaultSearch}
finishedApps={finishedApps}
onNodeSelect={onNodeSelect}
discoveryData={discoveryData}
setDiscoveryData={setDiscoveryData}
setDefaultSearch={setDefaultSearch}
apps={apps}
setMoreButton={setMoreButton}
moreButton={moreButton}
/>
)
})}
</Grid>
</div>
{!moreButton ? (
<div style={{ width: "100%", marginLeft: isMobile ? 80 : 200, marginBottom: 20, textAlign: isMobile ? "center" : null }}>
<Link style={{ color: "#FF8444" }} onClick={() => {
setMoreButton(true)
setTimeout(() => {
navigate("/welcome?tab=2")
}, 250)
}}
>See More Apps</Link>
</div>) : ""}
<div style={{ flexDirection: "row", width: isMobile ? 340 : null, textAlign: isMobile ? "center" : null }}>
<Button variant="contained" type="submit" fullWidth style={bottomButtonStyle} onClick={() => {
navigate("/usecases2")
setActiveStep(2)
}}>
See usecases
</Button>
</div>
</div>
</Fade>
) )
} }
+1 -1
View File
@@ -122,7 +122,7 @@ const Branding = (props) => {
return ( return (
<div style={{ width: clickedFromOrgTab? 1030: "auto", padding: 27, height: "auto", backgroundColor: '#212121', borderRadius: '16px', }}> <div style={{ width: clickedFromOrgTab? 1030: "auto", padding: 27, height: "auto", backgroundColor: '#212121', borderRadius: '16px', }}>
<h2 style={{marginTop: clickedFromOrgTab ?0:null,}}> <h2 style={{marginTop: clickedFromOrgTab ?0:null,}}>
Branding Partner Status & Branding
</h2> </h2>
<Typography variant="body1" color="textSecondary" style={{ marginTop: 20, marginBottom: 10 }}> <Typography variant="body1" color="textSecondary" style={{ marginTop: 20, marginBottom: 10 }}>
You can customize your organization's branding by uploading a logo, changing the color scheme and a lot more. You can customize your organization's branding by uploading a logo, changing the color scheme and a lot more.
@@ -792,8 +792,6 @@ const ConfigureWorkflow = (props) => {
} }
parsedName = (parsedName.charAt(0).toUpperCase() + parsedName.slice(1)).replaceAll("_", " "); parsedName = (parsedName.charAt(0).toUpperCase() + parsedName.slice(1)).replaceAll("_", " ");
console.log("AUTH Action: ", action)
return ( return (
<ListItem <ListItem
style={{padding: 0, display: "flex", flexDirection: "column", }} style={{padding: 0, display: "flex", flexDirection: "column", }}
+46 -3
View File
@@ -63,12 +63,12 @@ import {
} from "@mui/icons-material"; } from "@mui/icons-material";
const EditWorkflow = (props) => { const EditWorkflow = (props) => {
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 [_, setUpdate] = React.useState(""); // Used for rendering, don't remove
const [submitLoading, setSubmitLoading] = React.useState(false); 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 [innerWorkflow, setInnerWorkflow] = React.useState(workflow)
const [newWorkflowTags, setNewWorkflowTags] = React.useState(workflow.tags !== undefined && workflow.tags !== null ? JSON.parse(JSON.stringify(workflow.tags)) : []) 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 [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 [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(); const classes = useStyles();
@@ -283,6 +285,7 @@ const EditWorkflow = (props) => {
} }
innerWorkflow.input_questions = validfields innerWorkflow.input_questions = validfields
innerWorkflow.input_markdown = inputMarkdown
innerWorkflow.name = name innerWorkflow.name = name
innerWorkflow.description = description innerWorkflow.description = description
@@ -298,7 +301,13 @@ const EditWorkflow = (props) => {
innerWorkflow.due_date = new Date(`${dueDate["$y"]}-${dueDate["$M"]+1}-${dueDate["$D"]}`).getTime()/1000 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( setNewWorkflow(
innerWorkflow.name, innerWorkflow.name,
innerWorkflow.description, innerWorkflow.description,
@@ -804,6 +813,40 @@ const EditWorkflow = (props) => {
<AddIcon style={{}} /> <AddIcon style={{}} />
</Button> </Button>
{inputQuestions.length === 0 ? null :
<div>
<Typography variant="h6" style={{marginTop: 50, }}>
Input Markdown
</Typography>
<TextField
multiline
rows={3}
fullWidth
color="primary"
value={inputMarkdown}
onChange={(e) => {
setInputMarkdown(e.target.value)
workflow.input_markdown = e.target.value
setWorkflow(workflow)
setUpdate(Math.random())
}}
/>
{/*
<Typography variant="h6" style={{marginTop: 50, }}>
Output Markdown
</Typography>
<TextField
multiLine
rows={3}
fullWidth
color="primary"
/>
*/}
</div>
}
<Divider style={{marginTop: 20, marginBottom: 20, }} /> <Divider style={{marginTop: 20, marginBottom: 20, }} />
<Typography variant="body1" style={{marginTop: 50, }}> <Typography variant="body1" style={{marginTop: 50, }}>
+25 -26
View File
@@ -171,7 +171,7 @@ const Header = (props) => {
setTooltipOpen(true); setTooltipOpen(true);
}; };
const topbar_var = "topbar_closed2" const topbar_var = "topbar_closed4"
useEffect(() => { useEffect(() => {
const topbar = localStorage.getItem(topbar_var) const topbar = localStorage.getItem(topbar_var)
@@ -575,15 +575,15 @@ const Header = (props) => {
//globalUrl = responseJson.region_url //globalUrl = responseJson.region_url
} }
if (responseJson["reason"] === "SSO_REDIRECT") { if (responseJson["reason"] === "SSO_REDIRECT") {
setTimeout(() => { setTimeout(() => {
toast.info("Redirecting to SSO login page as SSO is required for this organization.") toast.info("Redirecting to SSO login page as SSO is required for this organization.")
window.location.href = responseJson["url"] window.location.href = responseJson["url"]
return return
}, 2000) }, 2000)
} else { } else {
setTimeout(() => { setTimeout(() => {
window.location.reload() window.location.reload()
}, 2000); }, 2000);
} }
toast("Successfully changed active organization - refreshing!"); toast("Successfully changed active organization - refreshing!");
} else { } else {
@@ -711,7 +711,7 @@ const Header = (props) => {
</MenuItem> </MenuItem>
</Link> </Link>
*/} */}
<Link to="/usecases" style={hrefStyle}> <Link to="/usecases2" style={hrefStyle}>
<MenuItem <MenuItem
onClick={(event) => { onClick={(event) => {
handleClose(); handleClose();
@@ -858,7 +858,7 @@ const Header = (props) => {
</List> </List>
<List className={classes.menuList} component="nav"> <List className={classes.menuList} component="nav">
<ListItem style={{ textAlign: "center", marginLeft: "0px" }}> <ListItem style={{ textAlign: "center", marginLeft: "0px" }}>
<Link to="/usecases" style={hrefStyle}> <Link to="/usecases2" style={hrefStyle}>
<Button <Button
variant="text" variant="text"
color="secondary" color="secondary"
@@ -890,7 +890,7 @@ const Header = (props) => {
</Link> </Link>
</ListItem> </ListItem>
<ListItem <ListItem
style={{ textAlign: "center", marginLeft: "0px", paddingRight: 0 }} style={{ textAlign: "center", marginLeft: "0px", paddingRight: 0 }}
// onMouseEnter={handleMenuOpen} // onMouseEnter={handleMenuOpen}
// onMouseLeave={handleMenuClose} // onMouseLeave={handleMenuClose}
> >
@@ -898,7 +898,7 @@ const Header = (props) => {
variant="text" variant="text"
color="secondary" color="secondary"
className={classes.menuButton} className={classes.menuButton}
style={{width:200}} style={{ width: 200 }}
onClick={handleMenuOpen} onClick={handleMenuOpen}
> >
Pricing & Services Pricing & Services
@@ -932,11 +932,10 @@ const Header = (props) => {
Training Courses Training Courses
</Link> </Link>
</MenuItem> </MenuItem>
<Divider /> <div className={classes.divider} />
<MenuItem className={classes.dropdownMenuItem} onClick={() => handleMenuItemClick('/partners')}>
<MenuItem className={classes.dropdownMenuItem} onClick={() => handleMenuItemClick('/partners')}>
<Link to="/partners" style={hrefStyle}> <Link to="/partners" style={hrefStyle}>
Partner Program Partner Program
</Link> </Link>
</MenuItem> </MenuItem>
</Menu> </Menu>
@@ -1031,7 +1030,7 @@ const Header = (props) => {
margin: "auto", margin: "auto",
}} }}
> >
<div style={{ flexDirection: "row", marginLeft: 0}}> <div style={{ flexDirection: "row", marginLeft: 0 }}>
<List <List
style={{ style={{
height: 56, height: 56,
@@ -1127,7 +1126,7 @@ const Header = (props) => {
</div> </div>
</Link> </Link>
</ListItem> </ListItem>
{/* {/*
<ListItem style={listItemStyle}> <ListItem style={listItemStyle}>
<Link to="/admin?admin_tab=billing" style={hrefStyle}> <Link to="/admin?admin_tab=billing" style={hrefStyle}>
<div <div
@@ -1407,7 +1406,7 @@ const Header = (props) => {
{/* {upgradeHovered ? {/* {upgradeHovered ?
"Upgrade License" "Upgrade License"
: */} : */}
Upgrade Upgrade
{/* } */} {/* } */}
</Button> </Button>
@@ -1639,12 +1638,12 @@ const Header = (props) => {
const topbarHeight = showTopbar ? 40 : 0 const topbarHeight = showTopbar ? 40 : 0
const topbar = !isCloud || !showTopbar ? null : 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" ?
<span style={{ zIndex: 50001, }}> <span style={{ zIndex: 50001, }}>
<div style={{ position: "relative", height: topbarHeight, backgroundImage: "linear-gradient(to right, #f86a3e, #f34079)", overflow: "hidden", }}> <div style={{ position: "relative", height: topbarHeight, backgroundImage: "linear-gradient(to right, #f86a3e, #f34079)", overflow: "hidden", }}>
<Typography variant="body1" style={{ paddingTop: 7, margin: "auto", textAlign: "center", color: "white", }}> <Typography style={{ paddingTop: 7, fontSize:16, margin: "auto", textAlign: "center", color: "white", }}>
{/* Shuffle 1.4.0 is out! Read more about&nbsp; */} {/* Shuffle 1.4.0 is out! Read more about&nbsp; */}
New Public&nbsp; New&nbsp;
<u> <u>
<span onClick={() => { <span onClick={() => {
ReactGA.event({ ReactGA.event({
@@ -1655,11 +1654,11 @@ const Header = (props) => {
navigate("/training") navigate("/training")
}} style={{ cursor: "pointer", textDecoration: "none", fontWeight: 600, color: "rgba(255,255,255,0.8)" }}> }} style={{ cursor: "pointer", textDecoration: "none", fontWeight: 600, color: "rgba(255,255,255,0.9)" }}>
Training Dates Released Public Training
</span> </span>
</u> </u>
&nbsp;Ahead! &nbsp;Dates Released!
</Typography> </Typography>
<IconButton color="secondary" style={{ position: "absolute", top: -3, right: 20, }} onClick={(event) => { <IconButton color="secondary" style={{ position: "absolute", top: -3, right: 20, }} onClick={(event) => {
setShowTopbar(false) setShowTopbar(false)
+21 -2
View File
@@ -299,12 +299,11 @@ const AuthenticationOauth2 = (props) => {
const handleOauth2Request = (client_id, client_secret, oauth_url, scopes, admin_consent, prompt, skipScopeReplace) => { const handleOauth2Request = (client_id, client_secret, oauth_url, scopes, admin_consent, prompt, skipScopeReplace) => {
console.log("SKIP SCOPE: ", skipScopeReplace)
if (skipScopeReplace === false || skipScopeReplace === undefined) { if (skipScopeReplace === false || skipScopeReplace === undefined) {
console.log("Selected scopes: ", selectedScopes) console.log("Selected scopes: ", selectedScopes)
if (selectedScopes !== undefined && selectedScopes !== null && selectedScopes.length > 0) { 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 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`; 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) console.log("OAUTH2 URL: ", url)
return
*/
// Force new consent // 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`; //const url = `${authenticationType.redirect_uri}?client_id=${client_id}&redirect_uri=${redirectUri}&response_type=code&scope=${resources}&prompt=consent&state=${state}&access_type=offline`;
+161 -79
View File
@@ -600,8 +600,9 @@ const ParsedAction = (props) => {
let errorVars = []; let errorVars = [];
if(paramvalue.includes("$")){ if(paramvalue.includes("$")){
let actions = workflow.actions?.map((action) => { let actions = workflow.actions?.map((action) => {
return "$"+action.label.toLowerCase(); return "$"+action.label?.toLowerCase();
}) })
if(newActionList?.length > 0){ if(newActionList?.length > 0){
let appParentActions = parentActionList?.map(action => "$" + action.name.toLowerCase()); let appParentActions = parentActionList?.map(action => "$" + action.name.toLowerCase());
let notPresentAction = actions?.filter((action) => !appParentActions?.includes(action)) let notPresentAction = actions?.filter((action) => !appParentActions?.includes(action))
@@ -629,9 +630,9 @@ const ParsedAction = (props) => {
let regex = /(^|[^\\])\$/; let regex = /(^|[^\\])\$/;
if (regex.test(paramvalue)) { if (regex.test(paramvalue)) {
if(message.length > 0){ if(message.length > 0){
message += "\nUse \"\\$\" instead of \"$\"."; message += "\nUse \"\\$\" instead of \"$\".";
}else{ }else{
message = "Use \"\\$\" instead of \"$\"."; message = "Use \"\\$\" instead of \"$\" to escape variables.";
} }
} }
} }
@@ -640,7 +641,7 @@ const ParsedAction = (props) => {
setSelectedActionParameters(newParameters); setSelectedActionParameters(newParameters);
setActionlist(newActionList); setActionlist(newActionList);
}, [workflow.execution_variables,paramUpdate, workflow.workflow_variables, workflowExecutions, workflow, selectedAction, listCache, getParents,setNewSelectedAction]); }, [workflow.execution_variables,paramUpdate, workflow.workflow_variables, workflowExecutions, workflow, selectedAction, listCache, getParents,setNewSelectedAction]);
console.log("Selected Action:", selectedAction)
useEffect(() => { useEffect(() => {
selectedNameChange(appActionName) selectedNameChange(appActionName)
@@ -1280,12 +1281,14 @@ const ParsedAction = (props) => {
} }
} }
var authWritten = false; var authWritten = false;
var noAppSelected = false var noAppSelected = false
var paramIndex = selectedAction.parameters.findIndex((param) => param.name === "app_name") if (selectedAction.parameters !== undefined && selectedAction.parameters !== null && selectedAction.parameters.length > 0) {
if (paramIndex === -1 || selectedAction.parameters[paramIndex].value === "" || selectedAction.parameters[paramIndex].value === "noapp") { var paramIndex = selectedAction.parameters.findIndex((param) => param.name === "app_name")
// Check the actual value and if it's the same if (paramIndex === -1 || selectedAction.parameters[paramIndex].value === "" || selectedAction.parameters[paramIndex].value === "noapp") {
noAppSelected = true // 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)
}
}} }}
> >
<div style={{ display: "flex", marginBottom: 0,}}> <div style={{ display: "flex", marginBottom: 0,}}>
@@ -1874,11 +1883,9 @@ const ParsedAction = (props) => {
} }
} }
console.log("Old found: ", workflow.actions[key].parameters[subkey].value)
const extralength = newname.length-parsedBaseLabel.length const extralength = newname.length-parsedBaseLabel.length
param.value = param.value.substring(0, foundindex) + newname + param.value.substring(foundindex-extralength+newname.length, param.value.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 { } else {
break break
} }
@@ -1990,18 +1997,25 @@ const ParsedAction = (props) => {
selectedAction.authentication_id = ""; selectedAction.authentication_id = "";
for (let [key,keyval] in Object.entries(selectedAction.parameters)) { 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].name === "url" && authenticationType?.type === "oauth2-app" && selectedAction.parameters[key].value.includes("http")) {
if (selectedAction.parameters[key].example.toLowerCase().includes("api") || selectedAction.parameters[key].example.toLowerCase().includes("key") || selectedAction.parameters[key].example.toLowerCase().includes("pass")) { continue
selectedAction.parameters[key].value = "" }
} else {
selectedAction.parameters[key].value = selectedAction.parameters[key].example 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("****")) {
} else { selectedAction.parameters[key].value = ""
selectedAction.parameters[key].value = "" } else {
} selectedAction.parameters[key].value = selectedAction.parameters[key].example
} }
} else {
selectedAction.parameters[key].value = ""
}
} }
setSelectedAction(selectedAction); setSelectedAction(selectedAction);
setUpdate(Math.random()); setUpdate(Math.random());
@@ -2020,7 +2034,11 @@ const ParsedAction = (props) => {
for (let [key,keyval] in Object.entries(selectedAction.parameters)) { for (let [key,keyval] in Object.entries(selectedAction.parameters)) {
//console.log(selectedAction.parameters[key]) //console.log(selectedAction.parameters[key])
if (selectedAction.parameters[key].configuration) { 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) => {
</div> </div>
) : null} ) : null}
{selectedAction.authentication_id === "authgroups" && (authGroups === undefined || authGroups === null || authGroups.length === 0) ?
<a href="/admin?tab=app_auth" target="_blank" style={{textDecoration: "none", color: "#f85a3e",}}>
<Typography variant="body2" style={{marginTop: 5,}}>
Create your first Authentication group
</Typography>
</a>
: null}
{showEnvironment !== undefined && showEnvironment && environments.length > 1 && !isIntegration ? ( {showEnvironment !== undefined && showEnvironment && environments.length > 1 && !isIntegration ? (
<div style={{ marginTop: "20px" }}> <div style={{ marginTop: "20px" }}>
@@ -2611,7 +2637,7 @@ const ParsedAction = (props) => {
marginBottom: hideExtraTypes ? 50 : 200, marginBottom: hideExtraTypes ? 50 : 200,
}} }}
> { > {
Object.getOwnPropertyNames(selectedAction).length > 0 && selectedActionParameters.length > 0 ? selectedActionParameters !== undefined && selectedActionParameters !== null && Object.getOwnPropertyNames(selectedAction).length > 0 && selectedActionParameters.length > 0 ?
<div style={{ marginTop: hideExtraTypes ? 10 : 30 }}> <div style={{ marginTop: hideExtraTypes ? 10 : 30 }}>
{isIntegration ? {isIntegration ?
apps !== undefined && apps !== null && apps.length > 0 ? apps !== undefined && apps !== null && apps.length > 0 ?
@@ -2907,20 +2933,59 @@ const ParsedAction = (props) => {
} }
if (data.value === "authgroup controlled") { 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 ( if (
(selectedAction.auth_not_required !== undefined && !selectedAction.auth_not_required) && (selectedAction.auth_not_required !== undefined && !selectedAction.auth_not_required) &&
selectedActionParameters[count] !== undefined &&
selectedActionParameters[count] !== null &&
selectedActionParameters[count].value !== undefined && selectedActionParameters[count].value !== undefined &&
selectedAction.parameters[count] !== undefined &&
selectedAction.parameters[count] !== null &&
selectedAction.parameters[count].value !== undefined && selectedAction.parameters[count].value !== undefined &&
selectedAction.selectedAuthentication !== undefined && selectedAction.selectedAuthentication !== undefined &&
selectedAction.selectedAuthentication.fields !== undefined && selectedAction.selectedAuthentication.fields !== undefined &&
selectedAction.selectedAuthentication.fields[data.name] !== selectedAction.selectedAuthentication.fields[data.name] !==
undefined 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 (
<Typography
key={count}
id="skip_auth"
variant="body2"
color="textSecondary"
style={{ marginTop: 5 }}
>
Authentication fields are hidden
</Typography>
)
}
*/
//!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) // This sets the placeholder in the frontend. (Replaced in backend)
selectedActionParameters[count].value = selectedActionParameters[count].value =
selectedAction.selectedAuthentication.fields[data.name]; selectedAction.selectedAuthentication.fields[data.name];
@@ -2947,6 +3012,7 @@ const ParsedAction = (props) => {
); );
} }
// Added autofill to make this ALOT simpler // 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 (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) { if (selectedActionParameters[count].length === 0) {
@@ -3063,6 +3129,7 @@ const ParsedAction = (props) => {
var rows = "3"; var rows = "3";
var openApiHelperText = "This is an OpenAPI specific field"; var openApiHelperText = "This is an OpenAPI specific field";
if (selectedApp.generated && data.name === "headers") { if (selectedApp.generated && data.name === "headers") {
//console.log("HEADER: ", data) //console.log("HEADER: ", data)
//if (data.value.length === 0) { //if (data.value.length === 0) {
@@ -3278,7 +3345,7 @@ const ParsedAction = (props) => {
const description = data.description === undefined ? "" : data?.description; const description = data.description === undefined ? "" : data?.description;
const tooltipDescription = ( const tooltipDescription = (
<Box <Box
p={1.5} p={1.5}
borderRadius={3} borderRadius={3}
boxShadow={2} boxShadow={2}
@@ -3291,19 +3358,17 @@ const ParsedAction = (props) => {
{tmpitem.charAt(0).toUpperCase() + tmpitem.slice(1)} {tmpitem.charAt(0).toUpperCase() + tmpitem.slice(1)}
</Typography> </Typography>
<IconButton size="small" <IconButton size="small"
onClick={() => {
onMouseDown={(event) => { setUiBox("closed")
event.preventDefault();
event.stopPropagation(); /*
}} const inputElement = document.getElementById(uiBox);
if (inputElement) {
onClick={() => { inputElement.focus();
setUiBox("closed") }
const inputElement = document.getElementById(uiBox); */
if (inputElement) { }}
inputElement.focus(); >
}
}}>
<CloseIcon fontSize="small" /> <CloseIcon fontSize="small" />
</IconButton> </IconButton>
</Box> </Box>
@@ -3316,7 +3381,7 @@ const ParsedAction = (props) => {
<strong>Description:</strong> {description} <strong>Description:</strong> {description}
</Typography> </Typography>
<Typography variant="body2"> <Typography variant="body2">
<strong>Ex. :</strong> {data?.example.length > 0 ? data.example : "No example available"} <strong>Ex. :</strong> {data?.example?.length > 0 ? data.example : "No example available"}
</Typography> </Typography>
{ {
data?.configuration === true ? data?.configuration === true ?
@@ -3326,15 +3391,31 @@ const ParsedAction = (props) => {
</Typography> </Typography>
) : null ) : null
} }
<div onClick={(e) => {
e.preventDefault()
e.stopPropagation()
localStorage.setItem("disabled_ui_box", "true")
setUiBox("closed")
}}>
<Typography style={{marginTop: 10, color: "#f85a3e", cursor: "pointer",}} variant="body2">
Don't show again
</Typography>
</div>
</Box> </Box>
</Box> </Box>
); );
var datafield = ( var datafield = (
<Tooltip <Tooltip
title={tooltipDescription} title={tooltipDescription}
placement="right" placement="right"
open={clickedFieldId === uiBox} open={clickedFieldId === uiBox}
style={{
zIndex: 0,
}}
PopperProps={{ PopperProps={{
sx: { sx: {
'& .MuiTooltip-tooltip': { '& .MuiTooltip-tooltip': {
@@ -3348,6 +3429,7 @@ const ParsedAction = (props) => {
}} }}
> >
<TextField <TextField
id={clickedFieldId}
disabled={disabled} disabled={disabled}
style={{ style={{
backgroundColor: theme.palette.inputColor, backgroundColor: theme.palette.inputColor,
@@ -3424,7 +3506,6 @@ const ParsedAction = (props) => {
setScrollConfig(scrollConfig) setScrollConfig(scrollConfig)
} }
}} }}
id={clickedFieldId}
rows={data.name.startsWith("${") && data.name.endsWith("}") ? 2 : rows} rows={data.name.startsWith("${") && data.name.endsWith("}") ? 2 : rows}
color="primary" color="primary"
// defaultValue={data.value} // defaultValue={data.value}
@@ -3458,15 +3539,28 @@ const ParsedAction = (props) => {
handleParamChange(event, count, data) handleParamChange(event, count, data)
}} }}
onFocus={(event) => { 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) => { onBlur={(event) => {
baseHelperText = calculateHelpertext(event.target.value) baseHelperText = calculateHelpertext(event.target.value)
if (setLastSaved !== undefined) { if (setLastSaved !== undefined) {
setLastSaved(false) 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")
}
}} }}
/> />
</Tooltip> </Tooltip>
@@ -3664,7 +3758,6 @@ const ParsedAction = (props) => {
</div> </div>
} }
//console.log("FIELD VALUE: ", data.value)
//const regexp = new RegExp("\W+\.", "g") //const regexp = new RegExp("\W+\.", "g")
//let match //let match
//while ((match = regexp.exec(data.value)) !== null) { //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") { if (files !== undefined && files !== null && data.name.toLowerCase() === "file_category") {
//selectedActionParameters[count].options.length > 0 //selectedActionParameters[count].options.length > 0
console.log("FileS: ", files) console.log("FileS: ", files)
if (files.namespaces !== undefined && files.namespaces !== null && files.namespaces.length > 0) { if (files.namespaces !== undefined && files.namespaces !== null && files.namespaces.length > 0) {
data.options = files.namespaces data.options = files.namespaces
} }
} }
//const keywords = ["len", "lower", "upper", "trim", "split", "length", "number", "parse", "join"] //const keywords = ["len", "lower", "upper", "trim", "split", "length", "number", "parse", "join"]
if ( if (
@@ -3736,26 +3827,11 @@ const ParsedAction = (props) => {
}} }}
onBlur={(event) => {}} 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 ( } else if (
(data.options !== undefined && (data.options !== undefined && data.options !== null && data.options.length > 0) ||
data.options !== null && (selectedActionParameters[count].options !== undefined && selectedActionParameters[count].options !== null && selectedActionParameters[count].options.length > 0)) {
data.options.length > 0) const parsedoptions = data.options !== undefined && data.options !== null && data.options.length > 0 ? data.options : selectedActionParameters[count].options
||
(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 === "") { if (selectedActionParameters[count].value === "") {
// && selectedActionParameters[count].required) { // && selectedActionParameters[count].required) {
@@ -3833,7 +3909,7 @@ const ParsedAction = (props) => {
} }
if (data.field_active === false) { if (data.field_active === false) {
return null; return null;
} }
@@ -4212,12 +4288,18 @@ const ParsedAction = (props) => {
const buttonTitle = `Authenticate ${selectedApp.name.replaceAll("_", " ")}` const buttonTitle = `Authenticate ${selectedApp.name.replaceAll("_", " ")}`
const hasAutocomplete = data?.autocompleted === true const hasAutocomplete = data?.autocompleted === true
if (data.variant === undefined || data.variant === null) { if (data.variant === undefined || data.variant === null) {
data.variant = "STATIC_VALUE" data.variant = "STATIC_VALUE"
} }
/*
if (data?.configuration === true) {
if (data?.name === "url" && authenticationType?.type === "oauth2-app") {
data.configuration = false
}
}
*/
return ( return (
<div key={data.name}> <div key={data.name}>
{/* {hideBodyButton} */} {/* {hideBodyButton} */}
+2 -14
View File
@@ -64,6 +64,7 @@ const WelcomeForm = (props) => {
discoveryWrapper, discoveryWrapper,
setDiscoveryWrapper, setDiscoveryWrapper,
appFramework, appFramework,
setAppFramework,
getFramework, getFramework,
activeStep, activeStep,
setActiveStep, setActiveStep,
@@ -435,20 +436,6 @@ const WelcomeForm = (props) => {
setSkipped(newSkipped); 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 = () => { const handleReset = () => {
setActiveStep(0); setActiveStep(0);
@@ -645,6 +632,7 @@ const WelcomeForm = (props) => {
globalUrl={globalUrl} globalUrl={globalUrl}
userdata={userdata} userdata={userdata}
appFramework={appFramework} appFramework={appFramework}
setAppFramework={setAppFramework}
setActiveStep={setActiveStep} setActiveStep={setActiveStep}
defaultSearch={defaultSearch} defaultSearch={defaultSearch}
setDefaultSearch={setDefaultSearch} setDefaultSearch={setDefaultSearch}
@@ -4,21 +4,28 @@ import { toast } from "react-toastify"
import { import {
Tooltip, Tooltip,
Chip,
Typography, Typography,
IconButton,
Avatar, Avatar,
AvatarGroup, AvatarGroup,
} from "@mui/material" } from "@mui/material"
import {
ErrorOutline as ErrorOutlineIcon,
} from "@mui/icons-material"
import { import {
green, green,
yellow, yellow,
red, red,
grey,
} from "../views/AngularWorkflow.jsx" } from "../views/AngularWorkflow.jsx"
import { validateJson, GetIconInfo } from "../views/Workflows.jsx"; import { validateJson, GetIconInfo } from "../views/Workflows.jsx";
import theme from "../theme.jsx"; import theme from "../theme.jsx";
const itemHeight = 40 const itemHeight = 24
export const getParentNodes = (workflow, action) => { export const getParentNodes = (workflow, action) => {
if (action === undefined || action === null) { if (action === undefined || action === null) {
@@ -123,6 +130,8 @@ export const getParentNodes = (workflow, action) => {
const WorkflowValidationTimeline = (props) => { const WorkflowValidationTimeline = (props) => {
const { workflow, originalWorkflow, apps, getParents, execution} = props const { workflow, originalWorkflow, apps, getParents, execution} = props
const showMiddle = false
if (workflow === undefined || workflow === null) { if (workflow === undefined || workflow === null) {
return null return null
@@ -196,6 +205,15 @@ const WorkflowValidationTimeline = (props) => {
parents = getParentNodes(workflow, action) 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) //const parents = getParentNodes(workflow, action)
//console.log("PARENTS", key, parents) //console.log("PARENTS", key, parents)
if (parents !== undefined && parents !== null && parents.length > 0) { if (parents !== undefined && parents !== null && parents.length > 0) {
@@ -219,6 +237,20 @@ const WorkflowValidationTimeline = (props) => {
continue 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) { if (workflow.actions.find((element) => element.id === trigger.id) === undefined) {
newactions.push(trigger) newactions.push(trigger)
//workflow.actions.push(trigger) //workflow.actions.push(trigger)
@@ -242,14 +274,19 @@ const WorkflowValidationTimeline = (props) => {
}) })
// FIXME: Add other relevant items as well from subflows (?) // FIXME: Add other relevant items as well from subflows (?)
var nodecolor = "grey" var nodecolor = grey
var branchcolor = "grey" var branchcolor = grey
var skipped = false var skipped = false
var previousTools = 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 ( return (
<div style={{border: workflow.validation.valid ? `2px solid ${green}` : "1px solid rgba(255,255,255,0.4)", padding: "10px 20px 10px 20px", borderRadius: theme.palette.borderRadius, }}> <div style={{ padding: "10px 5px 10px 5px", borderRadius: theme.palette.borderRadius, }}>
<div style={{display: "flex", justifyContent: "center", alignItems: "center"}}> <div style={{display: "flex", justifyContent: "center", alignItems: "center"}}>
{relevantactions.map((action, index) => { {relevantactions.map((action, index) => {
action.result = {} action.result = {}
@@ -274,13 +311,13 @@ const WorkflowValidationTimeline = (props) => {
if (validate.result.success === true) { if (validate.result.success === true) {
branchcolor = green branchcolor = green
} else { } else {
branchcolor = "grey" branchcolor = grey
} }
} }
} else if (action.status === "SKIPPED") { } else if (action.status === "SKIPPED") {
branchcolor = "grey" branchcolor = grey
} else { } else {
if (action.status === undefined) { if (action.status === undefined) {
branchcolor = green branchcolor = green
@@ -292,13 +329,13 @@ const WorkflowValidationTimeline = (props) => {
previousTools = true previousTools = true
if (startnodeId !== action.id) { if (startnodeId !== action.id) {
return null //return null
} }
} else { } else {
if (action.status === "SUCCESS") { if (action.status === "SUCCESS") {
nodecolor = green nodecolor = green
} else if (action.status === "SKIPPED") { } else if (action.status === "SKIPPED") {
nodecolor = "grey" nodecolor = grey
} else { } else {
if (action.status === undefined) { if (action.status === undefined) {
nodecolor = green nodecolor = green
@@ -308,7 +345,7 @@ const WorkflowValidationTimeline = (props) => {
} }
} }
} else { } else {
nodecolor = "grey" nodecolor = grey
} }
if (action.status === "SKIPPED") { if (action.status === "SKIPPED") {
@@ -340,18 +377,44 @@ const WorkflowValidationTimeline = (props) => {
} }
var founderror = "" var founderror = ""
//console.log("WORKFLOW VALIDATION: ", workflow.validation)
if (workflow.validation !== undefined && workflow.validation !== null && workflow.validation.errors !== undefined && workflow.validation.errors !== null) { 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) const foundError = workflow.validation.errors.find((element) => element.action_id === action.id)
if (foundError !== undefined) { if (foundError !== undefined) {
founderror = foundError.error founderror = foundError.error
nodecolor = yellow nodecolor = red
branchcolor = yellow 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 ?
<Tooltip title={middleError}>
<IconButton style={{width: 30, height: 30, backgroundColor: "rgba(255,255,255,0.0)", borderRadius: 30, marginTop: 2, }}>
<ErrorOutlineIcon style={{color: "red", }} />
</IconButton>
</Tooltip>
: null
return (
selectedIcon
)
} else {
return null
} }
} }
if (skipped && !lastitem) { if (skipped && !lastitem) {
nodecolor = "grey" nodecolor = grey
branchcolor = "grey" branchcolor = grey
} }
if (previousTools) { if (previousTools) {
@@ -379,14 +442,60 @@ const WorkflowValidationTimeline = (props) => {
} }
var flex = index !== 0 && index !== relevantactions.length - 1 ? 1 : 3 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 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 ( return (
<div style={{display: "flex", flex: flex, justifyContent: "right", }}> <div style={{display: "flex", flex: flex, justifyContent: "right", }}>
{lastitem ? {lastitem ?
<Tooltip title={branchTooltip}> <Tooltip title={branchTooltip}>
<div style={{marginLeft: 0, marginRight: 0, marginTop: 20, height: 3, width: "100%", backgroundColor: branchcolor, }} /> <div style={{display: "flex", width: "100%", position: "relative",}}>
<div style={{marginLeft: 0, marginRight: 0, marginTop: topMargin, height: 3, width: "100%", backgroundColor: branchcolor, }} />
<div style={{position: "absolute", right: -3, top: topMargin-2.5, backgroundColor: ballcolor, width: ballsize, height: ballsize, borderRadius: 10, }}/>
</div>
</Tooltip> </Tooltip>
: null} : null}
@@ -419,30 +528,36 @@ const WorkflowValidationTimeline = (props) => {
: :
<Tooltip title={ <Tooltip title={
<Typography variant="body1" style={{margin: 5, color: "white", }}> <Typography variant="body1" style={{margin: 5, color: "white", }}>
{founderror.length > 0 ? founderror : `${action.app_name.replaceAll('_', ' ')}`} {founderror.length > 0 ? founderror : `App: ${appname}`}
</Typography> </Typography>
} placement="top"> } placement="top">
<Avatar <Chip label={`${appname}`} style={chipStyle} icon={
variant="round" <Avatar
sx={{ backgroundColor: nodecolor, width: itemHeight, height: itemHeight, borderRadius: 50, border: `4px solid ${nodecolor}`,}} variant="round"
> sx={{ backgroundColor: nodecolor, width: itemHeight, height: itemHeight, borderRadius: 50, border: `1px solid ${nodecolor}`,}}
{image !== "" ? >
<img {image !== "" ?
src={image} <img
style={{ src={image}
width: itemHeight, style={{
height: itemHeight, width: itemHeight,
}} height: itemHeight,
/> }}
: null} />
</Avatar> : null}
</Avatar>
} />
</Tooltip> </Tooltip>
} }
{lastitem ? null : {lastitem ? null :
<Tooltip title={branchTooltip}> <Tooltip title={branchTooltip}>
<div style={{marginLeft: 0, marginRight: 0, marginTop: 20, height: 3, width: "100%", backgroundColor: branchcolor, }} /> <div style={{display: "flex", width: "100%", position: "relative",}}>
<div style={{position: "absolute", left: -3, top: topMargin-2.5, backgroundColor: ballcolor, color: ballcolor, width: ballsize, height: ballsize, borderRadius: 10, }}/>
<div style={{marginLeft: 0, marginRight: 0, marginTop: 20, height: 3, width: "100%", backgroundColor: branchcolor, }} />
</div>
</Tooltip> </Tooltip>
} }
</div> </div>
+49 -10
View File
@@ -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", 1: "cloud_sync",
2: "priorities", 2: "priorities",
3: "billing", 3: "billing",
4: "branding", 4: "partner",
}; };
const setConfig = (event, inputValue) => { 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
> >
<Tab label=<span>Edit Details</span> /> <Tab label=<span>Edit Details</span> />
<Tab label=<span>Limits & Cloud Sync</span> /> <Tab label=<span>Limits & Cloud Sync</span> />
<Tab label=<span>Priorities</span> /> <Tab label=<span>Notifications</span> />
<Tab label=<span>Billing & Stats</span> /> <Tab label=<span>Billing & Stats</span> />
<Tab disabled={!isCloud} label=<span>Partner</span> /> <Tab disabled={!isCloud} label=<span>Partner</span> />
</Tabs> </Tabs>
@@ -4383,9 +4383,11 @@ If you're interested, please let me know a time that works for you, or set up a
<Select <Select
labelId="user-ip-simple-select-label" labelId="user-ip-simple-select-label"
id="user-ip-simple-select" id="user-ip-simple-select"
onChange={async (event) => { onChange={(event) => {
setIpSelected(event.target.value); 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
})()} })()}
</Select> </Select>
</FormControl> </FormControl>
{logsLoading && ipSelected.length !== 0 ? ( {logsLoading && ipSelected.length !== 0 ? (
<div <div
style={{ style={{
@@ -4425,7 +4428,38 @@ If you're interested, please let me know a time that works for you, or set up a
) : null} ) : null}
<List> <List>
{logs.map((data, index) => ( <ListItem>
<ListItemText
primary={
"Timestamp"
}
style={{
minWidth: 200,
maxWidth: 200,
}}
/>
<ListItemText
primary={"Referer"}
style={{
minWidth: 300,
maxWidth: 300,
overflow: "hidden",
}}
/>
<ListItemText
primary={"URL"}
style={{
minWidth: 700,
maxWidth: 700,
overflow: "hidden",
marginLeft: 10,
}}
/>
</ListItem>
{logs.map((data, index) => {
//console.log("LOG: ", data)
return (
// redirect user to logs // redirect user to logs
// using request id or trace id // using request id or trace id
<ListItem <ListItem
@@ -4436,7 +4470,7 @@ If you're interested, please let me know a time that works for you, or set up a
> >
<ListItemText <ListItemText
primary={new Date( primary={new Date(
data.start_time.seconds * 1000, data.timestamp * 1000,
).toLocaleString("en-US", { ).toLocaleString("en-US", {
year: "numeric", year: "numeric",
month: "2-digit", month: "2-digit",
@@ -4452,7 +4486,7 @@ If you're interested, please let me know a time that works for you, or set up a
}} }}
/> />
<ListItemText <ListItemText
primary={data.referrer} primary={data.referer}
style={{ style={{
minWidth: 300, minWidth: 300,
maxWidth: 300, maxWidth: 300,
@@ -4460,7 +4494,7 @@ If you're interested, please let me know a time that works for you, or set up a
}} }}
/> />
<ListItemText <ListItemText
primary={data.resource} primary={data.url}
style={{ style={{
minWidth: 700, minWidth: 700,
maxWidth: 700, maxWidth: 700,
@@ -4469,7 +4503,7 @@ If you're interested, please let me know a time that works for you, or set up a
}} }}
/> />
</ListItem> </ListItem>
))} )})}
</List> </List>
</DialogContent> </DialogContent>
</Dialog> </Dialog>
@@ -4567,6 +4601,11 @@ If you're interested, please let me know a time that works for you, or set up a
onClick={() => { onClick={() => {
setLogsViewModal(true); setLogsViewModal(true);
setUserLogViewing(data); 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} {data.username}
@@ -6077,7 +6116,7 @@ If you're interested, please let me know a time that works for you, or set up a
</div> </div>
) : null; ) : null;
const getLogs = async (ip, userId) => { const getLogs = (ip, userId) => {
setLogsLoading(true); setLogsLoading(true);
console.log("logs loading: ", logsLoading); console.log("logs loading: ", logsLoading);
fetch(`${globalUrl}/api/v1/users/${userId}/audit?user_ip=${ip}`, { fetch(`${globalUrl}/api/v1/users/${userId}/audit?user_ip=${ip}`, {
+117 -50
View File
@@ -319,9 +319,12 @@ export function SetJsonDotnotation(jsonInput, inputKey) {
return jsonInput; return jsonInput;
} }
export const green = "#86c142"; //export const green = "#86c142";
export const green = "#02CB70"
export const yellow = "#FECC00"; 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) { export function removeParam(key, sourceURL) {
if (sourceURL === undefined) { if (sourceURL === undefined) {
@@ -673,10 +676,16 @@ const releaseToConnectLabel = "Release to Connect"
const loadAppConfig = (appId, select) => { const loadAppConfig = (appId, select) => {
if (appId === undefined || appId === null || appId.length === 0) { if (appId === undefined || appId === null || appId.length === 0) {
console.log("No appId to load")
return
}
if (appId === "integration") {
return return
} }
if (loadedApps.includes(appId)) { if (loadedApps.includes(appId)) {
console.log("App already loaded: ", appId)
return return
} }
@@ -699,8 +708,6 @@ const releaseToConnectLabel = "Release to Connect"
if (responseJson.success === true && responseJson.app !== undefined && responseJson.app !== null && responseJson.app.length > 0) { if (responseJson.success === true && responseJson.app !== undefined && responseJson.app !== null && responseJson.app.length > 0) {
// Base64 decode into json // Base64 decode into json
const foundapp = JSON.parse(atob(responseJson.app)) const foundapp = JSON.parse(atob(responseJson.app))
console.log("Checked app: ", foundapp)
const selectedAppActions = selectedApp.actions === undefined || selectedApp.actions === null ? [] : selectedApp.actions const selectedAppActions = selectedApp.actions === undefined || selectedApp.actions === null ? [] : selectedApp.actions
if (foundapp.actions !== undefined && foundapp.actions !== null && foundapp.actions.length > selectedAppActions.length) { 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) { if (selectedApp.actions === undefined || selectedApp.actions === null || selectedApp.actions.length > 1) {
return return
} else { } else {
if (selectedApp.id !== undefined && selectedApp.id !== null && selectedApp.id.length > 0) { if (selectedApp.id !== undefined && selectedApp.id !== null && selectedApp.id.length > 0) {
loadAppConfig(selectedApp.id, true) loadAppConfig(selectedApp.id, true)
} }
@@ -1241,8 +1249,14 @@ const releaseToConnectLabel = "Release to Connect"
}) })
.then((response) => { .then((response) => {
if (response.status !== 200) { 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(); return response.json();
}) })
@@ -1253,8 +1267,6 @@ const releaseToConnectLabel = "Release to Connect"
"autoClose": false, "autoClose": false,
}) })
} else { } else {
if (refresh === true) { if (refresh === true) {
getAppAuthentication(true, true, true) getAppAuthentication(true, true, true)
@@ -2153,6 +2165,7 @@ const releaseToConnectLabel = "Release to Connect"
}) })
.catch((error) => { .catch((error) => {
setSavingState(0); setSavingState(0);
setExecutionRequestStarted(false)
console.log("Save workflow error: ", error.toString()); console.log("Save workflow error: ", error.toString());
toast.warn("Failed to save the workflow. Is the network down?") toast.warn("Failed to save the workflow. Is the network down?")
}); });
@@ -2346,6 +2359,7 @@ const releaseToConnectLabel = "Release to Connect"
//toast(error.toString()); //toast(error.toString());
setExecutionRequestStarted(false) setExecutionRequestStarted(false)
console.log("Execute workflow err: ", error.toString()); 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) { if (!curapp || curapp === undefined) {
const tmpapp = { const tmpapp = {
name: curaction.app_name, name: curaction.app_name,
@@ -5753,8 +5771,10 @@ const releaseToConnectLabel = "Release to Connect"
setLastSaved(false); setLastSaved(false);
const edge = event.target.data(); const edge = event.target.data();
//console.log("edge added: ", edge) console.log("edge added: ", edge)
if (edge.source === undefined && edge.target === undefined) { if (edge.source === undefined && edge.target === undefined) {
console.log("Edge added without source or target")
return return
} }
@@ -5768,8 +5788,9 @@ const releaseToConnectLabel = "Release to Connect"
const sourcenode = cy.getElementById(edge.source) const sourcenode = cy.getElementById(edge.source)
const destinationnode = cy.getElementById(edge.target) const destinationnode = cy.getElementById(edge.target)
if (sourcenode === undefined || sourcenode === null || destinationnode === undefined || destinationnode === null) { if (sourcenode === undefined || sourcenode === null || destinationnode === undefined || destinationnode === null) {
console.log("Source or destination node is undefined or null: ", sourcenode, destinationnode)
} else { } 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("type") === "TRIGGER") {
if (sourcenode.data("app_name") !== "Shuffle Workflow" && sourcenode.data("app_name") !== "User Input") { if (sourcenode.data("app_name") !== "Shuffle Workflow" && sourcenode.data("app_name") !== "User Input") {
setTimeout(() => { setTimeout(() => {
@@ -5778,7 +5799,6 @@ const releaseToConnectLabel = "Release to Connect"
(data) => data.data.source === edge.source && data.data.id !== edge.id (data) => data.data.source === edge.source && data.data.id !== edge.id
) )
console.log("Node: ", targetedge)
if (targetedge !== -1) { if (targetedge !== -1) {
event.target.remove() event.target.remove()
@@ -5810,14 +5830,15 @@ const releaseToConnectLabel = "Release to Connect"
); );
if (targetnode !== -1) { 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") { 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 { } else {
toast("Can't have triggers as target of branch"); toast("Can't have triggers as target of branch")
event.target.remove(); event.target.remove()
} }
} }
const eventTarget = event.target.target() 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) { if (eventTarget.data("isButton") === true) {
const parentNode = cy.getElementById(eventTarget.data("attachedTo")) const parentNode = cy.getElementById(eventTarget.data("attachedTo"))
event.target.remove() event.target.remove()
@@ -5843,13 +5864,10 @@ const releaseToConnectLabel = "Release to Connect"
} }
} }
if ( if (eventTarget.data("isDescriptor") === true || eventTarget.data("type") === "COMMENT") {
eventTarget.data("isDescriptor") === true ||
eventTarget.data("type") === "COMMENT"
) {
console.log("Removing because of descriptor or comment") console.log("Removing because of descriptor or comment")
event.target.remove(); event.target.remove()
return; return
} }
targetnode = -1; targetnode = -1;
@@ -5858,26 +5876,33 @@ const releaseToConnectLabel = "Release to Connect"
// dest == source && source == dest // dest == source && source == dest
// dest == dest && source == source // dest == dest && source == source
// backend: check all children? to stop recursion // backend: check all children? to stop recursion
//
var found = false; var found = false;
for (let branchkey in workflow.branches) { for (let branchkey in workflow.branches) {
if ( if (workflow.branches[branchkey].destination_id === edge.source && workflow.branches[branchkey].source_id === edge.target) {
workflow.branches[branchkey].destination_id === edge.source && toast("A branch in the opposite direction already exists")
workflow.branches[branchkey].source_id === edge.target event.target.remove()
) { found = true
toast("A branch in the opposite direction already exists"); break
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();
found = true; if (workflow.branches[branchkey].destination_id === edge.target && workflow.branches[branchkey].source_id === edge.source) {
break;
} else if (edge.target === workflow.start) { 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( targetnode = workflow.triggers.findIndex(
(data) => data.id === edge.source (data) => data.id === edge.source
); );
@@ -5890,7 +5915,9 @@ const releaseToConnectLabel = "Release to Connect"
found = true; 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 // FIXME: Verify multi-target for triggers
// 1. Check if destination exists // 1. Check if destination exists
// 2. Check if source is a trigger // 2. Check if source is a trigger
@@ -7522,7 +7549,7 @@ const releaseToConnectLabel = "Release to Connect"
// Reset cytoscape nodes and branches // Reset cytoscape nodes and branches
if (cy !== undefined && cy !== null) { if (cy !== undefined && cy !== null) {
if (inputworkflow.actions !== undefined && inputworkflow.actions !== null && inputworkflow.actions.length > 0) { 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 const actionIndex = startIndex < 0 ? 0 : startIndex
if (app.actions[actionIndex] === undefined || app.actions[actionIndex] === null) { 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) console.log("No actions found for app: ", app)
return return
} }
@@ -9409,7 +9440,7 @@ const releaseToConnectLabel = "Release to Connect"
setHover(true) 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) console.log("HOVERING: ", app.id)
loadAppConfig(app.id, false) loadAppConfig(app.id, false)
} }
@@ -10295,6 +10326,10 @@ const releaseToConnectLabel = "Release to Connect"
var handled = []; var handled = [];
var results = []; var results = [];
if (cy === undefined || cy === null) {
return []
}
// maxiter = max amount of parent nodes to loop // maxiter = max amount of parent nodes to loop
// also handles breaks if there are issues // also handles breaks if there are issues
var iterations = 0; var iterations = 0;
@@ -10934,6 +10969,10 @@ const releaseToConnectLabel = "Release to Connect"
checked = workflow.auth_groups.includes(data.id) 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 ( return (
<div <div
style={{ display: 'flex', alignItems: 'center', marginBottom: '10px', marginLeft: '5px', cursor: "pointer", }} style={{ display: 'flex', alignItems: 'center', marginBottom: '10px', marginLeft: '5px', cursor: "pointer", }}
@@ -12528,7 +12567,7 @@ const releaseToConnectLabel = "Release to Connect"
if (e.target.value.id !== workflow.id && e.target.value.id.length > 0 ) { if (e.target.value.id !== workflow.id && e.target.value.id.length > 0 ) {
console.log("WORKFLOW: ", e.target.value); 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) { if (startnode !== undefined && startnode !== null) {
@@ -16643,9 +16682,17 @@ const releaseToConnectLabel = "Release to Connect"
} }
const foundnode = cy.nodes().filter((node) => { 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) { if (foundnode === undefined || foundnode === null || foundnode.length === 0) {
return return
} }
@@ -18986,7 +19033,7 @@ const releaseToConnectLabel = "Release to Connect"
) : null} ) : null}
{executionData.workflow !== undefined && executionData.workflow !== null && executionData.status !== "EXECUTING" ? {userdata.support === true && executionData.workflow !== undefined && executionData.workflow !== null && executionData.status !== "EXECUTING" ?
<div style={{marginTop: 5, marginBottom: 5, }}> <div style={{marginTop: 5, marginBottom: 5, }}>
<WorkflowValidationTimeline <WorkflowValidationTimeline
originalWorkflow={workflow} originalWorkflow={workflow}
@@ -19110,7 +19157,7 @@ const releaseToConnectLabel = "Release to Connect"
} }
} }
if (imgSrc.length === 0 && cy !== undefined && cy !== null) { if ((imgSrc === undefined || imgSrc === null || imgSrc.length === 0) && cy !== undefined && cy !== null) {
const foundnode = cy.getElementById(data.action.id) const foundnode = cy.getElementById(data.action.id)
if (foundnode !== undefined && foundnode !== null && foundnode.length > 0) { if (foundnode !== undefined && foundnode !== null && foundnode.length > 0) {
// FIXME: Find image from cytoscape action // FIXME: Find image from cytoscape action
@@ -20837,6 +20884,25 @@ const releaseToConnectLabel = "Release to Connect"
/> />
<div /> <div />
{selectedApp.authentication.parameters.map((data, index) => { {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 ( return (
<div key={index} style={{ marginTop: 10 }}> <div key={index} style={{ marginTop: 10 }}>
<LockOpenIcon style={{ marginRight: 10 }} /> <LockOpenIcon style={{ marginRight: 10 }} />
@@ -21617,8 +21683,8 @@ const releaseToConnectLabel = "Release to Connect"
// Should render if it's not the same as workflow.edited // Should render if it's not the same as workflow.edited
console.log("Clicked revision: ", newrevision) console.log("Clicked revision: ", newrevision)
setLastSaved(false) setLastSaved(false)
setSelectedVersion(newrevision); setSelectedVersion(newrevision);
setWorkflow(newrevision) setWorkflow(newrevision)
setSelectedAction({}); setSelectedAction({});
setSelectedApp({}) setSelectedApp({})
@@ -21638,18 +21704,19 @@ const releaseToConnectLabel = "Release to Connect"
cy.removeListener("free"); cy.removeListener("free");
cy.removeListener("cxttap"); cy.removeListener("cxttap");
//cy.remove('*')
setElements([]) setElements([])
}
// Remove all edges cy.remove('*')
cy.edges().remove() cy.edges().remove()
cy.nodes().remove() cy.nodes().remove()
}
// Remove all cy nodes // Remove all cy nodes
setTimeout(() => { setTimeout(() => {
//toast("Running setupgraph with new revision. Actions: " + newrevision.actions.length)
setupGraph(newrevision) setupGraph(newrevision)
}, 100) }, 250)
// Re-adding cytoscape triggers // Re-adding cytoscape triggers
+2
View File
@@ -228,6 +228,8 @@ const parseCurl = (s) => {
}; };
// Basically CRUD for each category + special // 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 = [ export const appCategories = [
{ {
"name": "Communication", "name": "Communication",
+4 -1
View File
@@ -1542,12 +1542,15 @@ const Apps = (props) => {
flex: 1, flex: 1,
padding: 15, padding: 15,
margin: 10, margin: 10,
paddingTop: 25,
backgroundColor: hover ? theme.palette.surfaceColor : "transparent", backgroundColor: hover ? theme.palette.surfaceColor : "transparent",
border: hover ? "1px solid #f85a3e" : "1px solid rgba(255,255,255,0.3)", border: hover ? "1px solid #f85a3e" : "1px solid rgba(255,255,255,0.3)",
cursor: hover ? "pointer" : "default", cursor: hover ? "pointer" : "default",
textAlign: "center", textAlign: "center",
minHeight: 150, minHeight: 150,
maxHeight: 150, maxHeight: 150,
borderRadius: theme.palette.borderRadius,
}} }}
> >
{icon} {icon}
@@ -1569,7 +1572,7 @@ const Apps = (props) => {
</h2> </h2>
<div style={{display: "flex"}}> <div style={{display: "flex"}}>
<AppCreateButton <AppCreateButton
text="Generate from OpenAPI/Swagger" text="Upload OpenAPI or Swagger"
func={() => { func={() => {
setOpenApiModal(true) setOpenApiModal(true)
}} }}
+187 -62
View File
@@ -2,14 +2,17 @@
import React, {useState, useEffect} from 'react'; import React, {useState, useEffect} from 'react';
import ReactDOM from "react-dom" import ReactDOM from "react-dom"
import { ToastContainer, toast } from "react-toastify" import { green, yellow, red, grey} from "./AngularWorkflow.jsx";
import { useInterval } from "react-powerhooks"; import { CodeHandler, Img, OuterLink, } from "../views/Docs.jsx";
import { makeStyles } from '@mui/material/styles';
import { useNavigate, Link, useParams } from "react-router-dom"; 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 { 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 { import {
Tooltip, Tooltip,
@@ -21,6 +24,10 @@ import {
Paper, Paper,
Typography, Typography,
Divider, Divider,
Dialog,
DialogTitle,
DialogContent,
} from '@mui/material'; } from '@mui/material';
import { import {
@@ -35,14 +42,15 @@ const hrefStyle = {
const bodyDivStyle = { const bodyDivStyle = {
margin: "auto", margin: "auto",
marginTop: 100,
width: isMobile? "100%":"500px", width: isMobile? "100%":"500px",
position: "relative", position: "relative",
marginTop: 25,
} }
const RunWorkflow = (defaultprops) => { 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(); let navigate = useNavigate();
const [_, setUpdate] = useState(""); // Used to force rendring, don't remove const [_, setUpdate] = useState(""); // Used to force rendring, don't remove
@@ -58,15 +66,16 @@ const RunWorkflow = (defaultprops) => {
const [apps, setApps] = React.useState([]); const [apps, setApps] = React.useState([]);
const [buttonClicked, setButtonClicked] = React.useState(""); const [buttonClicked, setButtonClicked] = React.useState("");
const [foundSourcenode, setFoundSourcenode] = React.useState(undefined); const [foundSourcenode, setFoundSourcenode] = React.useState(undefined);
const [editWorkflowModalOpen, setEditWorkflowModalOpen] = React.useState(false)
const [sharingOpen, setSharingOpen] = React.useState(false)
const boxStyle = { const boxStyle = {
color: "white", color: "white",
paddingLeft: "30px", padding: 50,
paddingRight: "30px",
paddingBottom: "30px",
paddingTop: "30px",
backgroundColor: theme.palette.surfaceColor, backgroundColor: theme.palette.surfaceColor,
marginBottom: 150, marginBottom: 150,
borderRadius: 25,
minHeight: 500,
} }
const params = useParams(); const params = useParams();
@@ -74,7 +83,7 @@ const RunWorkflow = (defaultprops) => {
props.match = {} props.match = {}
props.match.params = params props.match.params = params
const defaultTitle = "Run Workflow" const defaultTitle = workflow.name !== undefined ? workflow.name : "Run Workflow"
if (document != undefined && document.title != defaultTitle) { if (document != undefined && document.title != defaultTitle) {
document.title = defaultTitle document.title = defaultTitle
} }
@@ -96,16 +105,42 @@ const RunWorkflow = (defaultprops) => {
return true 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 = () => { const getApps = () => {
fetch(globalUrl + "/api/v1/apps", { fetch(globalUrl + "/api/v1/apps", {
method: "GET", method: "GET",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
Accept: "application/json", Accept: "application/json",
}, },
credentials: "include", credentials: "include",
}) })
.then((response) => { .then((response) => {
if (response.status !== 200) { if (response.status !== 200) {
console.log("Status not 200 for apps :O!"); console.log("Status not 200 for apps :O!");
@@ -829,52 +864,75 @@ const RunWorkflow = (defaultprops) => {
const basedata = const basedata =
<div style={bodyDivStyle}> <div style={bodyDivStyle}>
<Paper style={boxStyle}> <Paper style={boxStyle}>
<form onSubmit={(e) => {onSubmit(e)}} style={{margin: "15px 15px 15px 15px"}}> {workflow.input_markdown !== undefined && workflow.input_markdown !== null && workflow.input_markdown.length > 0 ?
<div style={{marginBottom: 20, }}>
<img <Markdown
alt={workflow.name} components={{
src={image} img: Img,
style={{ code: CodeHandler,
marginRight: 20, a: OuterLink,
width: 100, }}
height: 100, id="markdown_wrapper"
border: `2px solid ${green}`, escapeHtml={false}
borderRadius: 50, style={{
position: "absolute", maxWidth: "100%", minWidth: "100%",
top: -50, }}
left: 200, >
}} {workflow.input_markdown}
/> </Markdown>
</div>
: null}
<Typography variant="h6" style={{marginBottom: 10, marginTop: 50, textAlign: "center", }}> <form onSubmit={(e) => {onSubmit(e)}} style={{margin: "50px 0px 15px 0px",}}>
{organization} {workflow.input_markdown !== undefined && workflow.input_markdown !== null && workflow.input_markdown.length > 0 ? null :
</Typography> <div>
<Divider style={{marginTop: 20, marginBottom: 20, }}/> <img
alt={workflow.name}
src={image}
style={{
marginRight: 20,
width: 100,
height: 100,
border: `2px solid ${green}`,
borderRadius: 50,
position: "absolute",
top: -50,
left: 200,
}}
/>
{disabledButtons && message.length > 0 ? null : <Typography variant="h6" style={{marginBottom: 10, marginTop: 50, textAlign: "center", }}>
<Typography color="textSecondary" style={{textAlign: "center", }}> {organization}
{message}
</Typography> </Typography>
} <Divider style={{marginTop: 20, marginBottom: 20, }}/>
{answer !== undefined && answer !== null ? null : {disabledButtons && message.length > 0 ? null :
<Typography variant="h6" style={{marginBottom: 15, textAlign: "center", }}><b>{workflow.name}</b></Typography> <Typography color="textSecondary" style={{textAlign: "center", }}>
} {message}
{workflowQuestion.length > 0 ?
<div style={{
backgroundColor: theme.palette.inputColor,
padding: 20,
borderRadius: theme.palette.borderRadius,
marginBottom: 35,
marginTop: 30,
}}>
<Typography variant="body1" style={{ marginRight: 15, textAlign: "center", whiteSpace: "pre-line", }}>
{workflowQuestion}
</Typography> </Typography>
}
{answer !== undefined && answer !== null ? null :
<Typography variant="h6" style={{marginBottom: 15, textAlign: "center", }}><b>{workflow.name}</b></Typography>
}
{workflowQuestion.length > 0 ?
<div style={{
backgroundColor: theme.palette.inputColor,
padding: 20,
borderRadius: theme.palette.borderRadius,
marginBottom: 35,
marginTop: 30,
}}>
<Typography variant="body1" style={{ marginRight: 15, textAlign: "center", whiteSpace: "pre-line", }}>
{workflowQuestion}
</Typography>
</div>
: null}
</div> </div>
: null} }
{workflow.input_questions !== undefined && workflow.input_questions !== null && workflow.input_questions.length > 0 ? {workflow.input_questions !== undefined && workflow.input_questions !== null && workflow.input_questions.length > 0 ?
<div style={{marginBottom: 5, }}> <div style={{marginBottom: 5, }}>
{workflow.input_questions.map((question, index) => { {workflow.input_questions.map((question, index) => {
@@ -988,7 +1046,7 @@ const RunWorkflow = (defaultprops) => {
<div style={{display: "flex", marginTop: "15px"}}> <div style={{display: "flex", marginTop: "15px"}}>
<Button variant="contained" type="submit" color="primary" fullWidth disabled={!handleValidateForm(executionArgument) || executionLoading}> <Button variant="contained" type="submit" color="primary" fullWidth disabled={!handleValidateForm(executionArgument) || executionLoading}>
{executionLoading ? {executionLoading ?
<CircularProgress color="secondary" style={{color: "white",}} /> : "Run Workflow"} <CircularProgress color="secondary" style={{color: "white",}} /> : "Submit"}
</Button> </Button>
</div> </div>
} }
@@ -1018,12 +1076,79 @@ const RunWorkflow = (defaultprops) => {
</Paper> </Paper>
</div> </div>
// const isCorrectOrg = userdata.active_org.id === undefined || userdata.active_org.id === null || workflow.org_id === null || workflow.org_id === undefined || workflow.org_id.length === 0 || userdata.active_org.id === workflow.org_id
const loadedCheck = isLoaded ? const loadedCheck = isLoaded ?
<div> <div>
{editWorkflowModalOpen === true ?
<EditWorkflow
saveWorkflow={saveWorkflow}
workflow={workflow}
setWorkflow={setWorkflow}
modalOpen={editWorkflowModalOpen}
setModalOpen={setEditWorkflowModalOpen}
isEditing={true}
userdata={userdata}
usecases={undefined}
expanded={true}
/>
: null}
<Dialog
open={sharingOpen}
onClose={() => {
setSharingOpen(false);
}}
PaperProps={{
style: {
color: "white",
minWidth: isMobile ? "90%" : 400,
maxWidth: isMobile ? "90%" : 400,
minHeight: 350,
paddingTop: 25,
paddingLeft: 50,
//minWidth: isMobile ? "90%" : newWorkflow === true ? 1000 : 550,
//maxWidth: isMobile ? "90%" : newWorkflow === true ? 1000 : 550,
},
}}
>
<DialogTitle style={{padding: 30, paddingBottom: 0, zIndex: 1000,}}>
Share Landingpage
</DialogTitle>
<DialogContent style={{paddingTop: 10, display: "flex", minHeight: 300, zIndex: 1001, paddingBottom: 200, }}>
</DialogContent>
</Dialog>
{isLoggedIn && userdata?.active_org?.id === workflow?.org_id ?
<div style={{position: "fixed", top: 10, right: 20, }}>
<Button
variant={"outlined"}
color={"secondary"}
style={{marginRight: 10, }}
onClick={() => {
setEditWorkflowModalOpen(true)
}}
>
Edit Details
</Button>
<Button
variant={"outlined"}
color={"secondary"}
disabled
onClick={() => {
setSharingOpen(true)
}}
>
Manage Sharing
</Button>
</div>
: null}
{basedata} {basedata}
</div> </div>
: :
<div> <div>
<CircularProgress />
</div> </div>
return ( return (
+6 -3
View File
@@ -21,6 +21,7 @@ import {
Paper, Paper,
Chip, Chip,
Checkbox, Checkbox,
Fade,
} from "@mui/material"; } from "@mui/material";
import { import {
@@ -1741,9 +1742,11 @@ const Dashboard = (props) => {
</div> </div>
const dataWrapper = const dataWrapper =
<div style={{ maxWidth: 1366, margin: "auto" }}> <Fade in={true} timeout={1250}>
{data} <div style={{ maxWidth: 1366, margin: "auto" }}>
</div> {data}
</div>
</Fade>
return dataWrapper return dataWrapper
+6 -2
View File
@@ -32,7 +32,7 @@ const Welcome = (props) => {
const [inputUsecase, setInputUsecase] = useState({}); const [inputUsecase, setInputUsecase] = useState({});
const [frameworkData, setFrameworkData] = useState(undefined); const [frameworkData, setFrameworkData] = useState(undefined);
const [discoveryWrapper, setDiscoveryWrapper] = useState(undefined); const [discoveryWrapper, setDiscoveryWrapper] = useState(undefined);
const [activeStep, setActiveStep] = React.useState(1); const [activeStep, setActiveStep] = React.useState(0);
const [apps, setApps] = React.useState([]); const [apps, setApps] = React.useState([]);
const [defaultSearch, setDefaultSearch] = React.useState("") const [defaultSearch, setDefaultSearch] = React.useState("")
const [selectionOpen, setSelectionOpen] = React.useState(false) const [selectionOpen, setSelectionOpen] = React.useState(false)
@@ -285,6 +285,9 @@ const Welcome = (props) => {
} }
setActiveStep(foundTab-1) setActiveStep(foundTab-1)
//setRenderDone(
} }
} }
}, []) }, [])
@@ -398,6 +401,7 @@ const Welcome = (props) => {
discoveryWrapper={discoveryWrapper} discoveryWrapper={discoveryWrapper}
setDiscoveryWrapper={setDiscoveryWrapper} setDiscoveryWrapper={setDiscoveryWrapper}
appFramework={frameworkData} appFramework={frameworkData}
setAppFramework={setFrameworkData}
getFramework={getFramework} getFramework={getFramework}
steps={steps} steps={steps}
skipped={skipped} skipped={skipped}
@@ -462,7 +466,7 @@ const Welcome = (props) => {
OR OR
</Typography> */} </Typography> */}
</div> </div>
<Card style={paperObject} onClick={() => { <Card disabled style={paperObject} onClick={() => {
if (isCloud) { if (isCloud) {
ReactGA.event({ ReactGA.event({
category: "welcome", category: "welcome",