#709: Added usecase discovery and mapping on /usecases path

This commit is contained in:
frikky
2022-02-20 19:03:58 +01:00
parent ce0bfa7f89
commit 36f9ed26c2
6 changed files with 301 additions and 173 deletions
+2 -2
View File
@@ -85,7 +85,7 @@ const App = (message, props) => {
!window.location.pathname.startsWith("/docs") && !window.location.pathname.startsWith("/docs") &&
!window.location.pathname.startsWith("/detectionframework") && !window.location.pathname.startsWith("/detectionframework") &&
!window.location.pathname.startsWith("/adminsetup") && !window.location.pathname.startsWith("/adminsetup") &&
!window.location.pathname.startsWith("/dashboard") !window.location.pathname.startsWith("/usecases")
) { ) {
window.location = "/login"; window.location = "/login";
} }
@@ -407,7 +407,7 @@ const App = (message, props) => {
/> />
<Route <Route
exact exact
path="/dashboard" path="/usecases"
element={ element={
<Dashboard <Dashboard
isLoaded={isLoaded} isLoaded={isLoaded}
+11
View File
@@ -35,6 +35,7 @@ import {
import { import {
Analytics as AnalyticsIcon, Analytics as AnalyticsIcon,
Lightbulb as LightbulbIcon,
} from "@mui/icons-material"; } from "@mui/icons-material";
//import LogoutIcon from '@mui/icons-material/Logout'; //import LogoutIcon from '@mui/icons-material/Logout';
import { useAlert } from "react-alert"; import { useAlert } from "react-alert";
@@ -466,6 +467,16 @@ const Header = (props) => {
<AnalyticsIcon style={{marginRight: 5 }}/> Get Started <AnalyticsIcon style={{marginRight: 5 }}/> Get Started
</Link> </Link>
</MenuItem> </MenuItem>
<MenuItem
onClick={(event) => {
event.preventDefault();
handleClose();
}}
>
<Link to="/usecases" style={hrefStyle}>
<LightbulbIcon style={{marginRight: 5 }}/> Use Cases
</Link>
</MenuItem>
<MenuItem <MenuItem
onClick={(event) => { onClick={(event) => {
event.preventDefault(); event.preventDefault();
+120 -32
View File
@@ -10,15 +10,18 @@ import { useAlert } from "react-alert";
import { import {
Typography, Typography,
Grid,
Paper,
Chip,
} from "@material-ui/core"; } from "@material-ui/core";
// core components // core components
import { //import {
chartExample1, // chartExample1,
chartExample2, // chartExample2,
chartExample3, // chartExample3,
chartExample4, // chartExample4,
} from "../charts.js"; //} from "../charts.js";
import { import {
RadialBarChart, RadialBarChart,
@@ -298,6 +301,47 @@ const categorydata = [
} }
] ]
const UsecaseListComponent = ({keys}) => {
if (keys === undefined || keys === null || keys.length === 0) {
return null
}
return (
<div style={{marginTop: 25, minHeight: 1000,}}>
<Typography variant="h1">
Shuffle usecases
</Typography>
<Typography variant="body1">
Usecases in Shuffle are divided into {keys.length} type{keys.length === 1 ? "" : "s"}.
</Typography>
{keys.map((usecase, index) => {
return (
<div key={index} style={{marginTop: index === 0 ? 50 : 100}}>
<Typography variant="h6">
{usecase.name}
</Typography>
<Grid container spacing={3} style={{marginTop: 25}}>
{usecase.list.map((subcase, subindex) => {
return (
<Grid item xs={4} key={subindex} style={{minHeight: 110,}}>
<Paper style={{padding: "30px 30px 20px 30px", minHeight: 110, cursor: "pointer", border: `1px solid ${usecase.color}`}} onClick={() => {
console.log("Clicked: ", subcase.name)
}}>
<Typography variant="body1">
<b>{subcase.name}</b>
</Typography>
</Paper>
</Grid>
)
})}
</Grid>
</div>
)
})}
</div>
)
}
const TreeChart = ({keys}) => { const TreeChart = ({keys}) => {
const [hovered, setHovered] = useState(""); const [hovered, setHovered] = useState("");
@@ -307,8 +351,6 @@ const TreeChart = ({keys}) => {
}}> }}>
<TreeMap <TreeMap
id="all_categories" id="all_categories"
height={500}
width={1000}
data={keys} data={keys}
margins={10} margins={10}
series={ series={
@@ -341,12 +383,15 @@ const TreeChart = ({keys}) => {
} }
const RadialChart = ({keys}) => { const RadialChart = ({keys, setSelectedCategory}) => {
const [hovered, setHovered] = useState(""); const [hovered, setHovered] = useState("");
return ( return (
<div style={{cursor: "pointer",}} onClick={() => { <div style={{cursor: "pointer",}} onClick={() => {
console.log("Click: ", hovered) console.log("Click: ", hovered)
if (setSelectedCategory !== undefined) {
setSelectedCategory(hovered)
}
}}> }}>
<RadialAreaChart <RadialAreaChart
id="workflow_categories" id="workflow_categories"
@@ -355,10 +400,12 @@ const RadialChart = ({keys}) => {
data={keys} data={keys}
axis={<RadialAxis type="category" />} axis={<RadialAxis type="category" />}
series={ series={
<RadialAreaSeries <RadialAreaSeries
interpolation="smooth" interpolation="smooth"
colorScheme={'#f86a3e'} colorScheme={(colorInput) => {
console.log("Color: ", colorInput)
return '#f86a3e'
}}
animated={false} animated={false}
id="workflow_series_id" id="workflow_series_id"
style={{cursor: "pointer",}} style={{cursor: "pointer",}}
@@ -381,7 +428,6 @@ const RadialChart = ({keys}) => {
}} }}
isRadial={true} isRadial={true}
onValueEnter={(event) => { onValueEnter={(event) => {
console.log("Entered: ", event.value.x)
if (hovered !== event.value.x) { if (hovered !== event.value.x) {
setHovered(event.value.x) setHovered(event.value.x)
} }
@@ -394,7 +440,7 @@ const RadialChart = ({keys}) => {
}} }}
content={(data, color) => { content={(data, color) => {
return ( return (
<div style={{backgroundColor: theme.palette.inputColor, border: "1px solid rgba(255,255,255,0.3)", color: "white", padding: 5, cursor: "pointer",}}> <div style={{borderRadius: theme.palette.borderRadius, backgroundColor: theme.palette.inputColor, border: "1px solid rgba(255,255,255,0.3)", color: "white", padding: 5, cursor: "pointer",}}>
<Typography variant="body1"> <Typography variant="body1">
{data.x} {data.x}
</Typography> </Typography>
@@ -438,15 +484,24 @@ const Dashboard = (props) => {
const [keys, setKeys] = useState([]) const [keys, setKeys] = useState([])
const [treeKeys, setTreeKeys] = useState([]) const [treeKeys, setTreeKeys] = useState([])
//const keys = [ const [selectedUsecaseCategory, setSelectedUsecaseCategory] = useState("");
// { key: '1. Collect & Distribute', data: 2 }, const [selectedUsecases, setSelectedUsecases] = useState([]);
// { key: '2. Enrich', data: 11 }, const [usecases, setUsecases] = useState([]);
// { key: '3. Detect', data: 3 },
// { key: '4. Respond', data: 4 },
// { key: 'Validation', data: 7, color: "red",},
//]
document.title = "Shuffle - dashboard"; useEffect(() => {
console.log("Changed: ", selectedUsecaseCategory)
if (selectedUsecaseCategory.length === 0) {
setSelectedUsecases(usecases)
} else {
const foundUsecase = usecases.find(data => data.name === selectedUsecaseCategory)
if (foundUsecase !== undefined && foundUsecase !== null) {
console.log("FOUND: ", foundUsecase)
setSelectedUsecases([foundUsecase])
}
}
}, [selectedUsecaseCategory])
document.title = "Shuffle - usecases";
var dayGraphLabels = [60, 80, 65, 130, 80, 105, 90, 130, 70, 115, 60, 130]; var dayGraphLabels = [60, 80, 65, 130, 80, 105, 90, 130, 70, 115, 60, 130];
var dayGraphData = [60, 80, 65, 130, 80, 105, 90, 130, 70, 115, 60, 130]; var dayGraphData = [60, 80, 65, 130, 80, 105, 90, 130, 70, 115, 60, 130];
@@ -455,8 +510,7 @@ const Dashboard = (props) => {
var treeCategories = [] var treeCategories = []
for (key in categorydata) { for (key in categorydata) {
const category = categorydata[key] const category = categorydata[key]
console.log("cat: ", category) allCategories.push({"key": category.name, "data": category.list.length, "color": category.color})
allCategories.push({"key": category.name, "data": category.list.length,})
treeCategories.push({"key": category.name, "data": 100, "color": category.color,}) treeCategories.push({"key": category.name, "data": 100, "color": category.color,})
for (var subkey in category.list) { for (var subkey in category.list) {
treeCategories.push({"key": category.list[subkey].name, "data": 20, "color": category.color}) treeCategories.push({"key": category.list[subkey].name, "data": 20, "color": category.color})
@@ -487,6 +541,8 @@ const Dashboard = (props) => {
if (responseJson.success !== false) { if (responseJson.success !== false) {
console.log("Usecases: ", responseJson) console.log("Usecases: ", responseJson)
handleKeysetting(responseJson) handleKeysetting(responseJson)
setUsecases(responseJson)
setSelectedUsecases(responseJson)
} }
}) })
.catch((error) => { .catch((error) => {
@@ -748,20 +804,52 @@ const Dashboard = (props) => {
) : null; ) : null;
const data = ( const data = (
<div className="content" style={{paddingBottom: 200}}> <div className="content" style={{width: 1000, margin: "auto", paddingBottom: 200, textAlign: "center",}}>
{keys.length > 0 ? <div style={{width: 500, margin: "auto"}}>
<RadialChart keys={keys} /> {keys.length > 0 ?
<RadialChart keys={keys} setSelectedCategory={setSelectedUsecaseCategory} />
: null}
</div>
{usecases !== null && usecases !== undefined && usecases.length > 0 ?
<div style={{ display: "flex", marginLeft: 100,}}>
{usecases.map((usecase, index) => {
return (
<Chip
key={usecase.name}
style={{
backgroundColor: selectedUsecaseCategory === usecase.name ? usecase.color : theme.palette.surfaceColor,
marginRight: 10,
paddingLeft: 5,
paddingRight: 5,
height: 28,
cursor: "pointer",
border: `1px solid ${usecase.color}`,
color: "white",
}}
label={`${usecase.name} (${usecase.list.length})`}
onClick={() => {
console.log("Clicked: ", usecase.name)
if (selectedUsecaseCategory === usecase.name) {
setSelectedUsecaseCategory("")
} else {
setSelectedUsecaseCategory(usecase.name)
}
//addFilter(usecase.name.slice(3,usecase.name.length))
}}
variant="outlined"
color="primary"
/>
)
})}
</div>
: null} : null}
<UsecaseListComponent keys={selectedUsecases} />
{treeKeys.length > 0 ? {treeKeys.length > 0 ?
<TreeChart keys={treeKeys} /> <TreeChart keys={treeKeys} />
: null} : null}
{/*
<StackedBarSeries
type="stackedDiverging"
data={keys}
/>
*/}
{newdata} {newdata}
</div> </div>
+4 -3
View File
@@ -2160,7 +2160,7 @@ const GettingStarted = (props) => {
}) })
} }
}}> }}>
<Link to="/detectionframework" style={{textDecoration: "none", color: "#f86a3e",}}>Discover your apps</Link> by following our simple security model! <Link to="/detectionframework" style={{textDecoration: "none", color: "#f86a3e",}}>Find relevant apps</Link> and start your automation journey
</Typography> </Typography>
), ),
tutorial: "find_integrations", tutorial: "find_integrations",
@@ -2168,7 +2168,8 @@ const GettingStarted = (props) => {
{ {
html: html:
<Typography variant={textType} style={{marginTop: textSpacingDiff}}> <Typography variant={textType} style={{marginTop: textSpacingDiff}}>
Discover <span style={{cursor: "pointer", textDecoration: "none", color: "#f86a3e",}} onClick={() => { Discover <Link to="/usecases" style={{cursor: "pointer", textDecoration: "none", color: "#f86a3e",}}>Use Case ideas</Link> and&nbsp;
<span style={{cursor: "pointer", textDecoration: "none", color: "#f86a3e",}} onClick={() => {
if (isCloud) { if (isCloud) {
navigate(`/search?tab=workflows`) navigate(`/search?tab=workflows`)
@@ -2193,7 +2194,7 @@ const GettingStarted = (props) => {
alert.success("TBD: Coming in version 1.0.0"); alert.success("TBD: Coming in version 1.0.0");
} }
}}> }}>
use-cases made by other creators</span>! workflows made by other creators</span>!
</Typography>, </Typography>,
tutorial: "discover_workflows", tutorial: "discover_workflows",
}, },
+17 -12
View File
@@ -731,17 +731,22 @@ const Settings = (props) => {
<div style={{ display: runFlex ? "flex" : "", width: "100%" }}> <div style={{ display: runFlex ? "flex" : "", width: "100%" }}>
<div> <div>
{isCloud ? {isCloud ?
<Button <span>
style={{ height: 40, marginTop: 10 }} <Typography variant="body1" color="textSecondary">
variant="outlined" By connecting your Github account, you agree to our <a href="/docs/terms-of-service" target="_blank" style={{ textDecoration: "none", color: "#f86a3e"}}>Terms of Service</a>, and acknowledge that your non-sensitive data will be turned into a <a target="_blank" style={{ textDecoration: "none", color: "#f86a3e"}} href="https://shuffler.io/search?tab=creators">creator account</a>. This enables you to earn a passive income from Shuffle. This IS reversible.
color="primary" </Typography>
fullWidth={true} <Button
onClick={() => { style={{ height: 40, marginTop: 10 }}
handleGithubConnection(); variant="outlined"
}} color="primary"
> fullWidth={true}
Connect to Github onClick={() => {
</Button> handleGithubConnection();
}}
>
Connect to Github
</Button>
</span>
: null} : null}
</div> </div>
<div style={{ flex: 1, display: "flex" }}> <div style={{ flex: 1, display: "flex" }}>
@@ -861,7 +866,7 @@ const Settings = (props) => {
handleEthereumConnection(); handleEthereumConnection();
}} }}
> >
Authenticate Authenticate Metamask Wallet
</Button> </Button>
)} )}
</div> </div>
+147 -124
View File
@@ -2475,58 +2475,60 @@ const Workflows = (props) => {
setNewWorkflowTags(newWorkflowTags); setNewWorkflowTags(newWorkflowTags);
}} }}
/> />
<FormControl style={{flex: 1, marginLeft: 5, }}> {usecases !== null && usecases !== undefined && usecases.length > 0 ?
<InputLabel htmlFor="grouped-select-usecase">Usecases</InputLabel> <FormControl style={{flex: 1, marginLeft: 5, }}>
<Select <InputLabel htmlFor="grouped-select-usecase">Usecases</InputLabel>
defaultValue="" <Select
id="grouped-select" defaultValue=""
label="Usecases" id="grouped-select"
multiple label="Matching Usecase"
value={selectedUsecases} multiple
renderValue={(selected) => selected.join(', ')} value={selectedUsecases}
onChange={(event) => { renderValue={(selected) => selected.join(', ')}
console.log("Changed: ", event) onChange={(event) => {
}} console.log("Changed: ", event)
> }}
<MenuItem value=""> >
<em>None</em> <MenuItem value="">
</MenuItem> <em>None</em>
{usecases.map((usecase, index) => { </MenuItem>
console.log(usecase) {usecases.map((usecase, index) => {
return ( console.log(usecase)
<span key={index}> return (
<ListSubheader <span key={index}>
style={{color: usecase.color}} <ListSubheader
> style={{color: usecase.color}}
{usecase.name} >
</ListSubheader> {usecase.name}
{usecase.list.map((subcase, subindex) => { </ListSubheader>
//console.log(subcase) {usecase.list.map((subcase, subindex) => {
total_count += 1 //console.log(subcase)
return ( total_count += 1
<MenuItem value={total_count} onClick={(event) => { return (
if (selectedUsecases.includes(subcase.name)) { <MenuItem key={subindex} value={total_count} onClick={(event) => {
const itemIndex = selectedUsecases.indexOf(subcase.name) if (selectedUsecases.includes(subcase.name)) {
if (itemIndex > -1) { const itemIndex = selectedUsecases.indexOf(subcase.name)
selectedUsecases.splice(itemIndex, 1) if (itemIndex > -1) {
selectedUsecases.splice(itemIndex, 1)
}
} else {
selectedUsecases.push(subcase.name)
} }
} else {
selectedUsecases.push(subcase.name)
}
setUpdate(Math.random()); setUpdate(Math.random());
setSelectedUsecases(selectedUsecases) setSelectedUsecases(selectedUsecases)
}}> }}>
<Checkbox style={{color: selectedUsecases.includes(subcase.name) ? usecase.color : theme.palette.inputColor}} checked={selectedUsecases.includes(subcase.name)} /> <Checkbox style={{color: selectedUsecases.includes(subcase.name) ? usecase.color : theme.palette.inputColor}} checked={selectedUsecases.includes(subcase.name)} />
<ListItemText primary={subcase.name} /> <ListItemText primary={subcase.name} />
</MenuItem> </MenuItem>
) )
})} })}
</span> </span>
) )
})} })}
</Select> </Select>
</FormControl> </FormControl>
: null}
</div> </div>
{showMoreClicked ? {showMoreClicked ?
@@ -2643,7 +2645,7 @@ const Workflows = (props) => {
{view === "list" && ( {view === "list" && (
<Tooltip color="primary" title={"Grid View"} placement="top"> <Tooltip color="primary" title={"Grid View"} placement="top">
<Button <Button
color="primary" color="secondary"
variant="text" variant="text"
onClick={() => { onClick={() => {
localStorage.setItem("view", "grid"); localStorage.setItem("view", "grid");
@@ -2657,7 +2659,7 @@ const Workflows = (props) => {
{view === "grid" && ( {view === "grid" && (
<Tooltip color="primary" title={"List View"} placement="top"> <Tooltip color="primary" title={"List View"} placement="top">
<Button <Button
color="primary" color="secondary"
variant="text" variant="text"
onClick={() => { onClick={() => {
localStorage.setItem("view", "list"); localStorage.setItem("view", "list");
@@ -2670,12 +2672,12 @@ const Workflows = (props) => {
)} )}
<Tooltip color="primary" title={"Import workflows"} placement="top"> <Tooltip color="primary" title={"Import workflows"} placement="top">
{importLoading ? ( {importLoading ? (
<Button color="primary" style={{}} variant="text" onClick={() => {}}> <Button color="secondary" style={{}} variant="text" onClick={() => {}}>
<CircularProgress style={{ maxHeight: 15, maxWidth: 15 }} /> <CircularProgress style={{ maxHeight: 15, maxWidth: 15 }} />
</Button> </Button>
) : ( ) : (
<Button <Button
color="primary" color="secondary"
style={{}} style={{}}
variant="text" variant="text"
onClick={() => upload.click()} onClick={() => upload.click()}
@@ -2698,7 +2700,7 @@ const Workflows = (props) => {
placement="top" placement="top"
> >
<Button <Button
color="primary" color="secondary"
style={{}} style={{}}
variant="text" variant="text"
onClick={() => { onClick={() => {
@@ -2884,8 +2886,51 @@ const Workflows = (props) => {
<div style={viewStyle}> <div style={viewStyle}>
<div style={workflowViewStyle}> <div style={workflowViewStyle}>
<div style={{ display: "flex" }}> <div style={{ display: "flex" }}>
<div style={{ flex: 3 }}> <div style={{ flex: 1 }}>
<h2>Workflows</h2> <Typography variant="h1" style={{fontSize: 30}}>
Workflows
</Typography>
</div>
{/*
<div style={{ flex: 1 }}>
<Typography style={{ marginTop: 7, marginBottom: "auto" }}>
<a
rel="noopener noreferrer"
target="_blank"
href="https://shuffler.io/docs/workflows"
style={{ textDecoration: "none", color: "#f85a3e" }}
>
Learn more about Workflows
</a>
</Typography>
</div>
*/}
<div style={{ display: "flex", margin: "0px 0px 20px 0px" }}>
<div style={{ flex: 1, float: "right" }}>
<ChipInput
style={{}}
InputProps={{
style: {
color: "white",
maxWidth: 275,
minWidth: 275,
},
}}
placeholder="Add Filter"
color="primary"
fullWidth
value={filters}
onAdd={(chip) => {
addFilter(chip);
}}
onDelete={(_, index) => {
removeFilter(index);
}}
/>
</div>
</div>
<div style={{ flex: 1, textAlign: "right" }}>
{workflowButtons}
</div> </div>
</div> </div>
{/* {/*
@@ -2936,73 +2981,51 @@ const Workflows = (props) => {
}} }}
*/} */}
{usecases !== null && usecases !== undefined && usecases.length > 0 ? <div style={{width: "100%",}}>
<div style={{ display: "flex",}}> {usecases !== null && usecases !== undefined && usecases.length > 0 ?
{usecases.map((usecase, index) => { <div style={{ display: "flex",}}>
console.log(usecase) {usecases.map((usecase, index) => {
return ( console.log(usecase)
<Chip return (
key={usecase.name} <Paper
style={{ key={usecase.name}
backgroundColor: "#3d3f43", style={{
backgroundColor: theme.palette.surfaceColor, flex: 1,
marginRight: 10, backgroundColor: filters.includes(usecase.name.toLowerCase()) ? usecase.color : theme.palette.surfaceColor,
paddingLeft: 5, borderRadius: theme.palette.borderRadius,
paddingRight: 5, marginRight: index === usecases.length-1 ? 0 : 10,
height: 28, height: 65,
cursor: "pointer", cursor: "pointer",
border: `1px solid ${usecase.color}`, border: `1px solid ${usecase.color}`,
color: "white", overflow: "hidden",
}} padding: 10,
label={`${usecase.name} (${usecase.matches.length}/${usecase.list.length})`} }}
onClick={() => { onClick={() => {
console.log("Clicked!") console.log("Clicked!")
addFilter(usecase.name.slice(3,usecase.name.length)) return
}} if (filters.includes(usecase.name.toLowerCase())) {
variant="outlined" addFilter(usecase.name)
color="primary" } else {
/> const foundIndex = filters.indexOf(usecase.name.toLowerCase())
) removeFilter(foundIndex)
})} }
</div>
: null} }}
<div style={{ display: "flex", margin: "0px 0px 20px 0px" }}> >
<div style={{ flex: 1 }}> <a href={`/usecases?selected=${usecase.name}`} rel="noopener noreferrer" target="_blank" style={{ textDecoration: "none", }}>
<Typography style={{ marginTop: 7, marginBottom: "auto" }}> <Typography variant="body1" color="textPrimary">
<a {usecase.name}
rel="noopener noreferrer" </Typography>
target="_blank" <Typography variant="body2" color="textSecondary">
href="https://shuffler.io/docs/workflows" Finished: {usecase.matches.length}/{usecase.list.length}
style={{ textDecoration: "none", color: "#f85a3e" }} </Typography>
> </a>
Learn more about Workflows </Paper>
</a> )
</Typography> })}
</div> </div>
<div style={{ flex: 1, float: "right" }}> : null}
<ChipInput </div>
style={{}}
InputProps={{
style: {
color: "white",
},
}}
placeholder="Add Filter"
color="primary"
fullWidth
value={filters}
onAdd={(chip) => {
addFilter(chip);
}}
onDelete={(_, index) => {
removeFilter(index);
}}
/>
</div>
<div style={{ float: "right", flex: 1, textAlign: "right" }}>
{workflowButtons}
</div>
</div>
<div style={{ marginTop: 15 }} /> <div style={{ marginTop: 15 }} />
{actionImageList !== undefined && {actionImageList !== undefined &&
actionImageList !== null && actionImageList !== null &&