Tons of priority management fixes - both frontend & backend
This commit is contained in:
+214
-33
@@ -177,7 +177,7 @@ const Admin = (props) => {
|
||||
const [secret2FA, setSecret2FA] = React.useState("");
|
||||
const [show2faSetup, setShow2faSetup] = useState(false);
|
||||
|
||||
const [adminTab, setAdminTab] = React.useState(1);
|
||||
const [adminTab, setAdminTab] = React.useState(2);
|
||||
const [showApiKey, setShowApiKey] = useState(false);
|
||||
const [billingInfo, setBillingInfo] = React.useState({});
|
||||
const [selectedStatus, setSelectedStatus] = React.useState([]);
|
||||
@@ -189,8 +189,6 @@ const Admin = (props) => {
|
||||
}
|
||||
}, [isDropzone]);
|
||||
|
||||
|
||||
|
||||
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
|
||||
|
||||
const get2faCode = (userId) => {
|
||||
@@ -299,6 +297,165 @@ const Admin = (props) => {
|
||||
)
|
||||
}
|
||||
|
||||
// Basically just a simple way to get a generated email
|
||||
// This also may help understand how to communicate with users
|
||||
// both inside and outside Shuffle
|
||||
// This could also be generated on the backend
|
||||
const mailsendingButton = (org) => {
|
||||
if (org === undefined || org === null) {
|
||||
return ""
|
||||
}
|
||||
|
||||
if (users.length === 0) {
|
||||
return ""
|
||||
}
|
||||
|
||||
// 1 mail based on users that have only apps
|
||||
// Another based on those doing workflows
|
||||
// Another based on those trying usecases(?) or templates
|
||||
//
|
||||
// Start based on edr, siem & ticketing
|
||||
// Talk about enrichment?
|
||||
// Check suggested usecases
|
||||
// Check suggested workflows
|
||||
var your_apps = "- Connecting "
|
||||
|
||||
var subject_add = 0
|
||||
var subject = "Want to automate "
|
||||
|
||||
if (org.security_framework !== undefined && org.security_framework !== null) {
|
||||
if (org.security_framework.cases.name !== undefined && org.security_framework.cases.name !== null && org.security_framework.cases.name !== "") {
|
||||
your_apps += org.security_framework.cases.name.replace("_", " ", -1) + ", "
|
||||
|
||||
if (subject_add < 2) {
|
||||
if (subject_add === 1) {
|
||||
subject += " & "
|
||||
}
|
||||
|
||||
subject_add += 1
|
||||
subject += org.security_framework.cases.name.replace("_", " ", -1)
|
||||
}
|
||||
}
|
||||
|
||||
if (org.security_framework.siem.name !== undefined && org.security_framework.siem.name !== null && org.security_framework.siem.name !== "") {
|
||||
your_apps += org.security_framework.siem.name.replace("_", " ", -1) + ", "
|
||||
|
||||
if (subject_add < 2) {
|
||||
if (subject_add === 1) {
|
||||
subject += " & "
|
||||
}
|
||||
|
||||
subject_add += 1
|
||||
subject += org.security_framework.siem.name.replace("_", " ", -1)
|
||||
}
|
||||
}
|
||||
|
||||
if (org.security_framework.communication.name !== undefined && org.security_framework.communication.name !== null && org.security_framework.communication.name !== "") {
|
||||
your_apps += org.security_framework.communication.name.replace("_", " ", -1) + ", "
|
||||
|
||||
if (subject_add < 2) {
|
||||
if (subject_add === 1) {
|
||||
subject += " & "
|
||||
}
|
||||
|
||||
subject_add += 1
|
||||
subject += org.security_framework.communication.name.replace("_", " ", -1)
|
||||
}
|
||||
}
|
||||
|
||||
if (org.security_framework.edr.name !== undefined && org.security_framework.edr.name !== null && org.security_framework.edr.name !== "") {
|
||||
your_apps += org.security_framework.edr.name.replace("_", " ", -1) + ", "
|
||||
|
||||
if (subject_add < 2) {
|
||||
if (subject_add === 1) {
|
||||
subject += " & "
|
||||
}
|
||||
|
||||
subject_add += 1
|
||||
subject += org.security_framework.edr.name.replace("_", " ", -1)
|
||||
}
|
||||
}
|
||||
|
||||
if (org.security_framework.intel.name !== undefined && org.security_framework.intel.name !== null && org.security_framework.intel.name !== "") {
|
||||
your_apps += org.security_framework.intel.name.replace("_", " ", -1) + ", "
|
||||
|
||||
if (subject_add < 2) {
|
||||
if (subject_add === 1) {
|
||||
subject += " & "
|
||||
}
|
||||
|
||||
subject_add += 1
|
||||
subject += org.security_framework.intel.name.replace("_", " ", -1)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Remove comma
|
||||
subject += "?"
|
||||
your_apps = your_apps.substring(0, your_apps.length - 2)
|
||||
}
|
||||
|
||||
|
||||
// Add usecases they may not have tried (from recommendations): org.priorities where item type is usecase
|
||||
var usecases = "- Building usecases like "
|
||||
const active_usecase = org.priorities.filter((item) => item.type === "usecase" && item.active === true)
|
||||
if (active_usecase.length > 0) {
|
||||
for (var i = 0; i < active_usecase.length; i++) {
|
||||
if (active_usecase[i].name.includes("Suggested Usecase: ")) {
|
||||
usecases += active_usecase[i].name.replace("Suggested Usecase: ", "", -1) + ", "
|
||||
} else {
|
||||
usecases += active_usecase[i].name + ", "
|
||||
}
|
||||
}
|
||||
|
||||
usecases = usecases.substring(0, usecases.length - 2)
|
||||
}
|
||||
|
||||
if (your_apps.length <= 15) {
|
||||
your_apps = ""
|
||||
}
|
||||
|
||||
if (usecases.length <= 30) {
|
||||
usecases = ""
|
||||
}
|
||||
|
||||
var workflow_amount = "a few"
|
||||
var admins = ""
|
||||
|
||||
// Loop users
|
||||
for (var i = 0; i < users.length; i++) {
|
||||
if (users[i].role === "admin") {
|
||||
admins += users[i].username + ","
|
||||
}
|
||||
}
|
||||
|
||||
// Remove last comma
|
||||
admins = admins.substring(0, admins.length - 1)
|
||||
|
||||
if (your_apps.length > 5) {
|
||||
your_apps += "%0D%0A"
|
||||
}
|
||||
|
||||
if (usecases.length > 5) {
|
||||
usecases += "%0D%0A"
|
||||
}
|
||||
|
||||
// Get drift username from userdata.username before @ in email
|
||||
const username = userdata.username.substring(0, userdata.username.indexOf("@"))
|
||||
|
||||
var body = `Hey,%0D%0AI saw you trying to use Shuffle, and thought we may be able to help. Right now, it looks like you have ${workflow_amount} workflows made, but I'm not sure if you're getting the most out of Shuffle.%0D%0A%0D%0AIf you're interested, I'd love to set up a quick call to see if we can help you get more out of Shuffle. %0D%0A%0D%0A
|
||||
|
||||
Some of the things we can help with:%0D%0A
|
||||
${your_apps}
|
||||
- Properly authenticating and custom building apps%0D%0A
|
||||
${usecases}
|
||||
- Creating special usecases%0D%0A%0D%0A
|
||||
|
||||
Let me know if you're interested, or set up a call here: https://drift.me/${username}`
|
||||
|
||||
return `mailto:${admins}?subject=${subject}&body=${body}`
|
||||
}
|
||||
|
||||
const deleteAuthentication = (data) => {
|
||||
alert.info("Deleting auth " + data.label);
|
||||
|
||||
@@ -367,6 +524,13 @@ const Admin = (props) => {
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
if (userdata.support === true && selectedOrganization.id !== "" && selectedOrganization.id !== undefined && selectedOrganization.id !== null && selectedOrganization.id !== userdata.active_org.id) {
|
||||
alert.info("Refreshing window to fix org support access")
|
||||
window.location.reload()
|
||||
return null
|
||||
}
|
||||
|
||||
const handleVerify2FA = (userId, code) => {
|
||||
const data = {
|
||||
code: code,
|
||||
@@ -799,6 +963,10 @@ const Admin = (props) => {
|
||||
|
||||
if (responseJson.lead_info !== undefined && responseJson.lead_info !== null) {
|
||||
var leads = []
|
||||
if (responseJson.lead_info.contacted) {
|
||||
leads.push("contacted")
|
||||
}
|
||||
|
||||
if (responseJson.lead_info.customer) {
|
||||
leads.push("customer")
|
||||
}
|
||||
@@ -1181,11 +1349,6 @@ const Admin = (props) => {
|
||||
|
||||
var localData = "";
|
||||
|
||||
// useEffect(() => {
|
||||
// console.log('confirm', fileContent);
|
||||
// }, [fileContent])
|
||||
|
||||
|
||||
|
||||
const getSchedules = () => {
|
||||
fetch(globalUrl + "/api/v1/workflows/schedules", {
|
||||
@@ -1322,6 +1485,10 @@ const Admin = (props) => {
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
getUsers()
|
||||
}, []);
|
||||
|
||||
const getSettings = () => {
|
||||
fetch(globalUrl + "/api/v1/getsettings", {
|
||||
method: "GET",
|
||||
@@ -2189,27 +2356,45 @@ const Admin = (props) => {
|
||||
*/}
|
||||
|
||||
{userdata.support === true ?
|
||||
<FormControl sx={{ m: 1, width: 300, }} style={{top: -10, right: 50, position: "absolute" }}>
|
||||
<InputLabel id="">Status</InputLabel>
|
||||
<Select
|
||||
style={{minWidth: 150, maxWidth: 150, }}
|
||||
labelId="multiselect-status"
|
||||
id="multiselect-status"
|
||||
multiple
|
||||
value={selectedStatus}
|
||||
onChange={handleStatusChange}
|
||||
input={<OutlinedInput label="Status" />}
|
||||
renderValue={(selected) => selected.join(', ')}
|
||||
MenuProps={MenuProps}
|
||||
>
|
||||
{["lead", "pov", "demo done", "customer", "student", ].map((name) => (
|
||||
<MenuItem key={name} value={name}>
|
||||
<Checkbox checked={selectedStatus.indexOf(name) > -1} />
|
||||
<ListItemText primary={name} />
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
<span style={{display: "flex", top: -10, right: 50, position: "absolute"}}>
|
||||
<a href={mailsendingButton(selectedOrganization)} target="_blank" rel="noopener noreferrer" style={{textDecoration: "none"}} disabled={selectedStatus.length !== 0}>
|
||||
<Button
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
disabled={selectedStatus.length !== 0}
|
||||
style={{ minWidth: 80, maxWidth: 80, height: "100%", }}
|
||||
onClick={() => {
|
||||
console.log("Should send mail to admins of org with context")
|
||||
handleStatusChange({target: {value: ["contacted"]}})
|
||||
|
||||
// open a mailto with subject "hello" and sender "frikky@shuffler.io"
|
||||
}}
|
||||
>
|
||||
Sales mail
|
||||
</Button>
|
||||
</a>
|
||||
<FormControl sx={{ m: 1, width: 300, }} style={{}}>
|
||||
<InputLabel id="">Status</InputLabel>
|
||||
<Select
|
||||
style={{minWidth: 150, maxWidth: 150, }}
|
||||
labelId="multiselect-status"
|
||||
id="multiselect-status"
|
||||
multiple
|
||||
value={selectedStatus}
|
||||
onChange={handleStatusChange}
|
||||
input={<OutlinedInput label="Status" />}
|
||||
renderValue={(selected) => selected.join(', ')}
|
||||
MenuProps={MenuProps}
|
||||
>
|
||||
{["contacted", "lead", "pov", "demo done", "customer", "student", ].map((name) => (
|
||||
<MenuItem key={name} value={name}>
|
||||
<Checkbox checked={selectedStatus.indexOf(name) > -1} />
|
||||
<ListItemText primary={name} />
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
</span>
|
||||
: null}
|
||||
|
||||
<Tooltip
|
||||
@@ -2588,10 +2773,6 @@ const Admin = (props) => {
|
||||
userdata={userdata}
|
||||
adminTab={adminTab}
|
||||
globalUrl={globalUrl}
|
||||
handleGetOrg={handleGetOrg}
|
||||
selectedOrganization={selectedOrganization}
|
||||
selectedOrganization={selectedOrganization}
|
||||
setSelectedOrganization={setSelectedOrganization}
|
||||
checkLogin={checkLogin}
|
||||
/>
|
||||
: adminTab === 3 ?
|
||||
|
||||
@@ -11484,7 +11484,7 @@ const AngularWorkflow = (defaultprops) => {
|
||||
>
|
||||
<div style={{ flex: "1" }}>
|
||||
<h3 style={{ marginBottom: "5px" }}>
|
||||
{selectedTrigger.app_name}: {selectedTrigger.status}
|
||||
{selectedTrigger.app_name}
|
||||
</h3>
|
||||
<a
|
||||
rel="noopener noreferrer"
|
||||
@@ -11526,7 +11526,7 @@ const AngularWorkflow = (defaultprops) => {
|
||||
onChange={selectedTriggerChange}
|
||||
/>
|
||||
|
||||
<div style={{ marginTop: "20px" }}>
|
||||
{/*<div style={{ marginTop: "20px" }}>
|
||||
Environment:
|
||||
<TextField
|
||||
style={{
|
||||
@@ -11549,6 +11549,7 @@ const AngularWorkflow = (defaultprops) => {
|
||||
value={selectedTrigger.environment}
|
||||
/>
|
||||
</div>
|
||||
*/}
|
||||
<Divider
|
||||
style={{
|
||||
marginTop: "20px",
|
||||
@@ -13697,7 +13698,7 @@ const AngularWorkflow = (defaultprops) => {
|
||||
>
|
||||
<h2 style={{ color: "rgba(255,255,255,0.5)" }}>
|
||||
<DirectionsRunIcon style={{ marginRight: 10 }} />
|
||||
All Executions
|
||||
All Workflow Runs
|
||||
</h2>
|
||||
</Breadcrumbs>
|
||||
<Button
|
||||
@@ -13710,7 +13711,7 @@ const AngularWorkflow = (defaultprops) => {
|
||||
color="primary"
|
||||
>
|
||||
<CachedIcon style={{ marginRight: 10 }} />
|
||||
Refresh executions
|
||||
Refresh Runs
|
||||
</Button>
|
||||
<Divider
|
||||
style={{
|
||||
@@ -13956,7 +13957,7 @@ const AngularWorkflow = (defaultprops) => {
|
||||
style={{ color: "rgba(255,255,255,0.5)", cursor: "pointer" }}
|
||||
onClick={() => { }}
|
||||
>
|
||||
See other Executions
|
||||
See more runs
|
||||
</h2>
|
||||
</span>
|
||||
</Breadcrumbs>
|
||||
@@ -14781,7 +14782,7 @@ const AngularWorkflow = (defaultprops) => {
|
||||
{curapp === null ? null : (
|
||||
<img
|
||||
alt={selectedResult.action.app_name}
|
||||
src={selectedResult === undefined ? theme.palette.defaultImage : selectedResult.action.app_name === "shuffle-subflow" ? triggers[4].large_image : selectedResult.action.app_name === "User Input" ? triggers[5].large_image : selectedResult.action.large_image !== undefined && selectedResult.action.large_image !== null && selectedResult.action.large_image !== "" ? selectedResult.action.large_image : curapp.large_image}
|
||||
src={selectedResult === undefined ? theme.palette.defaultImage : selectedResult.action.app_name === "shuffle-subflow" ? triggers[4].large_image : selectedResult.action.app_name === "User Input" ? triggers[5].large_image : selectedResult.action !== undefined && selectedResult.action.large_image !== undefined && selectedResult.action.large_image !== null && selectedResult.action.large_image !== "" ? selectedResult.action.large_image : curapp !== undefined ? curapp.large_image : theme.palette.defaultImage}
|
||||
style={{
|
||||
marginRight: 20,
|
||||
width: imgsize,
|
||||
@@ -15509,7 +15510,7 @@ const AngularWorkflow = (defaultprops) => {
|
||||
<div>
|
||||
<DialogTitle id="draggable-dialog-title" style={{ cursor: "move", }}>
|
||||
<div style={{ color: "white" }}>
|
||||
Authentication for {selectedApp.name}
|
||||
Authentication for {selectedApp.name.replace("_", " ", -1)}
|
||||
</div>
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -127,6 +127,8 @@ const Search = (props) => {
|
||||
textColor="secondary"
|
||||
onChange={setConfig}
|
||||
aria-label="disabled tabs example"
|
||||
variant="scrollable"
|
||||
scrollButtons="auto"
|
||||
>
|
||||
<Tab
|
||||
label=<span>
|
||||
|
||||
@@ -334,7 +334,7 @@ const Welcome = (props) => {
|
||||
const defaultImage = "/images/experienced.png"
|
||||
const experienced_image = userdata !== undefined && userdata !== null && userdata.active_org !== undefined && userdata.active_org.image !== undefined && userdata.active_org.image !== null && userdata.active_org.image !== "" ? userdata.active_org.image : defaultImage
|
||||
return (
|
||||
<div style={{width: 1000, margin: "auto", paddingBottom: 150, minHeight: 1500, }}>
|
||||
<div style={{width: 1000, margin: "auto", paddingBottom: 150, minHeight: 1500, marginTop: 50, }}>
|
||||
{/*
|
||||
<div style={{position: "fixed", bottom: 110, right: 110, display: "flex", }}>
|
||||
<img src="/images/Arrow.png" style={{width: 250, height: "100%",}} />
|
||||
|
||||
@@ -9,6 +9,7 @@ import { Navigate } from "react-router-dom";
|
||||
import SecurityFramework from '../components/SecurityFramework.jsx';
|
||||
import EditWorkflow from "../components/EditWorkflow.jsx"
|
||||
import { ShepherdTour, ShepherdTourContext } from 'react-shepherd'
|
||||
import Priority from "../components/Priority.jsx";
|
||||
|
||||
import { isMobile } from "react-device-detect"
|
||||
|
||||
@@ -524,7 +525,7 @@ export const validateJson = (showResult) => {
|
||||
};
|
||||
|
||||
const Workflows = (props) => {
|
||||
const { globalUrl, isLoggedIn, isLoaded, userdata } = props;
|
||||
const { globalUrl, isLoggedIn, isLoaded, userdata, checkLogin } = props;
|
||||
document.title = "Shuffle - Workflows";
|
||||
let navigate = useNavigate();
|
||||
|
||||
@@ -585,6 +586,7 @@ const Workflows = (props) => {
|
||||
const [drawerOpen, setDrawerOpen] = React.useState(false)
|
||||
const [videoViewOpen, setVideoViewOpen] = React.useState(false)
|
||||
const [gettingStartedItems, setGettingStartedItems] = React.useState([])
|
||||
|
||||
const drawerWidth = drawerOpen ? 325 : 0
|
||||
|
||||
const sidebarKey = "getting_started_sidebar"
|
||||
@@ -618,7 +620,6 @@ const Workflows = (props) => {
|
||||
setDrawerOpen(true)
|
||||
} else {
|
||||
if (sidebar === "open") {
|
||||
console.log("OPEN the thingy!")
|
||||
setDrawerOpen(true)
|
||||
} else {
|
||||
setDrawerOpen(false)
|
||||
@@ -1126,6 +1127,11 @@ const Workflows = (props) => {
|
||||
var newcategories = []
|
||||
for (var key in categorydata) {
|
||||
var category = categorydata[key]
|
||||
// Check if category is bool
|
||||
if (typeof category === "boolean") {
|
||||
continue
|
||||
}
|
||||
|
||||
category.matches = []
|
||||
|
||||
for (var subcategorykey in category.list) {
|
||||
@@ -1216,8 +1222,8 @@ const Workflows = (props) => {
|
||||
color: "#ffffff",
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
minWidth: isMobile ? "100%" : drawerWidth > 0 ? 824 : 1024,
|
||||
maxWidth: isMobile ? "100%" : drawerWidth > 0 ? 824 : 1024,
|
||||
minWidth: isMobile ? "100%" : 1024,
|
||||
maxWidth: isMobile ? "100%" : 1024,
|
||||
margin: drawerWidth === 0 ? "auto" : `auto ${drawerWidth+100} auto auto`,
|
||||
paddingBottom: 200,
|
||||
};
|
||||
@@ -1583,7 +1589,8 @@ const Workflows = (props) => {
|
||||
const innerColor = "rgba(255,255,255,0.3)";
|
||||
const setupPaperStyle = {
|
||||
minHeight: paperAppStyle.minHeight,
|
||||
width: paperAppStyle.width,
|
||||
maxWidth: "100%",
|
||||
minWidth: paperAppStyle.width,
|
||||
color: innerColor,
|
||||
padding: paperAppStyle.padding,
|
||||
borderRadius: paperAppStyle.borderRadius,
|
||||
@@ -1612,7 +1619,7 @@ const Workflows = (props) => {
|
||||
}}
|
||||
>
|
||||
<Tooltip title={`New Workflow`} placement="bottom">
|
||||
<span style={{ textAlign: "center", width: 300, margin: "auto" }}>
|
||||
<span style={{ textAlign: "center", minWidth: 300, margin: "auto" }}>
|
||||
<AddCircleIcon style={{ height: 65, width: 65 }} />
|
||||
</span>
|
||||
</Tooltip>
|
||||
@@ -3197,7 +3204,7 @@ const Workflows = (props) => {
|
||||
var workflowDelay = -150
|
||||
var appDelay = -75
|
||||
|
||||
|
||||
const foundPriority = userdata === undefined || userdata === null ? null : userdata.priorities.find(prio => prio.type === "usecase" && prio.active === true)
|
||||
return (
|
||||
<div style={viewStyle}>
|
||||
<div style={workflowViewStyle}>
|
||||
@@ -3470,9 +3477,17 @@ const Workflows = (props) => {
|
||||
</div>
|
||||
: null}
|
||||
|
||||
<div style={{marginTop: 15, }}>
|
||||
{foundPriority != null && workflows.length < 6 ?
|
||||
<Priority
|
||||
globalUrl={globalUrl}
|
||||
priority={foundPriority}
|
||||
checkLogin={checkLogin}
|
||||
/>
|
||||
: null}
|
||||
|
||||
<div style={{marginTop: 15, marginBottom: 50, }}>
|
||||
{view === "grid" ? (
|
||||
<Grid container spacing={4} style={paperAppContainer}>
|
||||
<Grid container spacing={filteredWorkflows.length === 0 ? 12 : filteredWorkflows.length === 1 ? 6 : 4} style={paperAppContainer}>
|
||||
<Zoom in={true} style={{ transitionDelay: `${workflowDelay}ms` }}>
|
||||
<NewWorkflowPaper />
|
||||
</Zoom>
|
||||
@@ -3507,6 +3522,15 @@ const Workflows = (props) => {
|
||||
)}
|
||||
</div>
|
||||
|
||||
{foundPriority != null && filteredWorkflows.length > 6 ?
|
||||
<Priority
|
||||
style={{marginTop: 15, }}
|
||||
globalUrl={globalUrl}
|
||||
priority={foundPriority}
|
||||
checkLogin={checkLogin}
|
||||
/>
|
||||
: null}
|
||||
|
||||
<div style={{ marginBottom: 100 }} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user