Merge branch '1.3.0'

This commit is contained in:
Aditya
2023-11-16 01:10:52 +05:30
24 changed files with 1052 additions and 769 deletions
+12 -1
View File
@@ -70,7 +70,18 @@ http {
try_files $uri /index.html; try_files $uri /index.html;
} }
location ~ /api/v(1|2) { location /api/v1 {
proxy_pass http://{{ getenv "BACKEND_HOSTNAME" "shuffle-backend" }}:5001;
proxy_buffering off;
proxy_http_version 1.1;
proxy_connect_timeout 900;
proxy_send_timeout 900;
proxy_read_timeout 900;
send_timeout 900;
}
location /api/v2 {
proxy_pass http://{{ getenv "BACKEND_HOSTNAME" "shuffle-backend" }}:5001; proxy_pass http://{{ getenv "BACKEND_HOSTNAME" "shuffle-backend" }}:5001;
proxy_buffering off; proxy_buffering off;
proxy_http_version 1.1; proxy_http_version 1.1;
+2 -3
View File
@@ -39,7 +39,6 @@ cytoscape.use(edgehandles);
export const findSpecificApp = (framework, inputcategory) => { export const findSpecificApp = (framework, inputcategory) => {
// Get the frameworkinfo for the org and fill in // Get the frameworkinfo for the org and fill in
//
if (framework === undefined || framework === null) { if (framework === undefined || framework === null) {
console.log("findSpecificApp: framework is null") console.log("findSpecificApp: framework is null")
return null return null
@@ -52,7 +51,7 @@ export const findSpecificApp = (framework, inputcategory) => {
const category = inputcategory.toLowerCase().split(":")[0].trim() const category = inputcategory.toLowerCase().split(":")[0].trim()
console.log("findSpecificApp: ", category, framework) //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) {
return framework["EDR & AV"] return framework["EDR & AV"]
@@ -65,7 +64,7 @@ export const findSpecificApp = (framework, inputcategory) => {
description: "", description: "",
id: "", id: "",
} }
} else if (category === "communication") { } else if (category === "communication" || category === "comms") {
if (framework["Comms"] !== undefined && framework["Comms"].name !== undefined) { if (framework["Comms"] !== undefined && framework["Comms"].name !== undefined) {
return framework["Comms"] return framework["Comms"]
} }
+2 -2
View File
@@ -31,7 +31,7 @@ import {
const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240") const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240")
//const searchClient = algoliasearch("L55H18ZINA", "a19be455e7e75ee8f20a93d26b9fc6d6") //const searchClient = algoliasearch("L55H18ZINA", "a19be455e7e75ee8f20a93d26b9fc6d6")
const AppGrid = props => { const AppGrid = props => {
const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs, userdata } = props const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs, userdata, isHeader } = props
const isCloud = const isCloud =
window.location.host === "localhost:3002" || window.location.host === "localhost:3002" ||
@@ -167,7 +167,7 @@ const AppGrid = props => {
backgroundColor: index === mouseHoverIndex ? "rgba(255,255,255,0.8)" : theme.palette.inputColor, backgroundColor: index === mouseHoverIndex ? "rgba(255,255,255,0.8)" : theme.palette.inputColor,
color: index === mouseHoverIndex ? theme.palette.inputColor : "rgba(255,255,255,0.8)", color: index === mouseHoverIndex ? theme.palette.inputColor : "rgba(255,255,255,0.8)",
border: `1px solid ${innerColor}`, border: `1px solid ${innerColor}`,
padding: 15, padding: isHeader ? null : 15,
cursor: "pointer", cursor: "pointer",
position: "relative", position: "relative",
minHeight: 116, minHeight: 116,
+232 -340
View File
@@ -3,25 +3,26 @@ import theme from '../theme.jsx';
import ReactGA from 'react-ga4'; import ReactGA from 'react-ga4';
import { useNavigate, Link } from 'react-router-dom'; import { useNavigate, Link } from 'react-router-dom';
import { Search as SearchIcon, CloudQueue as CloudQueueIcon, Code as CodeIcon, Close as CloseIcon, Folder as FolderIcon, LibraryBooks as LibraryBooksIcon } from '@mui/icons-material'; import { Search as Searchicon, CloudQueue as CloudQueueicon, Code as Codeicon, Close as Closeicon, Folder as Foldericon, LibraryBooks as LibraryBooksicon, Delete as DeleteIcon, Close as CloseIcon, } from '@mui/icons-material';
import aa from 'search-insights' import aa from 'search-insights'
import DeleteIcon from '@mui/icons-material/Delete'; import Deleteicon from '@mui/icons-material/Delete';
import ShowChartIcon from '@mui/icons-material/ShowChart'; import ShowCharticon from '@mui/icons-material/ShowChart';
import ExploreIcon from '@mui/icons-material/Explore'; import Exploreicon from '@mui/icons-material/Explore';
import LightbulbIcon from "@mui/icons-material/Lightbulb"; import Lightbulbicon from "@mui/icons-material/Lightbulb";
import NewReleasesIcon from "@mui/icons-material/NewReleases"; import NewReleasesicon from "@mui/icons-material/NewReleases";
import ExtensionIcon from "@mui/icons-material/Extension"; import Extensionicon from "@mui/icons-material/Extension";
import EmailIcon from "@mui/icons-material/Email"; import Emailicon from "@mui/icons-material/Email";
import FingerprintIcon from '@mui/icons-material/Fingerprint'; import Fingerprinticon from '@mui/icons-material/Fingerprint';
import AppSearch from "../components/Appsearch.jsx"; import AppSearch from "../components/Appsearch.jsx";
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import { findSpecificApp } from "../components/AppFramework.jsx";
import { import {
Zoom, Zoom,
Grid, Grid,
Paper, Paper,
TextField, TextField,
Collapse, Collapse,
IconButton, iconButton,
Avatar, Avatar,
ButtonBase, ButtonBase,
InputAdornment, InputAdornment,
@@ -32,370 +33,261 @@ import {
ListItem, ListItem,
ListItemAvatar, ListItemAvatar,
ListItemText, ListItemText,
IconButton,
} from '@mui/material'; } from '@mui/material';
const AppSearchButtons = (props) => { const AppSearchButtons = (props) => {
const { userdata, globalUrl, appFramework, defaultSearch, finishedApps, onNodeSelect, setDiscoveryData, appName, AppImage, setDefaultSearch, discoveryData } = props const { userdata, globalUrl, appFramework, finishedApps, appType, totalApps, index, onNodeSelect, setDiscoveryData, appName, AppImage, setDefaultSearch, discoveryData, checkLogin, setMissing, } = props
const ref = useRef() const ref = useRef()
const [moreButton, setMoreButton] = useState(false);
let navigate = useNavigate(); let navigate = useNavigate();
const sizing = moreButton ? 510 : 480; const [isHover, setIsHover] = useState(false);
const [localSearchOpen, setLocalSearchOpen] = useState(false)
const [newSelectedApp, setNewSelectedApp] = useState(undefined)
useEffect(() => {
console.log("AppSearchButtons: newSelectedApp: " + JSON.stringify(newSelectedApp))
if (newSelectedApp !== undefined && setMissing != undefined) {
console.log("AppSearchButtons: setMissing is defined!")
const submitAppFramework = {
"description": newSelectedApp.description,
"id": newSelectedApp.objectID,
"name": newSelectedApp.name,
"type": appType,
"large_image": newSelectedApp.image_url,
}
setFrameworkItem(submitAppFramework)
setMissing(newSelectedApp)
}
}, [newSelectedApp])
if (appType === undefined) {
console.log("Apptype is required in AppSearchButtons")
return null;
}
const buttonWidth = 450; const buttonWidth = 450;
const buttonMargin = 10; const buttonMargin = 10;
const bottomButtonStyle = {
borderRadius: 200,
marginTop: moreButton ? 44 : "",
height: 51,
width: 510,
fontSize: 16,
// background: "linear-gradient(89.83deg, #FF8444 0.13%, #F2643B 99.84%)",
background: "linear-gradient(90deg, #F86744 0%, #F34475 100%)",
padding: "16px 24px",
// top: 20,
// margin: "auto",
textTransform: 'capitalize',
itemAlign: "center",
// marginTop: 25
// marginLeft: "65px",
};
const buttonStyle = {
flex: 1,
width: 224,
padding: 25,
margin: buttonMargin,
color: "var(--White-text, #F1F1F1)",
fontWeight: 400,
fontSize: 17,
background: "rgba(33, 33, 33, 1)",
textTransform: 'capitalize',
border: "1px solid rgba(33, 33, 33, 1)",
borderRadius: 8,
marginRight: 8,
};
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; const handleMouseEnter = () => {
setIsHover(true);
};
const handleMouseLeave = () => {
setIsHover(false);
};
const mouseOver = (e) => {
e.target.style.border = "1px solid #f85a3e"; const foundApp = findSpecificApp(appFramework, appType)
if (foundApp === undefined || foundApp === null) {
console.log("AppSearchButtons: App not found in appFramework: " + appType)
return null
}
const setFrameworkItem = (data) => {
fetch(globalUrl + "/api/v1/apps/frameworkConfiguration", {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
body: JSON.stringify(data),
credentials: "include",
})
.then((response) => {
if (response.status !== 200) {
console.log("Status not 200 for framework!");
}
if (checkLogin !== undefined) {
checkLogin()
}
return response;
})
.then((responseJson) => {
if (responseJson.success === false) {
if (responseJson.reason !== undefined) {
toast("Failed updating: " + responseJson.reason)
} else {
toast("Failed to update framework for your org.")
}
}
//setFrameworkLoaded(true)
//setFrameworkData(responseJson)
})
.catch((error) => {
if (checkLogin !== undefined) {
checkLogin()
}
toast(error.toString());
//setFrameworkLoaded(true)
})
} }
const mouseOut = (e) => {
e.target.style.border = "1px solid rgb(33, 33, 33)"; const icon = foundApp.large_image
let xsValue = 12;
if (index !== undefined && index !== 0) {
xsValue = 6;
}
if (index === totalApps - 4 && totalApps % 2 === 1) {
xsValue = 12;
}
if (index === totalApps - 1 || index === totalApps - 2 || index === totalApps - 3) {
xsValue = 4;
} }
return ( return (
<Grid item xs={11} style={{ display: "flex" }}> <Grid item xs={xsValue} style={{ alignItems: "center", marginTop: 5, }}
onMouseEnter={handleMouseEnter}
{/*<FormLabel style={{ color: "#B9B9BA" }}>Find your integrations!</FormLabel>*/} onMouseLeave={handleMouseLeave}
{/* <div style={{ display: "flex", width: 510, height:100 }}> >
<Button {localSearchOpen ? (
disabled={finishedApps.includes("CASES")} <div
variant={ style={{
defaultSearch === "CASES" ? "contained" : "outlined" width: 319,
} height: 395,
color="secondary" flexShrink: 0,
style={{ marginLeft: 70,
flex: 1, marginTop: 68,
width: "100%", position: "absolute",
padding: 25, zIndex: 100,
margin: buttonMargin, borderRadius: 6,
fontSize: 18, border: "1px solid var(--Container-Stroke, #494949)",
color: "var(--White-text, #F1F1F1)", background: "var(--Container, #212121)",
fontWeight: 400, boxShadow: "8px 8px 32px 24px rgba(0, 0, 0, 0.16)",
background: "rgba(33, 33, 33, 1)", }}
borderRadius: 8, >
textTransform: 'capitalize', <div style={{ display: "flex" }}>
border: "1px solid rgba(33, 33, 33, 1)" , <div style={{ display: "flex", textAlign: "center", textTransform: "capitalize" }}>
}} <Typography style={{ padding: 16, color: "#FFFFFF", textTransform: "capitalize" }}> {discoveryData} </Typography>
onMouseOver={mouseOver}
onMouseOut={mouseOut}
// startIcon = {defaultSearch === "CASES" ? newSelectedApp.image_url : <LightbulbIcon/>}
onClick={(event) => {
onNodeSelect("CASES");
setDefaultSearch(discoveryData.label)
}}
>
{appFramework === undefined || appFramework.cases === undefined || appFramework.cases.large_image === undefined ||
appFramework === null || appFramework.cases === null || appFramework.cases.large_image === null || appFramework.cases.large_image.length === 0 ?
<div style={{width: 40, border: "1px solid rgba(33, 33, 33, 1) !importent", height: 40, borderRadius: 9999, backgroundColor: "#2F2F2F", textAlign:"center"}}>
<LightbulbIcon style={{ marginTop: 6 }} />
</div>
: <img style={{ marginRight: 8, width: 40, height: 40, flexShrink: 0, borderRadius: 40, }} src={AppImage} />}
<div style={{marginLeft: 8, }}>
<Typography style={{display:"flex",border:"none"}} >Case Management</Typography>
{appFramework === undefined || appFramework.cases === undefined || appFramework.cases.name === undefined ||
appFramework === null || appFramework.cases === null || appFramework.cases.name === null || appFramework.cases.name.length === 0 ?
"":<Typography style={{fontSize: 12, textAlign:"left", color:"var(--label-grey-text, #9E9E9E)" }} >{AppName}</Typography>}
</div>
</Button>
</div>
<div style={{ display: "flex", width: 510, height: 100 }}>
<Button
disabled={finishedApps.includes("SIEM")}
variant={
defaultSearch === "SIEM" ? "contained" : "outlined"
}
style={buttonStyle}
// startIcon={<SearchIcon />}
onMouseOver={mouseOver}
onMouseOut={mouseOut}
color="secondary"
onClick={(event) => {
onNodeSelect("SIEM");
setDefaultSearch(discoveryData.label)
}}
>
{appFramework === undefined || appFramework.siem === undefined || appFramework.siem.large_image === undefined ||
appFramework === null || appFramework.siem === null || appFramework.siem.large_image === null || appFramework.siem.large_image.length === 0 ?
<div style={{width: 40, border: "1px solid rgba(33, 33, 33, 1) !importent", height: 40, borderRadius: 9999, backgroundColor: "#2F2F2F", textAlign:"center"}}>
<SearchIcon style={{ marginTop: 6 }} />
</div>
: <img style={{ marginRight: 8, width: 40, height: 40, flexShrink: 0, borderRadius: 40, }} src={appFramework.siem.large_image} />}
<div style={{marginLeft: 8,}}>
<Typography style={{display:"flex",}}>SIEM</Typography>
{appFramework === undefined || appFramework.siem === undefined || appFramework.siem.name === undefined ||
appFramework === null || appFramework.siem === null || appFramework.siem.name === null || appFramework.siem.name.length === 0 ?
"":<Typography style={{fontSize: 12, textAlign:"left", color:"var(--label-grey-text, #9E9E9E)" }} >{appFramework.siem.name.split('_').join(' ')}</Typography>}
</div> </div>
</Button> <div style={{ display: "flex" }}>
<Button <Tooltip
disabled={ title="Close"
finishedApps.includes("EDR & AV") || placement="top"
finishedApps.includes("ERADICATION") style={{ zIndex: 10011 }}
} >
onMouseOver={mouseOver} <IconButton
onMouseOut={mouseOut} style={{
variant={ flex: 1,
defaultSearch === "Eradication" ? "contained" : "outlined" // width: 224,
} marginLeft: discoveryData === ("communication") ? 112 : 200,
style={buttonStyle} width: "100%",
// startIcon={<NewReleasesIcon />} marginBottom: 23,
color="secondary" fontSize: 16,
onClick={(event) => { background: "rgba(33, 33, 33, 1)",
onNodeSelect("ERADICATION"); borderColor: "rgba(33, 33, 33, 1)",
}} borderRadius: 8,
> }}
{appFramework === undefined || appFramework.edr === undefined || appFramework.edr.large_image === undefined || onClick={() => {
appFramework === null || appFramework.edr === null || appFramework.edr.large_image === null || appFramework.edr.large_image.length === 0 ? setLocalSearchOpen(false)
<div style={{width: 40, border: "1px solid rgba(33, 33, 33, 1) !importent", height: 40, borderRadius: 9999, backgroundColor: "#2F2F2F", textAlign:"center"}}> }}
<NewReleasesIcon style={{marginTop: 8 }} /> >
</div> <Closeicon style={{ width: 16 }} />
: <img style={{ marginRight: 8, width: 40, height: 40, flexShrink: 0, borderRadius: 40, }} src={appFramework.edr.large_image} />} </IconButton>
<div style={{marginLeft: 8,}}> </Tooltip>
<Typography style={{display:"flex",}}>Endpoint</Typography> <Tooltip
{appFramework === undefined || appFramework.edr === undefined || appFramework.edr.name === undefined || title="Delete app"
appFramework === null || appFramework.edr === null || appFramework.edr.name === null || appFramework.edr.name.length === 0 ? placement="bottom"
"":<Typography style={{fontSize: 12, textAlign:"left", color:"var(--label-grey-text, #9E9E9E)" }} >{appFramework.edr.name.split('_').join(' ')}</Typography>} style={{ zIndex: 10011 }}
>
<IconButton
style={{ zIndex: 12501, position: "absolute", top: 32, right: 16 }}
onClick={(e) => {
e.preventDefault();
setLocalSearchOpen(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>
</Button>
</div>
<div style={{ display: "flex", width: 510, height: 100 }}>
<Button
disabled={finishedApps.includes("INTEL")}
variant={
defaultSearch === "INTEL" ? "contained" : "outlined"
}
onMouseOver={mouseOver}
onMouseOut={mouseOut}
style={buttonStyle}
// startIcon={<ExtensionIcon />}
color="secondary"
onClick={(event) => {
onNodeSelect("INTEL");
}}
>
{appFramework === undefined || appFramework.intel === undefined || appFramework.intel.large_image === undefined ||
appFramework === null || appFramework.intel === null || appFramework.intel.large_image === null || appFramework.intel.large_image.length === 0 ?
<div style={{width: 40, border: "1px solid rgba(33, 33, 33, 1) !importent", height: 40, borderRadius: 9999, backgroundColor: "#2F2F2F", textAlign:"center"}}>
<ExtensionIcon style={{ marginTop: 8 }} />
</div>
: <img style={{ marginRight: 8, width: 40, height: 40, flexShrink: 0, borderRadius: 40, }} src={appFramework.intel.large_image} />}
<div style={{marginLeft: 8,}}>
<Typography style={{display:"flex",}}>Intel</Typography>
{appFramework === undefined || appFramework.intel === undefined || appFramework.intel.name === undefined ||
appFramework === null || appFramework.intel === null || appFramework.intel.name === null || appFramework.intel.name.length === 0 ?
"":<Typography style={{fontSize: 12, textAlign:"left", color:"var(--label-grey-text, #9E9E9E)" }} >{appFramework.intel.name.split('_').join(' ')}</Typography>}
</div>
</Button>
<Button
disabled={
finishedApps.includes("COMMS") ||
finishedApps.includes("EMAIL")
}
variant={
defaultSearch === "EMAIL" ? "contained" : "outlined"
}
onMouseOver={mouseOver}
onMouseOut={mouseOut}
style={buttonStyle}
// startIcon={<EmailIcon />}
color="secondary"
onClick={(event) => {
onNodeSelect("EMAIL");
}}
>
{appFramework === undefined || appFramework.communication === undefined || appFramework.communication.large_image === undefined ||
appFramework === null || appFramework.communication === null || appFramework.communication.large_image === null || appFramework.communication.large_image.length === 0 ?
<div style={{width: 40, border: "1px solid rgba(33, 33, 33, 1) !importent", height: 40, borderRadius: 9999, backgroundColor: "#2F2F2F", textAlign:"center"}}>
<EmailIcon style={{ marginTop: 8 }} />
</div>
: <img style={{ marginRight: 8, width: 40, height: 40, flexShrink: 0, borderRadius: 40, }} src={appFramework.communication.large_image} />}
<div style={{marginLeft: 8,}}>
<Typography style={{display:"flex",}}>Email</Typography>
{appFramework === undefined || appFramework.communication === undefined || appFramework.communication.name === undefined ||
appFramework === null || appFramework.communication === null || appFramework.communication.name === null || appFramework.communication.name.length === 0 ?
"":<Typography style={{fontSize: 12, textAlign:"left", color:"var(--label-grey-text, #9E9E9E)" }} >{appFramework.communication.name.split('_').join(' ')}</Typography>}
</div>
</Button>
</div>
{moreButton ? (
<div style={{ display: "flex", width: 510, height: 100, marginBottom: 20 }}>
<Button
disabled={finishedApps.includes("NETWORK")}
variant={
defaultSearch === "NETWORK" ? "contained" : "outlined"
}
onMouseOver={mouseOver}
onMouseOut={mouseOut}
style={buttonStyle}
// startIcon={<ExtensionIcon />}
color="secondary"
onClick={(event) => {
onNodeSelect("NETWORK");
}}
>
{appFramework === undefined || appFramework.network === undefined || appFramework.network.large_image === undefined ||
appFramework === null || appFramework.network === null || appFramework.network.large_image === null || appFramework.network.large_image.length === 0 ?
<div style={{width: 40, border: "1px solid rgba(33, 33, 33, 1) !importent", height: 40, borderRadius: 9999, backgroundColor: "#2F2F2F", textAlign:"center"}}>
<ShowChartIcon style={{ marginTop: 8 }} />
</div>
: <img style={{ marginRight: 8, width: 40, height: 40, flexShrink: 0, borderRadius: 40, }} src={appFramework.network.large_image} />}
<div style={{marginLeft: 8,}}>
<Typography style={{display:"flex",}}>Network</Typography>
{appFramework === undefined || appFramework.network === undefined || appFramework.network.name === undefined ||
appFramework === null || appFramework.network === null || appFramework.network.name === null || appFramework.network.name.length === 0 ?
"":<Typography style={{fontSize: 10, textAlign:"left", color:"var(--label-grey-text, #9E9E9E)" }} >{appFramework.network.name}</Typography>}
</div>
</Button>
<Button
disabled={
finishedApps.includes("ASSETS")
}
variant={
defaultSearch === "ASSETS" ? "contained" : "outlined"
}
onMouseOver={mouseOver}
onMouseOut={mouseOut}
style={buttonStyle}
// startIcon={<EmailIcon />}
color="secondary"
onClick={(event) => {
onNodeSelect("ASSETS");
}}
>
{appFramework === undefined || appFramework.assets === undefined || appFramework.assets.large_image === undefined ||
appFramework === null || appFramework.assets === null || appFramework.assets.large_image === null || appFramework.assets.large_image.length === 0 ?
<div style={{width: 40, border: "1px solid rgba(33, 33, 33, 1) !importent", height: 40, borderRadius: 9999, backgroundColor: "#2F2F2F", textAlign:"center"}}>
<ExploreIcon style={{ marginTop: 8 }} />
</div>
: <img style={{ marginRight: 8, width: 40, height: 40, flexShrink: 0, borderRadius: 40, }} src={appFramework.assets.large_image} />}
<div style={{marginLeft: 8,}}>
<Typography style={{display:"flex",}}>Assets</Typography>
{appFramework === undefined || appFramework.assets === undefined || appFramework.assets.name === undefined ||
appFramework === null || appFramework.assets === null || appFramework.assets.name === null || appFramework.assets.name.length === 0 ?
"":<Typography style={{fontSize: 10, textAlign:"left", color:"var(--label-grey-text, #9E9E9E)" }} >{appFramework.assets.name}</Typography>}
</div>
</Button>
<Button
disabled={
finishedApps.includes("IAM")
}
variant={
defaultSearch === "IAM" ? "contained" : "outlined"
}
onMouseOver={mouseOver}
onMouseOut={mouseOut}
style={buttonStyle}
// startIcon={<EmailIcon />}
color="secondary"
onClick={(event) => {
onNodeSelect("IAM");
}}
>
{appFramework === undefined || appFramework.iam === undefined || appFramework.iam.large_image === undefined ||
appFramework === null || appFramework.iam === null || appFramework.iam.large_image === null || appFramework.iam.large_image.length === 0 ?
<div style={{width: 40, border: "1px solid rgba(33, 33, 33, 1) !importent", height: 40, borderRadius: 9999, backgroundColor: "#2F2F2F", textAlign:"center"}}>
<FingerprintIcon style={{ marginTop: 8 }} />
</div>
: <img style={{ marginRight: 8, width: 40, height: 40, flexShrink: 0, borderRadius: 40, }} src={appFramework.iam.large_image} />}
<div style={{marginLeft: 8,}}>
<Typography style={{display:"flex",}}>IAM</Typography>
{appFramework === undefined || appFramework.iam === undefined || appFramework.iam.name === undefined ||
appFramework === null || appFramework.iam === null || appFramework.iam.name === null || appFramework.iam.name.length === 0 ?
"":<Typography style={{fontSize: 8, textAlign:"left", color:"var(--label-grey-text, #9E9E9E)" }} >{appFramework.iam.name}</Typography>}
</div>
</Button>
</div> </div>
) <div
: style={{ width: "100%", border: "1px #494949 solid" }}
<div style={{ display: "flex", width: 510, paddingLeft: 165, }}> />
<Button <AppSearch
style={{ color: "#f86a3e", textTransform: 'capitalize', border: 2, backgroundColor: "var(--Background-color, #1A1A1A)" }} defaultSearch={appType}
className="btn btn-primary" userdata={userdata}
onClick={(event) => { newSelectedApp={newSelectedApp}
setMoreButton(true); setNewSelectedApp={setNewSelectedApp}
}} />
> </div>
<Typography style={{ textDecorationLine: 'underline', }}> ) : null}
See more Categories <div style={{ display: "flex", height: 70, border: isHover ? "1px solid #f85a3e" : "var(--Container, #212121)", borderRadius: 8, background: isHover ? "var(--Container, #212121)":"var(--Container, #212121)",
</Typography> alignItems: "center", justifyContent: "center",}}
</Button>
</div>} */}
<div style={{ display: "flex", width: 510, height: 64, borderRadius: 8, background: "var(--Container, #212121)" }}
> >
<div <Button
onMouseOver={mouseOver} fullWidth
onMouseOut={mouseOut}
disabled={finishedApps.includes("CASES")}
variant={
defaultSearch === "CASES" ? "contained" : "outlined"
}
color="secondary" color="secondary"
style={{ style={{
flex: 1, height:"100%",
width: "100%", width:"100%",
margin: buttonMargin, display:"grid"
fontSize: 18,
color: "var(--White-text, #F1F1F1)",
fontWeight: 400,
background: "rgba(33, 33, 33, 1)",
borderRadius: 8,
textTransform: 'capitalize',
}} }}
// startIcon = {defaultSearch === "CASES" ? newSelectedApp.image_url : <LightbulbIcon/>}
onClick={(event) => { onClick={(event) => {
onNodeSelect("CASES"); if (onNodeSelect !== undefined) {
setDefaultSearch(discoveryData.label) onNodeSelect(appType);
} else {
setLocalSearchOpen(true)
}
if (setDefaultSearch !== undefined) {
setDefaultSearch(appType)
}
}} }}
> >
<div style={{marginLeft: 20, display:"flex", textAlign:"center", alignItems:"center", marginLeft: 100, width: 320, marginRight: "auto" }}> <div style={{ display:"flex", textAlign:"center", justifyContent: "center", alignItems:"center", marginRight: "auto" }}>
{AppImage === undefined || AppImage === undefined || {AppImage === undefined || AppImage === null || AppImage.length === 0 ?
AppImage === null || AppImage === null || AppImage.length === 0 ?
<div style={{ width: 40, height: 40, borderRadius: 9999, backgroundColor: "#2F2F2F", textAlign: "center" }}> <div style={{ width: 40, height: 40, borderRadius: 9999, backgroundColor: "#2F2F2F", textAlign: "center" }}>
<LightbulbIcon style={{ marginTop: 6 }} /> <img style={{paddingLeft: 11, paddingTop: 11, width: 40, height: 40, flexShrink: 0, }} src={icon} />
</div> </div>
: <img style={{ marginRight: 8, width: 40, height: 40, flexShrink: 0, borderRadius: 40, }} src={AppImage} />} :
<img style={{ marginRight: 8, width: 35, height: 35, flexShrink: 0, borderRadius: 40, }} src={AppImage} />
}
<div style={{ marginLeft: 8, }}> <div style={{ marginLeft: 8, }}>
<Typography style={{ display: "flex", border: "none" }} >Case Management</Typography> <Typography style={{
display: "flex",
border: "none",
fontSize: 16,
textTransform: ["siem", "edr", "iam"].includes(appType.toLowerCase()) ? "uppercase" : 'capitalize'
}} >
{appType}
</Typography>
{appName === undefined || appName === undefined || {appName === undefined || appName === undefined ||
appName === null || appName === null || appName.length === 0 ? appName === null || appName === null || appName.length === 0 ?
"" ""
: :
<Typography style={{ fontSize: 12, textAlign: "left", color: "var(--label-grey-text, #9E9E9E)" }} >{appName}</Typography>} <Typography style={{ fontSize: 12, textAlign: "left", color: "var(--label-grey-text, #9E9E9E)", textTransform: "lowercase" }} >{appName.split('_').join(' ')}</Typography>}
</div> </div>
</div> </div>
</div> </Button>
</div> </div>
</Grid> </Grid>
) )
+188 -126
View File
@@ -2,7 +2,7 @@ import React, { useState, useEffect, useRef } from "react";
import theme from '../theme.jsx'; import theme from '../theme.jsx';
import ReactGA from 'react-ga4'; import ReactGA from 'react-ga4';
import { useNavigate, Link } from 'react-router-dom'; import { useNavigate, Link } from 'react-router-dom';
import {isMobile} from "react-device-detect";
import { Search as SearchIcon, CloudQueue as CloudQueueIcon, Code as CodeIcon, Close as CloseIcon, Folder as FolderIcon, LibraryBooks as LibraryBooksIcon } from '@mui/icons-material'; import { Search as SearchIcon, CloudQueue as CloudQueueIcon, Code as CodeIcon, Close as CloseIcon, Folder as FolderIcon, LibraryBooks as LibraryBooksIcon } from '@mui/icons-material';
import aa from 'search-insights' import aa from 'search-insights'
import DeleteIcon from '@mui/icons-material/Delete'; import DeleteIcon from '@mui/icons-material/Delete';
@@ -50,6 +50,7 @@ const AppSelection = props => {
const [newSelectedApp, setNewSelectedApp] = React.useState({}) const [newSelectedApp, setNewSelectedApp] = React.useState({})
const [finishedApps, setFinishedApps] = React.useState([]) const [finishedApps, setFinishedApps] = React.useState([])
const [appButtons, setAppButtons] = useState([]) const [appButtons, setAppButtons] = useState([])
const [lastPosted, setLastPosted] = useState([])
const [apps, setApps] = useState([]) const [apps, setApps] = useState([])
const [appName, setAppName] = React.useState(); const [appName, setAppName] = React.useState();
const [moreButton, setMoreButton] = useState(false); const [moreButton, setMoreButton] = useState(false);
@@ -59,6 +60,117 @@ const AppSelection = props => {
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";
useEffect(() => {
if (newSelectedApp === undefined || newSelectedApp.objectID === undefined || newSelectedApp.objectID === undefined || newSelectedApp.objectID.length === 0) {
return
}
const submitNewApp = {
description: newSelectedApp.description,
id: newSelectedApp.objectID,
large_image: newSelectedApp.image_url,
name: newSelectedApp.name,
type: discoveryData
}
if (discoveryData === "CASES") {
appFramework.cases = submitNewApp
}
else if (discoveryData === "SIEM") {
appFramework.siem = submitNewApp
}
else if (discoveryData === "ERADICATION") {
appFramework.edr = submitNewApp
}
else if (discoveryData === "INTEL") {
appFramework.intel = submitNewApp
}
else if (discoveryData === "EMAIL") {
appFramework.communication = submitNewApp
}
else if (discoveryData === "NETWORK") {
appFramework.network = submitNewApp
}
else if (discoveryData === "ASSETS") {
appFramework.assets = submitNewApp
}
else if (discoveryData === "IAM") {
appFramework.iam = submitNewApp
}
setFrameworkItem(submitNewApp);
setSelectionOpen(false);
console.log("Selected app changed (effect)");
}, [newSelectedApp]);
useEffect(() => {
var tempApps = []
if (tempApps.length === 0) {
// Object.entries(appFramework).forEach(([key, value]) => {
// value.type = key;
// tempApps.push(value);
// });
// // Define the custom sorting order
// const customSortingOrder = ["CASES", "SIEM", "ENDPOINT", "INTEL", "EMAIL"];
const lastApps = {}
let endTypes = ["network", "assets", "iam"]
if (appFramework === undefined || appFramework === null || Object.keys(appFramework).length === 0) {
//window.location.href = "/welcome"
return
}
Object.entries(appFramework).forEach(([key, value]) => {
if (key.toLowerCase() === "other" || key.toLowerCase() === "communication") {
return
}
value.type = key;
if (endTypes.includes(value.type.toLowerCase())) {
lastApps[value.type] = value
return
}
if (lastPosted.type === value.type) {
value = lastPosted
}
tempApps.push(JSON.parse(JSON.stringify(value)));
});
tempApps.sort((a, b) => {
if (a.type.length > b.type.length) {
return -1;
} else if (a.type.length < b.type.length) {
return 1;
}
});
let lastType = lastPosted.type === undefined ? "" : lastPosted.type.toLowerCase()
if (endTypes.includes(lastType)) {
lastApps[lastPosted.type] = lastPosted
}
if (moreButton) {
tempApps.push(JSON.parse(JSON.stringify(lastApps["network"])))
tempApps.push(JSON.parse(JSON.stringify(lastApps["assets"])))
tempApps.push(JSON.parse(JSON.stringify(lastApps["iam"])))
}
setAppButtons(tempApps)
console.log("Updated appButtons: ", appButtons)
GetApps()
}
}, [lastPosted, moreButton])
if (appFramework === undefined || appFramework === null || Object.keys(appFramework).length === 0) {
//window.location.href = "/welcome"
return null
}
const setFrameworkItem = (data) => { const setFrameworkItem = (data) => {
console.log("Setting framework item: ", data, isCloud) console.log("Setting framework item: ", data, isCloud)
@@ -75,7 +187,7 @@ const AppSelection = props => {
body: JSON.stringify(data), body: JSON.stringify(data),
credentials: "include", credentials: "include",
}) })
.then((response) => { .then(async (response) => {
if (response.status !== 200) { if (response.status !== 200) {
console.log("Status not 200 for framework!"); console.log("Status not 200 for framework!");
} }
@@ -84,7 +196,13 @@ const AppSelection = props => {
checkLogin() checkLogin()
} }
return response.json(); let resp = response.json();
let respAwaited = await resp;
if (respAwaited.success === true) {
setLastPosted(data)
}
return resp;
}) })
.then((responseJson) => { .then((responseJson) => {
if (responseJson.success === false) { if (responseJson.success === false) {
@@ -150,10 +268,6 @@ const AppSelection = props => {
} }
const onNodeSelect = (label) => { const onNodeSelect = (label) => {
// if (setDiscoveryWrapper !== undefined) {
// setDiscoveryWrapper({ id: label });
// }
if (isCloud) { if (isCloud) {
ReactGA.event({ ReactGA.event({
category: "welcome", category: "welcome",
@@ -161,130 +275,49 @@ const AppSelection = props => {
label: "", label: "",
}); });
} }
console.log("NODESELECT: ", label)
setDiscoveryData(label) setDiscoveryData(label)
setSelectionOpen(true) setSelectionOpen(true)
setNewSelectedApp({})
setDefaultSearch(label.charAt(0).toUpperCase() + (label.substring(1)).toLowerCase()) setDefaultSearch(label.charAt(0).toUpperCase() + (label.substring(1)).toLowerCase())
setNewSelectedApp(undefined)
}; };
useEffect(() => {
var tempApps = []
if (tempApps.length === 0) {
const tempApps =
[{
"description": newSelectedApp.description,
"id": newSelectedApp.objectID,
"large_image": newSelectedApp.image_url,
"name": newSelectedApp.name,
"type": discoveryData
},
//{
// // description: newSelectedApp.siem.description,
// id: newSelectedApp.siem.objectID,
// large_image: newSelectedApp.siem.image_url,
// name: newSelectedApp.siem.name,
// type: discoveryData.siem
// },{
// // description: newSelectedApp.edr.description,
// id: newSelectedApp.edr.objectID,
// large_image: newSelectedApp.edr.image_url,
// name: newSelectedApp.edr.name,
// type: discoveryData.edr
// }
]
setAppButtons(tempApps)
GetApps()
}
}, [])
useEffect(() => { // const sizing = moreButton ? 510 : 480;
if (newSelectedApp.objectID === undefined || newSelectedApp.objectID === undefined || newSelectedApp.objectID.length === 0) {
return
}
const submitNewApp = {
description: newSelectedApp.description,
id: newSelectedApp.objectID,
large_image: newSelectedApp.image_url,
name: newSelectedApp.name,
type: discoveryData
}
if (discoveryData === "CASES") {
appFramework.cases = submitNewApp
}
else if (discoveryData === "SIEM") {
appFramework.siem = submitNewApp
}
else if (discoveryData === "ERADICATION") {
appFramework.edr = submitNewApp
}
else if (discoveryData === "INTEL") {
appFramework.intel = submitNewApp
}
else if (discoveryData === "EMAIL") {
appFramework.communication = submitNewApp
}
else if (discoveryData === "NETWORK") {
appFramework.network = submitNewApp
}
else if (discoveryData === "ASSETS") {
appFramework.assets = submitNewApp
}
else if (discoveryData === "IAM") {
appFramework.iam = submitNewApp
}
setFrameworkItem(submitNewApp);
setSelectionOpen(false);
console.log("Selected app changed (effect)");
}, [newSelectedApp]);
const sizing = moreButton ? 510 : 480;
const buttonWidth = 450; const buttonWidth = 450;
const buttonMargin = 10; const buttonMargin = 10;
const bottomButtonStyle = { const bottomButtonStyle = {
borderRadius: 200, borderRadius: 200,
marginTop: moreButton ? 44 : "", marginTop: moreButton ? 44 : "",
height: 51, height: 51,
width: 510, width: isMobile ? 250 : 500,
fontSize: 16, fontSize: 16,
// background: "linear-gradient(89.83deg, #FF8444 0.13%, #F2643B 99.84%)",
background: "linear-gradient(90deg, #F86744 0%, #F34475 100%)", background: "linear-gradient(90deg, #F86744 0%, #F34475 100%)",
padding: "16px 24px", padding: "16px 24px",
// top: 20,
// margin: "auto",
textTransform: 'capitalize', textTransform: 'capitalize',
itemAlign: "center", itemAlign: "center",
// marginTop: 25
// marginLeft: "65px",
}; };
const buttonStyle = {
flex: 1,
width: 224,
padding: 25,
margin: buttonMargin,
color: "var(--White-text, #F1F1F1)",
fontWeight: 400,
fontSize: 17,
background: "rgba(33, 33, 33, 1)",
textTransform: 'capitalize',
border: "1px solid rgba(33, 33, 33, 1)",
borderRadius: 8,
marginRight: 8,
};
// console.log("appFramework",appFramework.cases.name)
return ( return (
<Collapse in={true}> <Collapse in={true}>
<div <div
style={{ style={{
minHeight: sizing, // minHeight: sizing,
maxHeight: sizing, // maxHeight: sizing,
marginTop: 10, marginTop: 10,
width: 500, width: isMobile ? 380 : 500,
marginBottom: 25,
textAlign: isMobile ? "center" : null,
}} }}
> >
{selectionOpen ? ( {selectionOpen ? (
<div <div
style={{ style={{
width: 319, width: isMobile ? 225 : 319,
height: 395, height: 395,
flexShrink: 0, flexShrink: 0,
marginLeft: 70, marginLeft: 70,
@@ -311,7 +344,7 @@ const AppSelection = props => {
style={{ style={{
flex: 1, flex: 1,
// width: 224, // width: 224,
marginLeft: discoveryData === ('ERADICATION') ? 120 : 177, marginLeft: discoveryData === ("communication") ? 112 : 200,
width: "100%", width: "100%",
marginBottom: 23, marginBottom: 23,
fontSize: 16, fontSize: 16,
@@ -374,7 +407,7 @@ const AppSelection = props => {
variant="h4" variant="h4"
style={{ style={{
marginLeft: 8, marginLeft: 8,
marginTop: 40, marginTop: isMobile ? null : 40,
marginRight: 30, marginRight: 30,
marginBottom: 0, marginBottom: 0,
}} }}
@@ -394,31 +427,60 @@ const AppSelection = props => {
> >
Select the apps you work with and we will connect them for you. Select the apps you work with and we will connect them for you.
</Typography> </Typography>
{appButtons.map((appData, index) => { <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)
const appName = appData.name appData = {
const AppImage = appData.large_image "count": 0,
const appType = appData.type "description": "",
"id": "",
"large_image": "",
"name": "",
"type": appData.type,
}
}
return ( const appName = appData.name
const AppImage = appData.large_image
const appType = appData.type
<AppSearchButtons return (
appFramework={appFramework} <AppSearchButtons
appName={appName} appFramework={appFramework}
appType = {appType} index={index}
AppImage={AppImage} totalApps={appButtons.length}
defaultSearch={defaultSearch} appName={appName}
finishedApps={finishedApps} appType={appType}
onNodeSelect={onNodeSelect} AppImage={AppImage}
discoveryData={discoveryData} defaultSearch={defaultSearch}
setDiscoveryData={setDiscoveryData} finishedApps={finishedApps}
setDefaultSearch={setDefaultSearch} onNodeSelect={onNodeSelect}
apps={apps} discoveryData={discoveryData}
/> setDiscoveryData={setDiscoveryData}
) setDefaultSearch={setDefaultSearch}
})} apps={apps}
setMoreButton={setMoreButton}
moreButton={moreButton}
/>
)
})}
</Grid>
</div> </div>
<div style={{ flexDirection: "row", }}> {!moreButton ? (
<div style={{width: "100%", marginLeft: isMobile ? 100 : 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 ? 380 : null, textAlign: isMobile ? "center" : null}}>
<Button variant="contained" type="submit" fullWidth style={bottomButtonStyle} onClick={() => { <Button variant="contained" type="submit" fullWidth style={bottomButtonStyle} onClick={() => {
navigate("/welcome?tab=3") navigate("/welcome?tab=3")
setActiveStep(2) setActiveStep(2)
+2 -27
View File
@@ -24,7 +24,7 @@ const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e52
const Appsearch = props => { const Appsearch = props => {
const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs, newSelectedApp, setNewSelectedApp, defaultSearch, showSearch, ConfiguredHits, userdata, cy, isCreatorPage, actionImageList, setActionImageList, setUserSpecialzedApp } = props const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs, newSelectedApp, setNewSelectedApp, defaultSearch, showSearch, ConfiguredHits, userdata, cy, isCreatorPage, actionImageList, setActionImageList, setUserSpecialzedApp } = props
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
const rowHandler = maxRows === undefined || maxRows === null ? 50 : maxRows const rowHandler = maxRows === undefined || maxRows === null ? 50 : maxRows
const xs = parsedXs === undefined || parsedXs === null ? 12 : parsedXs const xs = parsedXs === undefined || parsedXs === null ? 12 : parsedXs
//const theme = useTheme(); //const theme = useTheme();
@@ -112,31 +112,11 @@ const Appsearch = props => {
counted += 1 counted += 1
var parsedname = data.name.valueOf() var parsedname = data.name.valueOf()
//for (var key = 0; key < data.name.length; key++) {
// var character = data.name.charAt(key)
// if (character === character.toUpperCase()) {
// //console.log(data.name[key], data.name[key+1])
// if (data.name.charAt(key+1) !== undefined && data.name.charAt(key+1) === data.name.charAt(key+1).toUpperCase()) {
// } else {
// parsedname += " "
// }
// }
// parsedname += character
//}
parsedname = (parsedname.charAt(0).toUpperCase()+parsedname.substring(1)).replaceAll("_", " ") parsedname = (parsedname.charAt(0).toUpperCase()+parsedname.substring(1)).replaceAll("_", " ")
return ( return (
<Paper key={index} elevation={0} style={paperStyle} onMouseOver={() => { <Paper key={index} elevation={0} style={paperStyle} onMouseOver={() => {
setMouseHoverIndex(index) setMouseHoverIndex(index)
/*
ReactGA.event({
category: "app_grid_view",
action: `search_bar_click`,
label: "",
})
*/
}} onMouseOut={() => { }} onMouseOut={() => {
setMouseHoverIndex(-1) setMouseHoverIndex(-1)
}} onClick={() => { }} onClick={() => {
@@ -201,13 +181,8 @@ const Appsearch = props => {
const CustomHits = connectHits(InputHits) const CustomHits = connectHits(InputHits)
return ( return (
<div style={{width: 287, height: 295, padding: "16px 16px 267px 16px", alignItems: "center", gap: 138,}}> <div style={{width: isMobile ? null : 287, height: 295, padding: "16px 16px 267px 16px", alignItems: "center", gap: 138,}}>
<InstantSearch searchClient={searchClient} indexName="appsearch"> <InstantSearch searchClient={searchClient} indexName="appsearch">
{/* showSearch === false ? null :
<div style={{maxWidth: 450, margin: "auto", }}>
<CustomSearchBox />
</div>
*/}
<div style={{maxWidth: 450, margin: "auto", }}> <div style={{maxWidth: 450, margin: "auto", }}>
<CustomSearchBox /> <CustomSearchBox />
</div> </div>
+218 -15
View File
@@ -7,7 +7,7 @@ import countries from "../components/Countries.jsx";
import { import {
Box, Box,
Paper, Paper,
Typography, Typography,
Divider, Divider,
Button, Button,
Grid, Grid,
@@ -19,6 +19,10 @@ import {
DialogTitle, DialogTitle,
DialogContent, DialogContent,
TextField, TextField,
InputAdornment,
IconButton,
Checkbox,
Tooltip,
} from "@mui/material"; } from "@mui/material";
import { useNavigate, Link } from "react-router-dom"; import { useNavigate, Link } from "react-router-dom";
@@ -27,6 +31,9 @@ import { toast } from "react-toastify"
import { import {
Cached as CachedIcon, Cached as CachedIcon,
ContentCopy as ContentCopyIcon,
Draw as DrawIcon,
Close as CloseIcon,
} from "@mui/icons-material"; } from "@mui/icons-material";
//import { useAlert //import { useAlert
@@ -228,10 +235,44 @@ const Billing = (props) => {
}); });
}; };
const SubscriptionObject = (props) => { const sendSignatureRequest = (subscription) => {
const { globalUrl, userdata, serverside, billingInfo, stripeKey, selectedOrganization, handleGetOrg, subscription, highlight, } = props; const url = `${globalUrl}/api/v1/orgs/${selectedOrganization.id}`;
var top_text = "Base Access" fetch(url, {
body: JSON.stringify({
org_id: selectedOrganization.id,
subscription: subscription,
}),
mode: "cors",
method: "POST",
credentials: "include",
crossDomain: true,
withCredentials: true,
headers: {
"Content-Type": "application/json; charset=utf-8",
},
})
.then((response) => {
if (response.status !== 200) {
console.log("Error in response");
}
return response.json();
})
.then((responseJson) => {
console.log("Response from signature request: ", responseJson);
})
.catch((error) => {
console.log("Error: ", error);
})
}
const SubscriptionObject = (props) => {
const { globalUrl, index, userdata, serverside, billingInfo, stripeKey, selectedOrganization, handleGetOrg, subscription, highlight, } = props;
const [signatureOpen, setSignatureOpen] = React.useState(false);
const [tosChecked, setTosChecked] = React.useState(subscription.eula_signed)
var top_text = "Base Cloud Access"
if (subscription.limit === undefined && subscription.level === undefined || subscription.level === null || subscription.level === 0) { if (subscription.limit === undefined && subscription.level === undefined || subscription.level === null || subscription.level === 0) {
subscription.name = "Enterprise" subscription.name = "Enterprise"
subscription.currency_text = "$" subscription.currency_text = "$"
@@ -280,10 +321,109 @@ const Billing = (props) => {
return ( return (
<Paper style={newPaperstyle}> <Paper style={newPaperstyle}>
<Dialog
open={signatureOpen}
PaperProps={{
style: {
pointerEvents: "auto",
color: "white",
minWidth: 750,
padding: 30,
maxHeight: 700,
overflowY: "auto",
overflowX: "hidden",
zIndex: 10012,
border: theme.palette.defaultBorder,
},
}}
>
<Tooltip
title="Close window"
placement="top"
style={{ zIndex: 10011 }}
>
<IconButton
style={{ zIndex: 5000, position: "absolute", top: 34, right: 34 }}
onClick={(e) => {
e.preventDefault();
setSignatureOpen(false);
setTosChecked(false)
}}
>
<CloseIcon style={{ color: "white" }} />
</IconButton>
</Tooltip>
<DialogTitle id="form-dialog-title">Read and Accept the EULA</DialogTitle>
<DialogContent>
<TextField
rows={17}
multiline
fullWidth
InputProps={{
readOnly: true,
style: {
fontSize: 14,
color: "rgba(255, 255, 255, 0.6)",
}
}}
value={subscription.eula}
/>
<Checkbox
disabled={subscription.eula_signed}
checked={tosChecked}
onChange={(e) => {
setTosChecked(e.target.checked)
}}
inputProps={{ 'aria-label': 'primary checkbox' }}
/>
<Typography variant="body1" style={{display: "inline-block", marginLeft: 10, marginTop: 25, cursor: "pointer", }} onClick={() => {
setTosChecked(!tosChecked)
}}>
Accept
</Typography>
<Typography variant="body2" style={{display: "inline-block", marginLeft: 10, }} color="textSecondary">
By clicking the accept button, you are signing the document, electronically agreeing that it has the same legal validity and effects as a handwritten signature, and that you have the competent authority to represent and sign on behalf an entity. Need support or have questions? Contact us at support@shuffler.io.
</Typography>
<div style={{display: "flex", marginTop: 25, }}>
<Button
variant="contained"
color="primary"
style={{ marginLeft: "auto", }}
disabled={!tosChecked || subscription.eula_signed}
onClick={() => {
setSignatureOpen(false)
subscription.eula_signed = true
sendSignatureRequest(subscription)
}}
>
Submit
</Button>
</div>
</DialogContent>
</Dialog>
<div style={{display: "flex"}}> <div style={{display: "flex"}}>
<Typography variant="h6" style={{ marginTop: 10, marginBottom: 10 }}> <Typography variant="h6" style={{ marginTop: 10, marginBottom: 10, flex: 5, }}>
{top_text} {top_text}
</Typography> </Typography>
{isCloud && highlight === true ?
<Tooltip
title="Sign EULA"
placement="top"
style={{ zIndex: 10011 }}
>
<IconButton
disabled={subscription.eula_signed}
style={{ marginLeft: "auto", marginTop: 10, marginBottom: 10, flex: 1, }}
onClick={() => {
setSignatureOpen(true)
}}
>
<DrawIcon />
</IconButton>
</Tooltip>
: null}
</div> </div>
<Divider /> <Divider />
<div> <div>
@@ -320,15 +460,61 @@ const Billing = (props) => {
</a> </a>
} }
if (feature.includes("Licensed Worker: ")) { if (feature.includes("Worker License: ")) {
const fieldId = "webhook_uri_field_"+index
parsedFeature = parsedFeature =
<a <span style={{marginTop: 10, }}>
href={feature.split("Licensed Worker: ")[1]} <Typography
target="_blank" variant="body2"
style={{ textDecoration: "none", color: "#f85a3e",}} >
> Use the {feature.split("Worker License: ")[0]} Worker
Download the licensed worker </Typography>
</a> <TextField
value={feature.split("Worker License: ")[1]}
style={{
backgroundColor: theme.palette.inputColor,
borderRadius: theme.palette.borderRadius,
}}
id={fieldId}
onClick={() => {}}
InputProps={{
endAdornment:
<InputAdornment position="end">
<IconButton
aria-label="Copy webhook"
onClick={() => {
var copyText = document.getElementById(fieldId);
if (copyText !== undefined && copyText !== null) {
console.log("NAVIGATOR: ", navigator);
const clipboard = navigator.clipboard;
if (clipboard === undefined) {
toast("Can only copy over HTTPS (port 3443)");
return;
}
navigator.clipboard.writeText(copyText.value);
copyText.select();
copyText.setSelectionRange(
0,
99999
); /* For mobile devices */
/* Copy the text inside the text field */
document.execCommand("copy");
toast("Copied Webhook URL");
} else {
console.log("Couldn't find webhook URI field: ", copyText);
}
}}
edge="end"
>
<ContentCopyIcon />
</IconButton>
</InputAdornment>
}}
fullWidth
/>
</span>
} }
return ( return (
@@ -342,7 +528,7 @@ const Billing = (props) => {
: null} : null}
</ul> </ul>
</div> </div>
{(highlight === true && subscription.name === "Pay as you go" && subscription.limit <= 10000) || subscription.name.includes("Scale") ? {(highlight === true && (subscription.name === "Pay as you go" && subscription.limit <= 10000) || subscription.name === "Open Source") ?
<span> <span>
<Typography variant="body2" color="textSecondary" style={{ marginTop: 20, marginBottom: 10 }}> <Typography variant="body2" color="textSecondary" style={{ marginTop: 20, marginBottom: 10 }}>
{subscription.name.includes("Scale") ? {subscription.name.includes("Scale") ?
@@ -351,6 +537,18 @@ const Billing = (props) => {
"You are not subscribed to any plan and are using the free plan with max 10,000 app runs per month. Upgrade to deactivate this limit." "You are not subscribed to any plan and are using the free plan with max 10,000 app runs per month. Upgrade to deactivate this limit."
} }
</Typography> </Typography>
{/*isCloud ?
<Button
variant="contained"
color="primary"
style={{ marginTop: 20, marginBottom: 10, }}
onClick={() => {
window.open("https://checkout.stripe.com/c/pay/ppage_1O8UttDzMUgUjxHSBh3krC6Y#fidkdWxOYHwnPyd1blpxYHZxWkBhfWJOY3RoVEJdXDBPSW9hR3RxcG1GcjU1R01nbE5PQUcnKSdobGF2Jz9%2BJ2JwbGEnPydjY2M3MjA0NyhnYzcyKDFkMTQoPWFmPSgwYDI9MTA0NGAyZDNhPWZhNmcnKSdocGxhJz8nNWYwMjNnZGMoZGdgPCgxPDxhKD1kNjUoMjZmPTcxYzw9NjBjYzZmMGRnJykndmxhJz8nZGYxYWZhY2MoZ2FjNygxPDA2KDxhZ2MoZjY1PWM8NGcxYzUxMjJkYTRmJ3gpJ2dgcWR2Jz9eWCknaWR8anBxUXx1YCc%2FJ3Zsa2JpYFpscWBoJyknd2BjYHd3YHdKd2xibGsnPydtcXF1PyoqaWpmZGltanZxPzY1NTcnKSdpamZkaWAnP2twaWl4JSUl")
}}
>
Add Payment Method
</Button>
: null*/}
<Button <Button
variant="contained" variant="contained"
color="primary" color="primary"
@@ -363,7 +561,7 @@ const Billing = (props) => {
} }
}} }}
> >
Upgrade Now Upgrade now
</Button> </Button>
</span> </span>
: null} : null}
@@ -692,6 +890,7 @@ const Billing = (props) => {
<div style={{display: "flex", maxWidth: 768, minWidth: 768, }}> <div style={{display: "flex", maxWidth: 768, minWidth: 768, }}>
{isCloud && billingInfo.subscription !== undefined && billingInfo.subscription !== null ? {isCloud && billingInfo.subscription !== undefined && billingInfo.subscription !== null ?
<SubscriptionObject <SubscriptionObject
index={0}
globalUrl={globalUrl} globalUrl={globalUrl}
userdata={userdata} userdata={userdata}
serverside={serverside} serverside={serverside}
@@ -704,6 +903,7 @@ const Billing = (props) => {
: !isCloud ? : !isCloud ?
<span style={{display: "flex", }}> <span style={{display: "flex", }}>
<SubscriptionObject <SubscriptionObject
index={0}
globalUrl={globalUrl} globalUrl={globalUrl}
userdata={userdata} userdata={userdata}
serverside={false} serverside={false}
@@ -722,6 +922,7 @@ const Billing = (props) => {
highlight={true} highlight={true}
/> />
<SubscriptionObject <SubscriptionObject
index={1}
globalUrl={globalUrl} globalUrl={globalUrl}
userdata={userdata} userdata={userdata}
serverside={false} serverside={false}
@@ -751,6 +952,7 @@ const Billing = (props) => {
.map((sub, index) => { .map((sub, index) => {
return ( return (
<SubscriptionObject <SubscriptionObject
index={index+1}
globalUrl={globalUrl} globalUrl={globalUrl}
userdata={userdata} userdata={userdata}
serverside={serverside} serverside={serverside}
@@ -758,6 +960,7 @@ const Billing = (props) => {
stripeKey={stripeKey} stripeKey={stripeKey}
selectedOrganization={selectedOrganization} selectedOrganization={selectedOrganization}
subscription={sub} subscription={sub}
highlight={true}
/> />
) )
}) })
@@ -1,6 +1,7 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { useInterval } from "react-powerhooks"; import { useInterval } from "react-powerhooks";
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import theme from "../theme.jsx";
import { import {
InputAdornment, InputAdornment,
@@ -41,7 +42,6 @@ import aa from 'search-insights'
const ConfigureWorkflow = (props) => { const ConfigureWorkflow = (props) => {
const { const {
apps, apps,
theme,
isCloud, isCloud,
workflow, workflow,
userdata, userdata,
@@ -1235,12 +1235,8 @@ const ConfigureWorkflow = (props) => {
) )
} }
const topColor = "#f86a3e, #fc3922"
return ( return (
<div> <div>
{setConfigureWorkflowModalOpen !== undefined ?
<div style={{height: 75, width: "100%", background: `linear-gradient(to right, ${topColor}`, position: "relative",}} />
: null}
<div style={{margin: setConfigureWorkflowModalOpen !== undefined ? "0px 50px 0px 50px" : "35px 0px 0px", maxHeight: 475, }}> <div style={{margin: setConfigureWorkflowModalOpen !== undefined ? "0px 50px 0px 50px" : "35px 0px 0px", maxHeight: 475, }}>
+2 -2
View File
@@ -39,7 +39,7 @@ import {
const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240") const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240")
const CreatorGrid = props => { const CreatorGrid = props => {
const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs } = props const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs, isHeader } = props
const rowHandler = maxRows === undefined || maxRows === null ? 50 : maxRows const rowHandler = maxRows === undefined || maxRows === null ? 50 : maxRows
const xs = parsedXs === undefined || parsedXs === null ? isMobile ? 6 : 4 : parsedXs const xs = parsedXs === undefined || parsedXs === null ? isMobile ? 6 : 4 : parsedXs
//const [apps, setApps] = React.useState([]); //const [apps, setApps] = React.useState([]);
@@ -165,7 +165,7 @@ const CreatorGrid = props => {
return ( return (
<Zoom key={index} in={true} style={{}}> <Zoom key={index} in={true} style={{}}>
<Grid item xs={xs} style={{ padding: "12px 10px 12px 10px", }}> <Grid item xs={xs} style={{ padding: isHeader ? null : "12px 10px 12px 10px", }}>
<Card style={{border: "1px solid rgba(255,255,255,0.3)", minHeight: 177, maxHeight: 177,}}> <Card style={{border: "1px solid rgba(255,255,255,0.3)", minHeight: 177, maxHeight: 177,}}>
<a href={creatorUrl} rel="noopener noreferrer" target={isCloud ? "" : "_blank"} style={{textDecoration: "none", color: "inherit",}}> <a href={creatorUrl} rel="noopener noreferrer" target={isCloud ? "" : "_blank"} style={{textDecoration: "none", color: "inherit",}}>
<CardActionArea style={{padding: "5px 10px 5px 10px", minHeight: 177, maxHeight: 177,}}> <CardActionArea style={{padding: "5px 10px 5px 10px", minHeight: 177, maxHeight: 177,}}>
+9 -5
View File
@@ -4,6 +4,7 @@ import ReactGA from 'react-ga4';
import 'react-alice-carousel/lib/alice-carousel.css'; import 'react-alice-carousel/lib/alice-carousel.css';
import TrendingFlatIcon from '@mui/icons-material/TrendingFlat'; import TrendingFlatIcon from '@mui/icons-material/TrendingFlat';
import theme from '../theme.jsx'; import theme from '../theme.jsx';
import {isMobile} from "react-device-detect";
import CheckBoxSharpIcon from '@mui/icons-material/CheckBoxSharp'; import CheckBoxSharpIcon from '@mui/icons-material/CheckBoxSharp';
import { findSpecificApp } from "../components/AppFramework.jsx" import { findSpecificApp } from "../components/AppFramework.jsx"
import { import {
@@ -40,7 +41,7 @@ import { useNavigate, Link } from "react-router-dom";
import WorkflowTemplatePopup from "../components/WorkflowTemplatePopup.jsx"; import WorkflowTemplatePopup from "../components/WorkflowTemplatePopup.jsx";
const ExploreWorkflow = (props) => { const ExploreWorkflow = (props) => {
const { userdata, globalUrl, appFramework } = props const { userdata, globalUrl, appFramework, isLoggedIn, } = props
const [activeUsecases, setActiveUsecases] = useState(0); const [activeUsecases, setActiveUsecases] = useState(0);
const [modalOpen, setModalOpen] = React.useState(false); const [modalOpen, setModalOpen] = React.useState(false);
const [suggestedUsecases, setSuggestedUsecases] = useState([]) const [suggestedUsecases, setSuggestedUsecases] = useState([])
@@ -291,7 +292,7 @@ const ExploreWorkflow = (props) => {
return ( return (
<div style={{ marginTop: 0, margin: "auto", minHeight: sizing, maxHeight: sizing, }}> <div style={{ marginTop: 0, margin: "auto", minHeight: sizing, maxHeight: sizing, }}>
{modalView} {modalView}
<Typography variant="h4" style={{ marginLeft: 8, marginTop: 40, marginRight: 30, marginBottom: 0, }} color="rgba(241, 241, 241, 1)"> <Typography variant="h4" style={{ marginLeft: 8, marginTop: isMobile ? null : 40, marginRight: 30, marginBottom: 0, }} color="rgba(241, 241, 241, 1)">
Start using workflows Start using workflows
</Typography> </Typography>
<Typography variant="body2" style={{ marginLeft: 8, marginTop: 10, marginRight: 30, marginBottom: 40, }} color="rgba(158, 158, 158, 1)"> <Typography variant="body2" style={{ marginLeft: 8, marginTop: 10, marginRight: 30, marginBottom: 40, }} color="rgba(158, 158, 158, 1)">
@@ -300,7 +301,7 @@ const ExploreWorkflow = (props) => {
<div style={{ marginTop: 0, }}> <div style={{ marginTop: 0, }}>
<div className="thumbs" style={{ display: "flex" }}> <div className="thumbs" style={{ display: "flex" }}>
<div style={{ minWidth: 554, maxWidth: 554, borderRadius: theme.palette.borderRadius, }}> <div style={{ minWidth: isMobile ? 300 : 554, maxWidth: isMobile ? 300 : 554, borderRadius: theme.palette.borderRadius, }}>
<Grid item xs={11} style={{}}> <Grid item xs={11} style={{}}>
{suggestedUsecases.length === 0 && usecasesSet ? {suggestedUsecases.length === 0 && usecasesSet ?
<Typography variant="h6" style={{ marginTop: 30, marginBottom: 50, }} color="rgba(158, 158, 158, 1)"> <Typography variant="h6" style={{ marginTop: 30, marginBottom: 50, }} color="rgba(158, 158, 158, 1)">
@@ -330,7 +331,10 @@ const ExploreWorkflow = (props) => {
return ( return (
<WorkflowTemplatePopup <WorkflowTemplatePopup
isLoggedIn={isLoggedIn}
userdata={userdata} userdata={userdata}
appFramework={appFramework}
globalUrl={globalUrl} globalUrl={globalUrl}
img1={image1} img1={image1}
srcapp={srcapp} srcapp={srcapp}
@@ -351,7 +355,7 @@ const ExploreWorkflow = (props) => {
fullWidth style={{ fullWidth style={{
borderRadius: 200, borderRadius: 200,
height: 51, height: 51,
width: 464, width: isMobile? null : 464,
fontSize: 16, fontSize: 16,
padding: "16px 24px", padding: "16px 24px",
margin: "auto", margin: "auto",
@@ -368,7 +372,7 @@ const ExploreWorkflow = (props) => {
</Typography> </Typography>
</div> </div>
<Typography variant="body2" style={{ fontSize: 16, marginTop: 24 }} color="rgba(158, 158, 158, 1)"> <Typography variant="body2" style={{ fontSize: 16, marginTop: 24 }} color="rgba(158, 158, 158, 1)">
<Link style={{ color: "#f86a3e", marginLeft: 145 }} to="/usecases" className="btn btn-primary"> <Link style={{ color: "#f86a3e", marginLeft: isMobile ? null : 145 }} to="/usecases" className="btn btn-primary">
Explore usecases Explore usecases
</Link> </Link>
</Typography> </Typography>
+11 -10
View File
@@ -16,7 +16,7 @@ const Newsletter = (props) => {
const [email, setEmail] = useState(""); const [email, setEmail] = useState("");
const [msg, setMsg] = useState(""); const [msg, setMsg] = useState("");
const [buttonActive, setButtonActive] = useState(true); const [buttonActive, setButtonActive] = useState(true);
const buttonStyle = {minWidth: 300, borderRadius: 30, height: 60, width: 140, margin: isMobile ? "15px auto 15px auto" : "20px 20px 20px 10px", fontSize: 18,} const buttonStyle = {width: 176, marginTop:16, height: 45, paddingLeft: 24, paddingRight: 24, paddingTop: 16, paddingBottom: 16, background: 'linear-gradient(90deg, #FF8444 0%, #FB47A0 100%)', borderRadius: 200, justifyContent: 'center', alignItems: 'center', gap: 10, display: 'inline-flex', fontSize: 16, fontStyle: "normal", fontWeight: 600}
const newsletterSignup = (inemail) => { const newsletterSignup = (inemail) => {
if (inemail.length < 4) { if (inemail.length < 4) {
@@ -52,20 +52,20 @@ const Newsletter = (props) => {
} }
return ( return (
<div style={{margin: "auto", color: "white", textAlign: "center",}}> <div style={{margin: "auto", height:isMobile? 375 :286, color: "white", textAlign: isMobile? "cenetr":"Left", marginTop:24, marginLeft: 24, marginRight: 24}}>
<Typography variant="h4" style={{marginTop: 35,}}> <Typography variant="h4" style={{ fontWeight:700, textTransform: 'capitalize', fontSize:32 }}>
Security Automation Newsletter Security authentication<br/> newsletter
</Typography> </Typography>
<Typography variant="h6" style={{color: "#7d7f82", marginTop: 20, }}> <Typography variant="h6" style={{color: "#7d7f82", marginTop: 8, fontSize: 16,}}>
Defensive security is 99% noise. Join us to sift through it. Defensive security is 99% noise, join us to sift through it!<br/> Enter your email below:
</Typography> </Typography>
<div style={{}}> <div style={{ justifyContent:"center",}}>
<TextField <TextField
style={{minWidth: isMobile ? "90%" : 450, backgroundColor: theme.palette.inputColor, marginTop: 20, borderRadius: 10, }} style={{minWidth: isMobile ? "90%" : 450, height: 45, backgroundColor: theme.palette.inputColor, marginTop: 8, borderRadius: 10, }}
InputProps={{ InputProps={{
style:{ style:{
borderRadius: 10, borderRadius: 10,
height: 60, height: 45,
color: "white", color: "white",
}, },
}} }}
@@ -79,7 +79,6 @@ const Newsletter = (props) => {
margin="normal" margin="normal"
variant="outlined" variant="outlined"
/> />
</div>
<Button <Button
variant="contained" variant="contained"
color="primary" color="primary"
@@ -96,6 +95,8 @@ const Newsletter = (props) => {
> >
Sign up Sign up
</Button> </Button>
</div>
<div/> <div/>
{msg} {msg}
</div> </div>
+39
View File
@@ -18,11 +18,49 @@ const Priorities = (props) => {
const { globalUrl, userdata, serverside, billingInfo, stripeKey, checkLogin, setAdminTab, setCurTab, } = props; const { globalUrl, userdata, serverside, billingInfo, stripeKey, checkLogin, setAdminTab, setCurTab, } = props;
const [showDismissed, setShowDismissed] = React.useState(false); const [showDismissed, setShowDismissed] = React.useState(false);
const [showRead, setShowRead] = React.useState(false); const [showRead, setShowRead] = React.useState(false);
const [appFramework, setAppFramework] = React.useState({});
useEffect(() => {
getFramework()
}, [])
if (userdata === undefined || userdata === null) { if (userdata === undefined || userdata === null) {
return return
} }
const getFramework = () => {
fetch(globalUrl + "/api/v1/apps/frameworkConfiguration", {
method: "GET",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
credentials: "include",
})
.then((response) => {
if (response.status !== 200) {
console.log("Status not 200 for framework!");
}
return response.json();
})
.then((responseJson) => {
if (responseJson.success === false) {
setAppFramework({})
if (responseJson.reason !== undefined) {
//toast("Failed loading: " + responseJson.reason)
} else {
//toast("Failed to load framework for your org.")
}
} else {
setAppFramework(responseJson)
}
})
.catch((error) => {
console.log("err in framework: ", error.toString());
})
}
return ( return (
<div style={{maxWidth: 1000, }}> <div style={{maxWidth: 1000, }}>
<h2 style={{ display: "inline" }}>Suggestions</h2> <h2 style={{ display: "inline" }}>Suggestions</h2>
@@ -62,6 +100,7 @@ const Priorities = (props) => {
checkLogin={checkLogin} checkLogin={checkLogin}
setAdminTab={setAdminTab} setAdminTab={setAdminTab}
setCurTab={setCurTab} setCurTab={setCurTab}
appFramework={appFramework}
/> />
) )
}) })
+22 -9
View File
@@ -27,31 +27,42 @@ const Priority = (props) => {
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
let navigate = useNavigate(); let navigate = useNavigate();
var realigned = false var realignedSrc = false
var realignedDst = false
let newdescription = priority.description let newdescription = priority.description
const descsplit = priority.description.split("&") const descsplit = priority.description.split("&")
if (appFramework !== undefined && descsplit.length === 5 && priority.description.includes(":default")) { if (appFramework !== undefined && descsplit.length === 5 && priority.description.includes(":default")) {
console.log("descsplit: ", descsplit) console.log("descsplit: ", descsplit)
if (descsplit[1] === "") { if (descsplit[1] === "") {
const item = findSpecificApp(appFramework, descsplit[0]) const item = findSpecificApp(appFramework, descsplit[0])
console.log("item: ", item)
if (item !== null) { if (item !== null) {
descsplit[1] = item.large_image descsplit[1] = item.large_image
console.log("DESCSPLIT name: ", descsplit[0])
if (descsplit[0].includes(":default")) {
realignedSrc = true
}
descsplit[0] = descsplit[0].split(":")[0] descsplit[0] = descsplit[0].split(":")[0]
} }
realigned = true
} }
if (descsplit[3] === "") { if (descsplit[3] === "") {
const item = findSpecificApp(appFramework, descsplit[2]) const item = findSpecificApp(appFramework, descsplit[2])
console.log("item: ", item) //console.log("item: ", item)
realignedDst = true
if (item !== null) { if (item !== null) {
descsplit[3] = item.large_image descsplit[3] = item.large_image
if (descsplit[2].includes(":default")) {
realignedDst = true
}
descsplit[2] = descsplit[2].split(":")[0] descsplit[2] = descsplit[2].split(":")[0]
} }
realigned = true
} }
newdescription = descsplit.join("&") newdescription = descsplit.join("&")
@@ -101,6 +112,8 @@ const Priority = (props) => {
} }
const srcSize = realignedSrc ? 35 : 30
const dstSize = realignedDst ? 35 : 30
return ( return (
<div style={{border: priority.active === false ? "1px solid #000000" : priority.severity === 1 ? "1px solid #f85a3e" : "1px solid rgba(255,255,255,0.3)", borderRadius: theme.palette.borderRadius, marginTop: 10, marginBottom: 10, padding: 15, textAlign: "center", minHeight: isCloud ? 70 : 100, maxHeight: isCloud ? 70 : 100, textAlign: "left", backgroundColor: theme.palette.surfaceColor, display: "flex", }}> <div style={{border: priority.active === false ? "1px solid #000000" : priority.severity === 1 ? "1px solid #f85a3e" : "1px solid rgba(255,255,255,0.3)", borderRadius: theme.palette.borderRadius, marginTop: 10, marginBottom: 10, padding: 15, textAlign: "center", minHeight: isCloud ? 70 : 100, maxHeight: isCloud ? 70 : 100, textAlign: "left", backgroundColor: theme.palette.surfaceColor, display: "flex", }}>
<div style={{flex: 2, overflow: "hidden",}}> <div style={{flex: 2, overflow: "hidden",}}>
@@ -112,7 +125,7 @@ const Priority = (props) => {
</span> </span>
{priority.type === "usecase" && priority.description.includes("&") ? {priority.type === "usecase" && priority.description.includes("&") ?
<span style={{display: "flex", marginTop: 10, }}> <span style={{display: "flex", marginTop: 10, }}>
<img src={newdescription.split("&")[1]} alt={priority.name} style={{height: "auto", width: 30, marginRight: realigned ? -10 : 10, borderRadius: theme.palette.borderRadius, marginTop: realigned ? 5 : 0 }} /> <img src={newdescription.split("&")[1]} alt={priority.name} style={{height: srcSize, width: srcSize, marginRight: realignedSrc ? isCloud ? 0 : -10 : 10, borderRadius: theme.palette.borderRadius-3, marginTop: realignedSrc ? 5 : 0 }} />
<Typography variant="body2" color="textSecondary" style={{marginTop: 3, }}> <Typography variant="body2" color="textSecondary" style={{marginTop: 3, }}>
{newdescription.split("&")[0]} {newdescription.split("&")[0]}
</Typography> </Typography>
@@ -120,7 +133,7 @@ const Priority = (props) => {
{newdescription.split("&").length > 3 ? {newdescription.split("&").length > 3 ?
<span style={{display: "flex", }}> <span style={{display: "flex", }}>
<ArrowForwardIcon style={{marginLeft: 15, marginRight: 15, }}/> <ArrowForwardIcon style={{marginLeft: 15, marginRight: 15, }}/>
<img src={newdescription.split("&")[3]} alt={priority.name+"2"} style={{height: "auto", width: 30, marginRight: realigned ? -10 : 10, borderRadius: theme.palette.borderRadius, marginTop: realigned ? 5 : 0 }} /> <img src={newdescription.split("&")[3]} alt={priority.name+"2"} style={{height: dstSize, width: dstSize, marginRight: realignedDst ? -5 : 10, borderRadius: theme.palette.borderRadius-3, marginTop: realignedDst ? 5 : 0 }} />
<Typography variant="body2" color="textSecondary" style={{marginTop: 3}}> <Typography variant="body2" color="textSecondary" style={{marginTop: 3}}>
{newdescription.split("&")[2]} {newdescription.split("&")[2]}
</Typography> </Typography>
+6 -23
View File
@@ -4,7 +4,7 @@ import Checkbox from "@mui/material/Checkbox";
import AliceCarousel from "react-alice-carousel"; import AliceCarousel from "react-alice-carousel";
import "react-alice-carousel/lib/alice-carousel.css"; import "react-alice-carousel/lib/alice-carousel.css";
import {isMobile} from "react-device-detect";
import SearchIcon from "@mui/icons-material/Search"; import SearchIcon from "@mui/icons-material/Search";
import EmailIcon from "@mui/icons-material/Email"; import EmailIcon from "@mui/icons-material/Email";
import NewReleasesIcon from "@mui/icons-material/NewReleases"; import NewReleasesIcon from "@mui/icons-material/NewReleases";
@@ -79,6 +79,7 @@ const WelcomeForm = (props) => {
selectionOpen, selectionOpen,
setSelectionOpen, setSelectionOpen,
checkLogin, checkLogin,
isLoggedIn,
} = props; } = props;
const [moreButton, setMoreButton] = useState(false); const [moreButton, setMoreButton] = useState(false);
const ref = useRef() const ref = useRef()
@@ -449,19 +450,6 @@ const WelcomeForm = (props) => {
} }
}; };
const handleSkip = () => {
setclickdiff(240);
if (!isStepOptional(activeStep)) {
throw new Error("You can't skip a step that isn't optional.");
}
setActiveStep((prevActiveStep) => prevActiveStep + 1);
setSkipped((prevSkipped) => {
const newSkipped = new Set(prevSkipped.values());
newSkipped.add(activeStep);
return newSkipped;
});
};
const handleReset = () => { const handleReset = () => {
setActiveStep(0); setActiveStep(0);
}; };
@@ -652,6 +640,7 @@ const WelcomeForm = (props) => {
); );
case 1: case 1:
return ( return (
appFramework === undefined || appFramework === null ? null :
<AppSelection <AppSelection
globalUrl={globalUrl} globalUrl={globalUrl}
userdata={userdata} userdata={userdata}
@@ -667,13 +656,14 @@ const WelcomeForm = (props) => {
case 2: case 2:
return ( return (
<Collapse in={true}> <Collapse in={true}>
<div style={{ marginTop: 0, maxWidth: 700, minWidth: 700, margin: "auto", minHeight: sizing, maxHeight: sizing, }}> <div style={{ marginTop: 0, maxWidth: isMobile ? 300 : 700, minWidth: isMobile ? 300 : 700, textAlign: isMobile ? "center" : null, margin: "auto", minHeight: sizing, maxHeight: sizing, }}>
<div style={{ marginTop: 0, }}> <div style={{ marginTop: 0, }}>
<div className="thumbs" style={{ display: "flex" }}> <div className="thumbs" style={{ display: "flex" }}>
<div style={{ minWidth: 554, maxWidth: 554, borderRadius: theme.palette.borderRadius, }}> <div style={{ minWidth: isMobile ? 300 : 554, maxWidth: isMobile ? 300 : 554, borderRadius: theme.palette.borderRadius, }}>
<ExploreWorkflow <ExploreWorkflow
globalUrl={globalUrl} globalUrl={globalUrl}
isLoggedIn={isLoggedIn}
userdata={userdata} userdata={userdata}
appFramework={appFramework} appFramework={appFramework}
/> />
@@ -692,13 +682,6 @@ const WelcomeForm = (props) => {
const extraHeight = isCloud ? -7 : 0; const extraHeight = isCloud ? -7 : 0;
return ( return (
<div style={{}}> <div style={{}}>
{/*selectionOpen ?
<WorkflowSearch
defaultSearch={defaultSearch}
newSelectedApp={newSelectedApp}
setNewSelectedApp={setNewSelectedApp}
/>
: null*/}
<div> <div>
{activeStep === steps.length ? ( {activeStep === steps.length ? (
<div paddingTop="20px"> <div paddingTop="20px">
@@ -3,6 +3,8 @@ import React, { useState, useEffect } from "react";
import { toast } from "react-toastify" import { toast } from "react-toastify"
import theme from '../theme.jsx'; import theme from '../theme.jsx';
import { useNavigate, Link, useParams } from "react-router-dom"; import { useNavigate, Link, useParams } from "react-router-dom";
import AppSearchButtons from "../components/AppSearchButtons.jsx";
import { isMobile } from "react-device-detect";
import { import {
Button, Button,
Typography, Typography,
@@ -26,7 +28,7 @@ import WorkflowTemplatePopup2 from "./WorkflowTemplatePopup.jsx";
import ConfigureWorkflow from "../components/ConfigureWorkflow.jsx"; import ConfigureWorkflow from "../components/ConfigureWorkflow.jsx";
const WorkflowTemplatePopup = (props) => { const WorkflowTemplatePopup = (props) => {
const { userdata, globalUrl, img1, srcapp, img2, dstapp, title, description, visualOnly, apps } = props; const { userdata, appFramework, globalUrl, img1, srcapp, img2, dstapp, title, description, visualOnly, apps, isLoggedIn, isHomePage } = props;
const [isActive, setIsActive] = useState(false); const [isActive, setIsActive] = useState(false);
const [isHovered, setIsHovered] = useState(false); const [isHovered, setIsHovered] = useState(false);
@@ -36,14 +38,21 @@ const WorkflowTemplatePopup = (props) => {
const [workflow, setWorkflow] = useState({}); const [workflow, setWorkflow] = useState({});
const [appAuthentication, setAppAuthentication] = React.useState(undefined); const [appAuthentication, setAppAuthentication] = React.useState(undefined);
const [missingSource, setMissingSource] = React.useState(undefined)
const [missingDestination, setMissingDestination] = React.useState(undefined);
useEffect(() => {
console.log("Source & Dest check:", missingSource, missingDestination)
}, [missingSource, missingDestination])
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
let navigate = useNavigate(); let navigate = useNavigate();
const imagestyleWrapper = { const imagestyleWrapper = {
height: 40, height: 40,
width: 40, width: 40,
borderRadius: 40, borderRadius: 40,
border: "1px solid rgba(255,255,255,0.3)", border: isHomePage ? null : "1px solid rgba(255,255,255,0.3)",
overflow: "hidden", overflow: "hidden",
display: "flex", display: "flex",
} }
@@ -52,7 +61,7 @@ const WorkflowTemplatePopup = (props) => {
height: 40, height: 40,
width: 40, width: 40,
borderRadius: 40, borderRadius: 40,
border: "1px solid rgba(255,255,255,0.3)", border: isHomePage ? null : "1px solid rgba(255,255,255,0.3)",
overflow: "hidden", overflow: "hidden",
display: "flex", display: "flex",
} }
@@ -61,13 +70,13 @@ const WorkflowTemplatePopup = (props) => {
height: 40, height: 40,
width: 40, width: 40,
borderRadius: 40, borderRadius: 40,
border: "1px solid rgba(255,255,255,0.3)", border: isHomePage ? null : "1px solid rgba(255,255,255,0.3)",
overflow: "hidden", overflow: "hidden",
} }
const imagestyleDefault = { const imagestyleDefault = {
display: "block", display: "block",
marginLeft: 11, marginLeft: 12,
marginTop: 11, marginTop: 11,
height: 35, height: 35,
width: "auto", width: "auto",
@@ -108,8 +117,10 @@ const WorkflowTemplatePopup = (props) => {
} }
const loadAppAuth = () => { const loadAppAuth = () => {
if (userdata === undefined || userdata === null) { // Check if it exists, and has keys
if (userdata === undefined || userdata === null || Object.keys(userdata).length === 0) {
setErrorMessage("You need to be logged in to try usecases. Redirecting in 5 seconds...") setErrorMessage("You need to be logged in to try usecases. Redirecting in 5 seconds...")
// Send the user to the login screen after 3 seconds // Send the user to the login screen after 3 seconds
setTimeout(() => { setTimeout(() => {
// Make it cancel if the state modalOpen changes // Make it cancel if the state modalOpen changes
@@ -162,6 +173,7 @@ const WorkflowTemplatePopup = (props) => {
}); });
} }
const getGeneratedWorkflow = () => { const getGeneratedWorkflow = () => {
// POST // POST
// https://shuffler.io/api/v1/workflows/merge // https://shuffler.io/api/v1/workflows/merge
@@ -174,12 +186,24 @@ const WorkflowTemplatePopup = (props) => {
if (srcapp.includes(":default") || dstapp.includes(":default")) { if (srcapp.includes(":default") || dstapp.includes(":default")) {
toast("You need to select both a source and destination app before generating this workflow.") toast("You need to select both a source and destination app before generating this workflow.")
if (srcapp.includes(":default")) {
setMissingSource({
"type": srcapp.split(":")[0],
})
}
if (dstapp.includes(":default")) {
setMissingDestination({
"type": dstapp.split(":")[0],
})
}
return return
} }
setWorkflowLoading(true) setWorkflowLoading(true)
// FIXME: Remove hardcoding here after testing, and user srcapp/dstapp
const newsrcapp = srcapp const newsrcapp = srcapp
const newdstapp = dstapp const newdstapp = dstapp
@@ -207,15 +231,21 @@ const WorkflowTemplatePopup = (props) => {
}) })
.then((response) => { .then((response) => {
if (response.status !== 200) { if (response.status !== 200) {
console.log("Status not 200 for framework!"); //console.log("Status not 200 for framework!");
} }
setWorkflowLoading(false) setWorkflowLoading(false)
return response.json(); return response.json();
}) })
.then((responseJson) => { .then((responseJson) => {
if (responseJson.id !== undefined && responseJson.id !== null && responseJson.id !== "" && responseJson.name !== undefined && responseJson.name !== null && responseJson.name !== "") {
console.log("Success in workflow template (prebuilt): ", responseJson);
setWorkflow(responseJson)
return
}
if (responseJson.success === false) { if (responseJson.success === false) {
console.log("Error in workflow template: ", responseJson.error); //console.log("Error in workflow template: ", responseJson.error);
setErrorMessage("Failed to generate workflow for these tools - the Shuffle team has been notified. Click out of this window to continue. Contact support@shuffler.io for further assistance.") setErrorMessage("Failed to generate workflow for these tools - the Shuffle team has been notified. Click out of this window to continue. Contact support@shuffler.io for further assistance.")
@@ -269,9 +299,9 @@ const WorkflowTemplatePopup = (props) => {
style: { style: {
backgroundColor: "black", backgroundColor: "black",
color: "white", color: "white",
minWidth: 700, minWidth: isHomePage ? null : isMobile ? 300 : 700,
maxWidth: 700, maxWidth: isHomePage ? null : isMobile ? 300 : 700,
paddingTop: 75, paddingTop: isMobile ? null : 75,
itemAlign: "center", itemAlign: "center",
}, },
}} }}
@@ -290,8 +320,8 @@ const WorkflowTemplatePopup = (props) => {
> >
<CloseIcon /> <CloseIcon />
</IconButton> </IconButton>
<DialogContent style={{marginTop: 0, marginLeft: 75, maxWidth: 470, }}> <DialogContent style={{marginTop: 0, marginLeft: isHomePage ? null : isMobile ? null : 75, maxWidth: 470, }}>
<Typography variant="h4"> <Typography variant="h4" style={{ fontSize: isMobile ? 20 : null}}>
<b>Configure Workflow</b> <b>Configure Workflow</b>
</Typography> </Typography>
<Typography variant="body2" color="textSecondary" style={{marginTop: 25, }}> <Typography variant="body2" color="textSecondary" style={{marginTop: 25, }}>
@@ -322,6 +352,41 @@ const WorkflowTemplatePopup = (props) => {
</Typography> </Typography>
</div> </div>
} }
{isLoggedIn && (missingSource !== undefined || missingDestination !== undefined) ?
<Typography variant="body1" style={{marginTop: 75, }}>
{"The following app category is required generate this workflow: "}
</Typography>
: null}
{(missingSource !== undefined) ?
<div style={{}}>
<AppSearchButtons
globalUrl={globalUrl}
appFramework={appFramework}
appType={missingSource.type}
appImage={missingSource.image}
setMissing={setMissingSource}
/>
</div>
: null}
{(missingDestination !== undefined) ?
<div style={{}}>
<AppSearchButtons
globalUrl={globalUrl}
appFramework={appFramework}
appType={missingDestination.type}
appImage={missingDestination.image}
setMissing={setMissingDestination}
/>
</div>
: null}
<ConfigureWorkflow <ConfigureWorkflow
userdata={userdata} userdata={userdata}
theme={theme} theme={theme}
@@ -352,26 +417,26 @@ const WorkflowTemplatePopup = (props) => {
if (title.length > maxlength) { if (title.length > maxlength) {
parsedTitle = title.substring(0, maxlength) + "..." parsedTitle = title.substring(0, maxlength) + "..."
} }
console.log("isHomePage", isHomePage)
parsedTitle = parsedTitle.replaceAll("_", " ") parsedTitle = parsedTitle.replaceAll("_", " ")
const parsedDescription = description !== undefined && description !== null ? description.replaceAll("_", " ") : "" const parsedDescription = description !== undefined && description !== null ? description.replaceAll("_", " ") : ""
return ( return (
<div style={{ display: "flex", maxWidth: isCloud ? 470 : 450, minWidth: isCloud ? 470 : 450, height: 78, borderRadius: 8 }}> <div style={{ display: "flex", maxWidth: isCloud ? 470 : isMobile? null: 450, minWidth: isCloud ? 470 : isMobile? null: 450, height: 78, borderRadius: 8, }}>
<ModalView /> <ModalView />
<div <div
// variant={isActive === 1 ? "contained" : "outlined"} // variant={isActive === 1 ? "contained" : "outlined"}
color="secondary" color="secondary"
disabled={visualOnly === true} disabled={visualOnly === true}
style={{ style={{
margin: 4, margin: isHomePage ? isMobile ? null : 4 : 4 ,
width: "100%", width: "100%",
borderRadius: 8, borderRadius: 8,
textTransform: "none", textTransform: "none",
backgroundColor: theme.palette.inputColor, backgroundColor: isHomePage ? null : theme.palette.inputColor,
border: isActive ? errorMessage !== "" ? "1px solid red" : `2px solid ${theme.palette.green}` : isHovered ? "1px solid #f85a3e" : "1px solid rgba(33, 33, 33, 1)", border: isHomePage ? null : isActive ? errorMessage !== "" ? "1px solid red" : `2px solid ${theme.palette.green}` : isHovered ? "1px solid #f85a3e" : "1px solid rgba(33, 33, 33, 1)",
cursor: isActive ? errorMessage !== "" ? "not-allowed" : "pointer" : "pointer", cursor: isActive ? errorMessage !== "" ? "not-allowed" : "pointer" : "pointer",
padding: "10px 20px 10px 20px", padding: "10px 20px 10px 20px",
position: "relative", position: "relative",
@@ -434,10 +499,10 @@ const WorkflowTemplatePopup = (props) => {
} }
</div> </div>
<div style={{ flex: 3, marginLeft: 20, }}> <div style={{ flex: 3, marginLeft: 20, }}>
<Typography variant="body1" style={{ marginTop: parsedDescription.length === 0 ? 10 : 0, }} color="rgba(241, 241, 241, 1)"> <Typography variant="body1" style={{ marginTop: parsedDescription.length === 0 ? 10 : 0, fontSize: isMobile ? 13 : 16,fontWeight: isHomePage? 600 : null,textTransform: 'capitalize', color: isHomePage ? "var(--White-text, #F1F1F1)" :"rgba(241, 241, 241, 1)"}} >
{parsedTitle} {parsedTitle}
</Typography> </Typography>
<Typography variant="body2" color="textSecondary" style={{ marginTop: 0, marginRight: 0, maxHeight: 16, overflow: "hidden",}} color="rgba(158, 158, 158, 1)"> <Typography variant="body2" color="textSecondary" style={{ fontSize: isMobile ? 10: 16, fontWeight: isHomePage ? 400 : null, textTransform: 'capitalize', marginTop: 0, overflow: "hidden",}} color="rgba(158, 158, 158, 1)">
{parsedDescription} {parsedDescription}
</Typography> </Typography>
</div> </div>
+48 -5
View File
@@ -980,6 +980,22 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user
leads.push("customer") leads.push("customer")
} }
if (responseJson.lead_info.old_customer) {
leads.push("old customer")
}
if (responseJson.lead_info.tech_partner) {
leads.push("tech partner")
}
if (responseJson.lead_info.creator) {
leads.push("creator")
}
if (responseJson.lead_info.opensource) {
leads.push("open source")
}
if (responseJson.lead_info.demo_done) { if (responseJson.lead_info.demo_done) {
leads.push("demo done") leads.push("demo done")
} }
@@ -2412,7 +2428,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user
renderValue={(selected) => selected.join(', ')} renderValue={(selected) => selected.join(', ')}
MenuProps={MenuProps} MenuProps={MenuProps}
> >
{["contacted", "lead", "pov", "demo done", "customer", "student", "internal"].map((name) => ( {["contacted", "lead", "demo done", "pov", "customer", "open source", "student", "internal", "old customer", "creator", "tech partner"].map((name) => (
<MenuItem key={name} value={name}> <MenuItem key={name} value={name}>
<Checkbox checked={selectedStatus.indexOf(name) > -1} /> <Checkbox checked={selectedStatus.indexOf(name) > -1} />
<ListItemText primary={name} /> <ListItemText primary={name} />
@@ -2528,7 +2544,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user
/> />
<Tab <Tab
label=<span> label=<span>
Licensing (Beta) Licensing
</span> </span>
/> />
<Tab <Tab
@@ -3000,7 +3016,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user
curTab === 1 ? ( curTab === 1 ? (
<div> <div>
<div style={{ marginTop: 20, marginBottom: 20 }}> <div style={{ marginTop: 20, marginBottom: 20 }}>
<h2 style={{ display: "inline" }}>User management</h2> <h2 style={{ display: "inline" }}>User Management</h2>
<span style={{ marginLeft: 25 }}> <span style={{ marginLeft: 25 }}>
Add, edit, block or change passwords.{" "} Add, edit, block or change passwords.{" "}
<a <a
@@ -3080,7 +3096,11 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user
) : null} ) : null}
<ListItemText <ListItemText
primary="Actions" primary="Actions"
style={{ minWidth: 180, maxWidth: 180 }} style={{ minWidth: 100, maxWidth: 100, }}
/>
<ListItemText
primary="Last Login"
style={{ minWidth: 150, maxWidth: 150, }}
/> />
</ListItem> </ListItem>
{users === undefined || users === null {users === undefined || users === null
@@ -3091,6 +3111,24 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user
bgColor = "#1f2023"; bgColor = "#1f2023";
} }
const timeNow = new Date().getTime();
// Get the highest timestamp in data.login_info
var lastLogin = "N/A"
if (data.login_info !== undefined && data.login_info !== null) {
var loginInfo = 0
for (var i = 0; i < data.login_info.length; i++) {
if (data.login_info[i].timestamp > loginInfo) {
loginInfo = data.login_info[i].timestamp
}
}
if (loginInfo > 0) {
console.log("SETTING LAST LOGIN: " + loginInfo)
lastLogin = new Date(loginInfo * 1000).toISOString().slice(0, 10) + " (" + data.login_info.length + ")"
}
}
return ( return (
<ListItem key={index} style={{ backgroundColor: bgColor }}> <ListItem key={index} style={{ backgroundColor: bgColor }}>
<ListItemText <ListItemText
@@ -3242,7 +3280,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user
} }
/> />
) : null} ) : null}
<ListItemText style={{ display: "flex" }}> <ListItemText style={{ display: "flex", minWidth: 100, maxWidth: 100, }}>
<IconButton <IconButton
onClick={() => { onClick={() => {
setSelectedUserModalOpen(true); setSelectedUserModalOpen(true);
@@ -3300,6 +3338,11 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user
New apikey New apikey
</Button>*/} </Button>*/}
</ListItemText> </ListItemText>
<ListItemText
style={{ minWidth: 150, maxWidth: 150, }}
primary={lastLogin}
><span/>
</ListItemText>
</ListItem> </ListItem>
); );
})} })}
+27 -23
View File
@@ -977,14 +977,17 @@ const AngularWorkflow = (defaultprops) => {
tmpView = execution_id; tmpView = execution_id;
} }
console.log("TMPVIEW: ", tmpView);
// Compare with currently selected item // Compare with currently selected item
if (tmpView !== undefined && tmpView !== null && tmpView.length > 0) { if (tmpView !== undefined && tmpView !== null && tmpView.length > 0) {
// Don't clean up if it's already open // Don't clean up if it's already open
if (executionModalOpen === true) { if (executionModalOpen === true) {
console.log("Execution modal already open, not cleaning up")
return return
} }
const execution = responseJson.find((data) => data.execution_id === tmpView); const execution = responseJson.executions.find((data) => data.execution_id === tmpView);
setExecutionModalOpen(true) setExecutionModalOpen(true)
if (execution !== null && execution !== undefined) { if (execution !== null && execution !== undefined) {
@@ -16678,28 +16681,29 @@ const AngularWorkflow = (defaultprops) => {
> >
<CloseIcon /> <CloseIcon />
</IconButton> </IconButton>
<ConfigureWorkflow <div style={{height: 75, width: "100%", background: `linear-gradient(to right, #f86a3e, #fc3922)`, position: "relative",}} />
userdata={userdata} <div style={{ padding: "50px 0px 50px 0px", }}>
alert={alert} <ConfigureWorkflow
theme={theme} workflow={workflow}
setAuthenticationType={setAuthenticationType} userdata={userdata}
globalUrl={globalUrl} globalUrl={globalUrl}
workflow={workflow} apps={apps}
setSelectedAction={setSelectedAction} setAuthenticationType={setAuthenticationType}
setSelectedApp={setSelectedApp} setSelectedAction={setSelectedAction}
setAuthenticationModalOpen={setAuthenticationModalOpen} setSelectedApp={setSelectedApp}
appAuthentication={appAuthentication} setAuthenticationModalOpen={setAuthenticationModalOpen}
selectedAction={selectedAction} appAuthentication={appAuthentication}
apps={apps} selectedAction={selectedAction}
setConfigureWorkflowModalOpen={setConfigureWorkflowModalOpen} setConfigureWorkflowModalOpen={setConfigureWorkflowModalOpen}
saveWorkflow={saveWorkflow} saveWorkflow={saveWorkflow}
newWebhook={newWebhook} newWebhook={newWebhook}
submitSchedule={submitSchedule} submitSchedule={submitSchedule}
referenceUrl={referenceUrl} referenceUrl={referenceUrl}
workflowExecutions={workflowExecutions} workflowExecutions={workflowExecutions}
getWorkflowExecution={getWorkflowExecution} getWorkflowExecution={getWorkflowExecution}
isCloud={isCloud} isCloud={isCloud}
/> />
</div>
</Dialog> </Dialog>
) : null; ) : null;
+6 -6
View File
@@ -929,9 +929,9 @@ const AppCreator = (defaultprops) => {
} }
} }
if (path === "/files/{file_id}/content") { if (path === "/files/{file_id}/content") {
//console.log("FILE DOWNLOAD Method: ", path, method, methodvalue) //console.log("FILE DOWNLOAD Method: ", path, method, methodvalue)
} }
// Typescript? I think not ;) // Typescript? I think not ;)
@@ -2137,7 +2137,7 @@ const AppCreator = (defaultprops) => {
} }
if (queryitem.name.toLowerCase() == "file_id") { if (queryitem.name.toLowerCase() == "file_id") {
item.queries[querykey].name = "fileid" item.queries[querykey].name = "fileid"
continue; continue;
//skipped = true //skipped = true
//break //break
@@ -2525,8 +2525,8 @@ const AppCreator = (defaultprops) => {
for (let authkey in extraAuth) { for (let authkey in extraAuth) {
const curauth = extraAuth[authkey]; const curauth = extraAuth[authkey];
if (curauth.name.toLowerCase() == "url") { if (curauth.name.length === 0 || curauth.name.toLowerCase() == "url") {
toast("Can't add extra auth with Name URL"); toast("Can't add extra auth with empty name or Name URL");
setAppBuilding(false); setAppBuilding(false);
return; return;
} }
-1
View File
@@ -1660,7 +1660,6 @@ const Apps = (props) => {
refine(event.currentTarget.value) refine(event.currentTarget.value)
}} }}
limit={5}
/> />
{/*isSearchStalled ? 'My search is stalled' : ''*/} {/*isSearchStalled ? 'My search is stalled' : ''*/}
</form> </form>
+67 -46
View File
@@ -95,7 +95,9 @@ const useStyles = makeStyles({
const UsecaseListComponent = ({userdata, keys, isCloud, globalUrl, frameworkData, isLoggedIn, workflows, setWorkflows}) => { const UsecaseListComponent = (props) => {
const { keys, userdata, isCloud, globalUrl, frameworkData, isLoggedIn, workflows, setWorkflows, } = props
const [expandedIndex, setExpandedIndex] = useState(-1); const [expandedIndex, setExpandedIndex] = useState(-1);
const [expandedItem, setExpandedItem] = useState(-1); const [expandedItem, setExpandedItem] = useState(-1);
const [inputUsecase, setInputUsecase] = useState({}); const [inputUsecase, setInputUsecase] = useState({});
@@ -165,12 +167,9 @@ const UsecaseListComponent = ({userdata, keys, isCloud, globalUrl, frameworkData
const parseUsecase = (subcase) => { const parseUsecase = (subcase) => {
console.log("parseUsecase: ", subcase) //console.log("parseUsecase: ", subcase)
const srcdata = findSpecificApp(frameworkData, subcase.type) const srcdata = findSpecificApp(frameworkData, subcase.type)
const dstdata = findSpecificApp(frameworkData, subcase.last) const dstdata = findSpecificApp(frameworkData, subcase.last)
console.log("srcdata: ", srcdata)
console.log("dstdata: ", dstdata)
if (srcdata !== undefined && srcdata !== null) { if (srcdata !== undefined && srcdata !== null) {
subcase.srcimg = srcdata.large_image subcase.srcimg = srcdata.large_image
@@ -185,13 +184,14 @@ const UsecaseListComponent = ({userdata, keys, isCloud, globalUrl, frameworkData
return subcase return subcase
} }
const getUsecase = (subcase, index, subindex) => { const getUsecase = (subcase, index, subindex) => {
subcase = parseUsecase(subcase) subcase = parseUsecase(subcase)
// Timeout 50ms to delay it slightly // Timeout 50ms to delay it slightly
setTimeout(() => { //setTimeout(() => {
setInputUsecase(subcase) // setInputUsecase(subcase)
}, 50) //}, 50)
fetch(`${globalUrl}/api/v1/workflows/usecases/${escape(subcase.name.replaceAll(" ", "_"))}`, { fetch(`${globalUrl}/api/v1/workflows/usecases/${escape(subcase.name.replaceAll(" ", "_"))}`, {
method: "GET", method: "GET",
@@ -209,12 +209,13 @@ const UsecaseListComponent = ({userdata, keys, isCloud, globalUrl, frameworkData
return response.json(); return response.json();
}) })
.then((responseJson) => { .then((responseJson) => {
console.log("In responseJson for usecase: ", responseJson)
var parsedUsecase = responseJson var parsedUsecase = responseJson
if (responseJson.success === false) { if (responseJson.success === false) {
parsedUsecase = subcase parsedUsecase = subcase
} else { } else {
console.log("FOUND: ", JSON.parse(JSON.stringify(responseJson)))
parsedUsecase = responseJson parsedUsecase = responseJson
parsedUsecase.srcimg = subcase.srcimg parsedUsecase.srcimg = subcase.srcimg
@@ -229,19 +230,21 @@ const UsecaseListComponent = ({userdata, keys, isCloud, globalUrl, frameworkData
setExpandedItem(subindex) setExpandedItem(subindex)
setTimeout(() => { setTimeout(() => {
//console.log("Scroll!")
const found = document.getElementById("selected_box"); const found = document.getElementById("selected_box");
if (found !== undefined && found !== null) { if (found !== undefined && found !== null) {
//console.log("FOUND!!") //console.log("FOUND!!")
found.scrollTo({
top: 100, //found.scrollTo({
behavior: "smooth", // top: 100,
}) // behavior: "smooth",
//})
} else {
//console.log("NOT FOUND!!")
} }
setFirstLoad(true) setFirstLoad(true)
setSelectedWorkflows([]) setSelectedWorkflows([])
}, 100); }, 100)
}) })
.catch((error) => { .catch((error) => {
//toast(error.toString()); //toast(error.toString());
@@ -412,7 +415,7 @@ const UsecaseListComponent = ({userdata, keys, isCloud, globalUrl, frameworkData
const fixedName = subcase.name.toLowerCase().replace("_", " ") const fixedName = subcase.name.toLowerCase().replace("_", " ")
return ( return (
<Grid id={fixedName} item xs={selectedItem ? 12 : 4} key={subindex} style={{minHeight: 110,}} onClick={() => { <Grid id={fixedName} item xs={selectedItem ? 12 : 4} key={subindex} style={{minHeight: 110,}} onClick={() => {
//setSelectedWorkflows([]) //setSelectedWorkflows([])
if (selectedItem) { if (selectedItem) {
@@ -784,7 +787,6 @@ const UsecaseListComponent = ({userdata, keys, isCloud, globalUrl, frameworkData
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette.borderRadius,
}} }}
onChange={(event, newValue) => { onChange={(event, newValue) => {
console.log("CLICK: ", newValue)
//handleWorkflowSelectionUpdate({ target: { value: newValue} }) //handleWorkflowSelectionUpdate({ target: { value: newValue} })
//setSelectedWorkflows= //setSelectedWorkflows=
//var newvalue = [] //var newvalue = []
@@ -801,7 +803,6 @@ const UsecaseListComponent = ({userdata, keys, isCloud, globalUrl, frameworkData
if (workflows[key]["usecase_ids"] !== undefined && workflows[key]["usecase_ids"] !== null && workflows[key]["usecase_ids"].includes(subcase.name)) { if (workflows[key]["usecase_ids"] !== undefined && workflows[key]["usecase_ids"] !== null && workflows[key]["usecase_ids"].includes(subcase.name)) {
const filtered = workflows[key]["usecase_ids"].filter(data => data !== subcase.name) const filtered = workflows[key]["usecase_ids"].filter(data => data !== subcase.name)
if (filtered !== undefined && filtered !== null) { if (filtered !== undefined && filtered !== null) {
//console.log("Removing: ", workflows[key].name, workflows[key])
workflows[key]["usecase_ids"] = filtered workflows[key]["usecase_ids"] = filtered
setWorkflow(workflows[key]) setWorkflow(workflows[key])
@@ -813,19 +814,16 @@ const UsecaseListComponent = ({userdata, keys, isCloud, globalUrl, frameworkData
if (workflows[key]["usecase_ids"] === undefined || workflows[key]["usecase_ids"] === null) { if (workflows[key]["usecase_ids"] === undefined || workflows[key]["usecase_ids"] === null) {
workflows[key]["usecase_ids"] = [subcase.name] workflows[key]["usecase_ids"] = [subcase.name]
console.log("Setting: ", workflows[key].name)
setWorkflow(workflows[key]) setWorkflow(workflows[key])
} else if (!workflows[key]["usecase_ids"].includes(subcase.name)) { } else if (!workflows[key]["usecase_ids"].includes(subcase.name)) {
workflows[key]["usecase_ids"].push(subcase.name) workflows[key]["usecase_ids"].push(subcase.name)
console.log("Adding: ", workflows[key].name)
setWorkflow(workflows[key]) setWorkflow(workflows[key])
} }
} }
setWorkflows(workflows) setWorkflows(workflows)
console.log("New: ", newValue)
setSelectedWorkflows(newValue) setSelectedWorkflows(newValue)
//setUpdate(Math.random()) //setUpdate(Math.random())
}} }}
@@ -887,19 +885,23 @@ const UsecaseListComponent = ({userdata, keys, isCloud, globalUrl, frameworkData
> >
Try it out: Try it out:
</Typography> </Typography>
<WorkflowTemplatePopup {frameworkData !== undefined && frameworkData !== null && Object.keys(frameworkData).length > 0 ?
userdata={userdata} <WorkflowTemplatePopup
isLoggedIn={isLoggedIn}
appFramework={frameworkData}
userdata={userdata}
globalUrl={globalUrl} globalUrl={globalUrl}
img1={inputUsecase.srcimg} img1={inputUsecase.srcimg}
srcapp={inputUsecase.srcapp} srcapp={inputUsecase.srcapp}
img2={inputUsecase.dstimg} img2={inputUsecase.dstimg}
dstapp={inputUsecase.dstapp} dstapp={inputUsecase.dstapp}
title={inputUsecase.name} title={inputUsecase.name}
description={inputUsecase.description} description={inputUsecase.description}
apps={apps} apps={apps}
/> />
: null}
</span> </span>
{/* {/*
@@ -1125,7 +1127,7 @@ const RadialChart = ({keys, setSelectedCategory}) => {
// This is the start of a dashboard that can be used. // This is the start of a dashboard that can be used.
// What data do we fill in here? Idk // What data do we fill in here? Idk
const Dashboard = (props) => { const Dashboard = (props) => {
const { globalUrl, isLoggedIn } = props; const { globalUrl, isLoggedIn, userdata } = props;
//const alert = useAlert(); //const alert = useAlert();
const [bigChartData, setBgChartData] = useState("data1"); const [bigChartData, setBgChartData] = useState("data1");
const [dayAmount, setDayAmount] = useState(7); const [dayAmount, setDayAmount] = useState(7);
@@ -1176,7 +1178,7 @@ const Dashboard = (props) => {
const foundQuery2 = params["selected_object"] const foundQuery2 = params["selected_object"]
if (foundQuery2 !== null && foundQuery2 !== undefined) { if (foundQuery2 !== null && foundQuery2 !== undefined) {
console.log("Got selected_object: ", foundQuery2) //console.log("Got selected_object: ", foundQuery2)
const queryName = foundQuery2.toLowerCase().replaceAll("_", " ") const queryName = foundQuery2.toLowerCase().replaceAll("_", " ")
// Waiting a bit for it to render // Waiting a bit for it to render
@@ -1184,6 +1186,15 @@ const Dashboard = (props) => {
const foundItem = document.getElementById(queryName) const foundItem = document.getElementById(queryName)
if (foundItem !== undefined && foundItem !== null) { if (foundItem !== undefined && foundItem !== null) {
foundItem.click() foundItem.click()
// Scroll to it
setTimeout(() => {
foundItem.scrollIntoView({
behavior: "smooth",
block: "center",
inline: "center"
})
}, 100)
} else { } else {
//console.log("Couldn't find item with name ", queryName) //console.log("Couldn't find item with name ", queryName)
} }
@@ -1194,12 +1205,12 @@ const Dashboard = (props) => {
useEffect(() => { useEffect(() => {
if (usecases.length > 0) { if (usecases.length > 0) {
console.log(usecases) //console.log(usecases)
checkSelectedParams() checkSelectedParams()
} }
}, [usecases]) }, [usecases])
const getFramework = () => { const getFramework = () => {
fetch(globalUrl + "/api/v1/apps/frameworkConfiguration", { fetch(globalUrl + "/api/v1/apps/frameworkConfiguration", {
method: "GET", method: "GET",
headers: { headers: {
@@ -1216,20 +1227,29 @@ const Dashboard = (props) => {
return response.json(); return response.json();
}) })
.then((responseJson) => { .then((responseJson) => {
if (responseJson.success === false) { if (responseJson.success === false) {
if (responseJson.reason !== undefined) { const preparedData = {
//toast("Failed loading: " + responseJson.reason) "siem": findSpecificApp({}, "SIEM"),
} else { "communication": findSpecificApp({}, "COMMUNICATION"),
//toast("Failed to load framework for your org.") "assets": findSpecificApp({}, "ASSETS"),
} "cases": findSpecificApp({}, "CASES"),
} else { "network": findSpecificApp({}, "NETWORK"),
setFrameworkData(responseJson) "intel": findSpecificApp({}, "INTEL"),
"edr": findSpecificApp({}, "EDR"),
"iam": findSpecificApp({}, "IAM"),
"email": findSpecificApp({}, "EMAIL"),
} }
})
console.log("Got error for framework! ", preparedData)
setFrameworkData(preparedData)
} else {
setFrameworkData(responseJson)
}
})
.catch((error) => { .catch((error) => {
toast(error.toString()); toast(error.toString());
}) })
} }
const getAvailableWorkflows = () => { const getAvailableWorkflows = () => {
fetch(globalUrl + "/api/v1/workflows", { fetch(globalUrl + "/api/v1/workflows", {
@@ -1659,6 +1679,7 @@ const Dashboard = (props) => {
: null} : null}
<UsecaseListComponent <UsecaseListComponent
userdata={userdata}
isLoggedIn={isLoggedIn} isLoggedIn={isLoggedIn}
frameworkData={frameworkData} frameworkData={frameworkData}
keys={selectedUsecases} keys={selectedUsecases}
+19 -18
View File
@@ -21,20 +21,13 @@ import {
} from "@mui/icons-material"; } from "@mui/icons-material";
const bodyDivStyle = {
margin: "auto",
maxWidth: 1024,
scrollX: "hidden",
overflowX: "hidden",
}
// Should be different if logged in :| // Should be different if logged in :|
const Search = (props) => { const Search = (props) => {
const { globalUrl, isLoaded, serverside, userdata, hidemargins, } = props; const { globalUrl, isLoaded, serverside, userdata, hidemargins, isHeader} = props;
let navigate = useNavigate(); let navigate = useNavigate();
const [curTab, setCurTab] = useState(0); const [curTab, setCurTab] = useState(0);
const iconStyle = { marginRight: 10 }; const iconStyle = { marginRight: isHeader ? null : 10 };
useEffect(() => { useEffect(() => {
if (serverside !== true && window.location.search !== undefined && window.location.search !== null) { if (serverside !== true && window.location.search !== undefined && window.location.search !== null) {
@@ -58,15 +51,23 @@ const Search = (props) => {
return null return null
} }
const bodyDivStyle = {
margin: "auto",
maxWidth: 1024,
scrollX: "hidden",
overflowX: "hidden",
justifyContent: isHeader ? "center" : null,
}
const boxStyle = { const boxStyle = {
color: "white", color: "white",
flex: "1", flex: "1",
marginLeft: 10, marginLeft: isHeader ? null : 10,
marginRight: 10, marginRight: isHeader ? null : 10,
paddingLeft: 30, paddingLeft: isHeader ? null : 30,
paddingRight: 30, paddingRight: isHeader ? null : 30,
paddingBottom: 30, paddingBottom: isHeader ? null : 30,
paddingTop: hidemargins === true ? 0 : 30, paddingTop: hidemargins === true ? 0 : isHeader ? null : 30,
display: "flex", display: "flex",
flexDirection: "column", flexDirection: "column",
overflowX: "hidden", overflowX: "hidden",
@@ -121,7 +122,7 @@ const Search = (props) => {
<div style={{paddingBottom: hidemargins === true ? 0 : 100, color: "white", }}> <div style={{paddingBottom: hidemargins === true ? 0 : 100, color: "white", }}>
<div style={boxStyle}> <div style={boxStyle}>
<Tabs <Tabs
style={{width: 610, margin: "auto", marginTop: hidemargins === true ? 0 : 25, }} style={{width: isHeader ? 765 : 610, margin: isHeader? null :"auto", marginTop: hidemargins === true ? 0 : isHeader ? null : 25, }}
value={curTab} value={curTab}
indicatorColor="primary" indicatorColor="primary"
textColor="secondary" textColor="secondary"
@@ -152,7 +153,7 @@ const Search = (props) => {
/> />
</Tabs> </Tabs>
{curTab === 0 ? {curTab === 0 ?
<AppGrid maxRows={3} showSuggestion={true} globalUrl={globalUrl} isMobile={isMobile} userdata={userdata} /> <AppGrid maxRows={3} isHeader={true} showSuggestion={true} globalUrl={globalUrl} isMobile={isMobile} userdata={userdata} />
: :
curTab === 1 ? curTab === 1 ?
window.location.pathname === "/search" ? window.location.pathname === "/search" ?
@@ -164,7 +165,7 @@ const Search = (props) => {
<DocsGrid maxRows={6} parsedXs={12} showSuggestion={true} globalUrl={globalUrl} isMobile={isMobile} userdata={userdata} /> <DocsGrid maxRows={6} parsedXs={12} showSuggestion={true} globalUrl={globalUrl} isMobile={isMobile} userdata={userdata} />
: :
curTab === 3 ? curTab === 3 ?
<CreatorGrid parsedXs={4} showSuggestion={true} globalUrl={globalUrl} isMobile={isMobile} userdata={userdata} /> <CreatorGrid parsedXs={4} isHeader={true} showSuggestion={true} globalUrl={globalUrl} isMobile={isMobile} userdata={userdata} />
: :
null} null}
</div> </div>
+23 -74
View File
@@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react';
import ReactGA from 'react-ga4'; import ReactGA from 'react-ga4';
import WelcomeForm2 from "../components/WelcomeForm2.jsx"; import WelcomeForm2 from "../components/WelcomeForm2.jsx";
import AppFramework from "../components/AppFramework.jsx"; import AppFramework from "../components/AppFramework.jsx";
import {isMobile} from "react-device-detect";
import { import {
ArrorForwardIos as ArrowForwardIosIcon, ArrorForwardIos as ArrowForwardIosIcon,
} from '@mui/icons-material'; } from '@mui/icons-material';
@@ -47,10 +47,7 @@ const Welcome = (props) => {
} }
}, [activeStep]) }, [activeStep])
const isCloud = const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
window.location.host === "localhost:3002" ||
window.location.host === "shuffler.io";
const [steps, setSteps] = useState([ const [steps, setSteps] = useState([
"Help us get to know you", "Help us get to know you",
"Find your Apps", "Find your Apps",
@@ -272,22 +269,22 @@ const Welcome = (props) => {
getApps() getApps()
getAvailableWorkflows() getAvailableWorkflows()
if ( if (window.location.search !== undefined && window.location.search !== null) {
window.location.search !== undefined &&
window.location.search !== null
) {
const urlSearchParams = new URLSearchParams(window.location.search); const urlSearchParams = new URLSearchParams(window.location.search);
const params = Object.fromEntries(urlSearchParams.entries()); const params = Object.fromEntries(urlSearchParams.entries());
const foundTab = params["tab"]; const foundTab = params["tab"];
// Make foundtab into a number
console.log("foundTab: ", foundTab, activeStep+1)
if (foundTab !== null && foundTab !== undefined && !isNaN(foundTab) && foundTab >= 1 && foundTab <= 3) { if (foundTab !== null && foundTab !== undefined && !isNaN(foundTab) && foundTab >= 1 && foundTab <= 3) {
console.log("SETTING TAB TO: ", foundTab)
setShowWelcome(true) setShowWelcome(true)
if (foundTab === 3 || foundTab === "3") { if (foundTab === 3 || foundTab === "3") {
handleSetSearch(usecaseButtons[0].name, usecaseButtons[0].usecase) handleSetSearch(usecaseButtons[0].name, usecaseButtons[0].usecase)
} }
setActiveStep(foundTab-1) setActiveStep(foundTab-1)
} else {
navigate(`/welcome?tab=2`)
} }
} }
}, []) }, [])
@@ -300,8 +297,8 @@ const Welcome = (props) => {
flex: 1, flex: 1,
padding: 0, padding: 0,
textAlign: "center", textAlign: "center",
maxWidth: 275, maxWidth: isMobile ? null : 275,
minWidth: 275, minWidth: isMobile ? null : 275,
backgroundColor: theme.palette.surfaceColor, backgroundColor: theme.palette.surfaceColor,
color: "white", color: "white",
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette.borderRadius,
@@ -326,7 +323,7 @@ const Welcome = (props) => {
const buttonStyle = { const buttonStyle = {
borderRadius: 200, borderRadius: 200,
height: 51, height: 51,
width: 464, width: isMobile ? 300 : 464,
fontSize: 16, fontSize: 16,
// background: "linear-gradient(89.83deg, #FF8444 0.13%, #F2643B 99.84%)", // background: "linear-gradient(89.83deg, #FF8444 0.13%, #F2643B 99.84%)",
background: "linear-gradient(90deg, #F86744 0%, #F34475 100%)", background: "linear-gradient(90deg, #F86744 0%, #F34475 100%)",
@@ -334,7 +331,7 @@ const Welcome = (props) => {
top: 105, top: 105,
margin: "auto", margin: "auto",
itemAlign: "center", itemAlign: "center",
marginLeft: "65px", marginLeft: isMobile? null : "65px",
} }
const defaultImage = "/images/experienced.png" const defaultImage = "/images/experienced.png"
@@ -382,7 +379,7 @@ const Welcome = (props) => {
})} })}
</Stepper> </Stepper>
</div> */} </div> */}
<Grid container spacing={1} style={{ padding: 0, maxWidth: 500, minWidth: 500, marginRight : "auto" , marginLeft: "auto" }}> <Grid container spacing={1} style={{ padding: isMobile ? 10 : 0, maxWidth: isMobile ? null : 500, minWidth: isMobile ? null : 500, marginRight : "auto" , marginLeft: "auto" }}>
<Grid item xs={window.location.href.includes("tab=2") ? 6 : 12}> <Grid item xs={window.location.href.includes("tab=2") ? 6 : 12}>
<div> <div>
{/* {/*
@@ -394,6 +391,7 @@ const Welcome = (props) => {
/> />
*/} */}
<WelcomeForm2 <WelcomeForm2
isLoggedIn={isLoggedIn}
checkLogin={checkLogin} checkLogin={checkLogin}
userdata={userdata} userdata={userdata}
globalUrl={globalUrl} globalUrl={globalUrl}
@@ -417,49 +415,11 @@ const Welcome = (props) => {
/> />
</div> </div>
</Grid> </Grid>
{/* {frameworkData === undefined || window.location.href.includes("tab=1") || window.location.href.includes("tab=3") ? null :
<div style={{marginTop: 25, }}>
<Typography variant="h6" style={{textAlign: "center", marginBottom: 25, }}>
App Framework
</Typography>
<Fade>
<AppFramework
inputUsecase={inputUsecase}
frameworkData={frameworkData}
setFrameworkData={setFrameworkData}
selectedOption={"Draw"}
showOptions={false}
isLoaded={true}
isLoggedIn={true}
globalUrl={globalUrl}
size={0.78}
color={theme.palette.backgroundColor}
discoveryWrapper={discoveryWrapper}
setDiscoveryWrapper={setDiscoveryWrapper}
apps={apps}
inputUsecases={usecases}
setInputUsecases={setUsecases}
/>
</Fade>
</div>
} */}
</Grid> </Grid>
</div> </div>
: :
<Fade in={true}> <Fade in={true}>
<div style={{maxWidth: 590, margin: "auto", marginTop: 50, }}> <div style={{maxWidth: isMobile ? null : 590, margin: "auto", marginTop: 50, }}>
{/* <div style={{display:"flex"}}>
<ArrowBackIosIcon style={{color: "#9E9E9E", width: "16px",}} onClick={() => {
navigate("/workflows")
}}/>
<Typography variant="h8" style={{color: "#9E9E9E",textAlign: "center", marginBottom: 20, paddingRight: "366px"}} onClick={() => {
navigate("/workflows")
}}>
Back
</Typography>
</div> */}
<Typography variant="h4" style={{color: "#F1F1F1", marginTop: 50, fontSize: 32}}> <Typography variant="h4" style={{color: "#F1F1F1", marginTop: 50, fontSize: 32}}>
Help us get to know you Help us get to know you
</Typography> </Typography>
@@ -469,7 +429,7 @@ const Welcome = (props) => {
{/* <Typography variant="body1" style={{color: "#9E9E9E", marginTop: 12 ,marginBottom: 50, fontSize: 16}}> {/* <Typography variant="body1" style={{color: "#9E9E9E", marginTop: 12 ,marginBottom: 50, fontSize: 16}}>
We will use this information to personalize your automation We will use this information to personalize your automation
</Typography> */} </Typography> */}
<div style={{display: "flex", marginTop: 70, width: 700, margin: "auto",}}> <div style={{display: isMobile ? null : "flex", marginTop: 70, width: isMobile ? 280 : 700, margin: "auto",}}>
<div style={{border: "2px solid #806BFF", borderRadius: theme.palette.borderRadius, }}> <div style={{border: "2px solid #806BFF", borderRadius: theme.palette.borderRadius, }}>
<Card style={paperObject} onClick={() => { <Card style={paperObject} onClick={() => {
if (isCloud) { if (isCloud) {
@@ -482,6 +442,7 @@ const Welcome = (props) => {
//setActiveStep(1) //setActiveStep(1)
} }
navigate("/welcome?tab=2")
setShowWelcome(true) setShowWelcome(true)
}}> }}>
<CardActionArea style={actionObject}> <CardActionArea style={actionObject}>
@@ -490,12 +451,13 @@ const Welcome = (props) => {
New to Shuffle New to Shuffle
</Typography> </Typography>
<Typography variant="body1" style={{marginTop: 10, color: "rgba(255,255,255,0.8)"}}> <Typography variant="body1" style={{marginTop: 10, color: "rgba(255,255,255,0.8)"}}>
Let us guide you for an easier experience Let us guide you for an easier experience
</Typography> </Typography>
</CardActionArea> </CardActionArea>
</Card> </Card>
</div> </div>
<div style={{marginLeft: 25, marginRight: 25, }}> <div style={{marginLeft: 25, marginRight: 25,
marginTop: isMobile ? 30 : null, }}>
{/* <Typography style={{marginTop: 200, }}> {/* <Typography style={{marginTop: 200, }}>
OR OR
</Typography> */} </Typography> */}
@@ -512,7 +474,7 @@ const Welcome = (props) => {
navigate("/workflows?message=Skipped intro") navigate("/workflows?message=Skipped intro")
}}> }}>
<CardActionArea style={actionObject}> <CardActionArea style={actionObject}>
<img src={experienced_image} style={{padding: experienced_image === defaultImage ? 2 : 10, objectFit: "scale-down", minHeight: experienced_image === defaultImage ? 40 : 70, maxHeight: experienced_image === defaultImage ? 40 : 70, bordeRadius: theme.palette.borderRadius, marginBottom: experienced_image === defaultImage ? 24 : 2 }} /> <img src={experienced_image} style={{padding: experienced_image === defaultImage ? 2 : 10, objectFit: "scale-down", minHeight: experienced_image === defaultImage ? 40 : 70, maxHeight: experienced_image === defaultImage ? 40 : 70, bordeRadius: theme.palette.borderRadius*2, marginBottom: experienced_image === defaultImage ? 24 : 2 }} />
<Typography variant="h4" style={{color: "#F1F1F1"}}> <Typography variant="h4" style={{color: "#F1F1F1"}}>
Experienced Experienced
</Typography> </Typography>
@@ -524,10 +486,11 @@ const Welcome = (props) => {
</div> </div>
<div style={{ flexDirection: "row", }}> <div style={{ flexDirection: "row", textAlign: isMobile ? "center" : null, margin: isMobile ? "auto" : null }}>
<Button variant="contained" type="submit" fullWidth style={buttonStyle} onClick={() => { <Button variant="contained" type="submit" fullWidth style={buttonStyle} onClick={() => {
navigate(`/welcome?tab=2`) navigate(`/welcome?tab=2`)
setActiveStep(1) setActiveStep(1)
setShowWelcome(true)
ReactGA.event({ ReactGA.event({
category: "welcome", category: "welcome",
action: "click_welcome_continue", action: "click_welcome_continue",
@@ -537,20 +500,6 @@ const Welcome = (props) => {
Continue Continue
</Button> </Button>
</div> </div>
{/* <div style={{margin: "auto", borderRadius: theme.palette.borderRadius, marginTop: 50, width: 200, overflow: "wrap", padding: 25, cursor: "pointer", }} onClick={() => {
if (window.drift !== undefined) {
window.drift.api.startInteraction({ interactionId: 340045 })
} else {
console.log("Couldn't find drift in window.drift and not .drift-open-chat with querySelector: ", window.drift)
}
}}>
<Typography variant="body1" style={{margin: "auto", textAlign: "center"}}>
Want a demo instead?
</Typography>
</div> */}
</div> </div>
</Fade> </Fade>
} }
+6 -4
View File
@@ -530,6 +530,7 @@ export const validateJson = (showResult) => {
const Workflows = (props) => { const Workflows = (props) => {
const { globalUrl, isLoggedIn, isLoaded, userdata, checkLogin } = props; const { globalUrl, isLoggedIn, isLoaded, userdata, checkLogin } = props;
document.title = "Shuffle - Workflows"; document.title = "Shuffle - Workflows";
let navigate = useNavigate(); let navigate = useNavigate();
@@ -3419,11 +3420,11 @@ const Workflows = (props) => {
> >
<img <img
style={{ style={{
height: imgSize+4, height: isCloud ? imgSize : imgSize+4,
width: imgSize+4, width: isCloud ? imgSize : imgSize+4,
position: "absolute", position: "absolute",
top: -2, top: isCloud ? -1 : -2,
left: -2, left: isCloud ? -1 : -2,
cursor: "pointer", cursor: "pointer",
zIndex: 99, zIndex: 99,
border: "2px solid rgba(255,255,255,0.7)", border: "2px solid rgba(255,255,255,0.7)",
@@ -3488,6 +3489,7 @@ const Workflows = (props) => {
{foundPriority != null && workflows.length < 6 ? {foundPriority != null && workflows.length < 6 ?
<Priority <Priority
globalUrl={globalUrl} globalUrl={globalUrl}
userdata={userdata}
priority={foundPriority} priority={foundPriority}
checkLogin={checkLogin} checkLogin={checkLogin}
appFramework={appFramework} appFramework={appFramework}
+23 -2
View File
@@ -406,6 +406,17 @@ func deployServiceWorkers(image string) {
cnt = 1 cnt = 1
} }
appReplicas := os.Getenv("SHUFFLE_APP_REPLICAS")
appReplicaCnt := 1
if len(appReplicas) > 0 {
newCnt, err := strconv.Atoi(appReplicas)
if err != nil {
log.Printf("[ERROR] %s is not a valid number for SHUFFLE_APP_REPLICAS", appReplicas)
} else {
appReplicaCnt = newCnt
}
}
log.Printf("[DEBUG] Found %d node(s) to replicate over. Defaulting to 1 IF we can't auto-discover them.", cnt) log.Printf("[DEBUG] Found %d node(s) to replicate over. Defaulting to 1 IF we can't auto-discover them.", cnt)
replicatedJobs := uint64(replicas * nodeCount) replicatedJobs := uint64(replicas * nodeCount)
@@ -462,8 +473,7 @@ func deployServiceWorkers(image string) {
Env: []string{ Env: []string{
fmt.Sprintf("SHUFFLE_SWARM_CONFIG=%s", os.Getenv("SHUFFLE_SWARM_CONFIG")), fmt.Sprintf("SHUFFLE_SWARM_CONFIG=%s", os.Getenv("SHUFFLE_SWARM_CONFIG")),
fmt.Sprintf("SHUFFLE_SWARM_NETWORK_NAME=%s", networkName), fmt.Sprintf("SHUFFLE_SWARM_NETWORK_NAME=%s", networkName),
fmt.Sprintf("SHUFFLE_APP_REPLICAS=%d", cnt), fmt.Sprintf("SHUFFLE_APP_REPLICAS=%d", appReplicaCnt),
fmt.Sprintf("TZ=%s", timezone),
fmt.Sprintf("SHUFFLE_LOGS_DISABLED=%s", os.Getenv("SHUFFLE_LOGS_DISABLED")), fmt.Sprintf("SHUFFLE_LOGS_DISABLED=%s", os.Getenv("SHUFFLE_LOGS_DISABLED")),
fmt.Sprintf("DEBUG_MEMORY=%s", os.Getenv("DEBUG_MEMORY")), fmt.Sprintf("DEBUG_MEMORY=%s", os.Getenv("DEBUG_MEMORY")),
fmt.Sprintf("SHUFFLE_APP_SDK_TIMEOUT=%s", os.Getenv("SHUFFLE_APP_SDK_TIMEOUT")), fmt.Sprintf("SHUFFLE_APP_SDK_TIMEOUT=%s", os.Getenv("SHUFFLE_APP_SDK_TIMEOUT")),
@@ -1411,6 +1421,7 @@ func main() {
continue continue
} }
//defer newresp.Body.Close()
if newresp.StatusCode == 405 { if newresp.StatusCode == 405 {
log.Printf("[WARNING] Received 405 from %s. This is likely due to a misconfigured base URL. Automatically swapping to GET request (backwards compatibility)", fullUrl) log.Printf("[WARNING] Received 405 from %s. This is likely due to a misconfigured base URL. Automatically swapping to GET request (backwards compatibility)", fullUrl)
@@ -1555,6 +1566,14 @@ func main() {
env = append(env, fmt.Sprintf("SHUFFLE_CLOUDRUN_URL=%s", os.Getenv("SHUFFLE_CLOUDRUN_URL"))) env = append(env, fmt.Sprintf("SHUFFLE_CLOUDRUN_URL=%s", os.Getenv("SHUFFLE_CLOUDRUN_URL")))
} }
if len(os.Getenv("SHUFFLE_SKIPSSL_VERIFY")) > 0 {
env = append(env, fmt.Sprintf("SHUFFLE_SKIPSSL_VERIFY=%s", os.Getenv("SHUFFLE_SKIPSSL_VERIFY")))
}
if len(os.Getenv("SHUFFLE_DEBUG_MEMORY")) > 0 {
env = append(env, fmt.Sprintf("SHUFFLE_DEBUG_MEMORY=%s", os.Getenv("SHUFFLE_DEBUG_MEMORY")))
}
err = deployWorker(workerImage, containerName, env, execution) err = deployWorker(workerImage, containerName, env, execution)
zombiecounter += 1 zombiecounter += 1
if err == nil { if err == nil {
@@ -1612,6 +1631,7 @@ func main() {
continue continue
} }
defer resultResp.Body.Close()
body, err := ioutil.ReadAll(resultResp.Body) body, err := ioutil.ReadAll(resultResp.Body)
if err != nil { if err != nil {
log.Printf("[ERROR] Failed reading confirm body: %s", err) log.Printf("[ERROR] Failed reading confirm body: %s", err)
@@ -1906,6 +1926,7 @@ func sendWorkerRequest(workflowExecution shuffle.ExecutionRequest) error {
return err return err
} }
defer newresp.Body.Close()
body, err := ioutil.ReadAll(newresp.Body) body, err := ioutil.ReadAll(newresp.Body)
if err != nil { if err != nil {
log.Printf("[ERROR] Failed reading body in worker request body to worker on %s: %s", streamUrl, err) log.Printf("[ERROR] Failed reading body in worker request body to worker on %s: %s", streamUrl, err)