Added all new common files related to onboarding and workflow suggestions
This commit is contained in:
@@ -47,6 +47,7 @@
|
|||||||
"react": "^16.14.0",
|
"react": "^16.14.0",
|
||||||
"react-alert": "^5.5.0",
|
"react-alert": "^5.5.0",
|
||||||
"react-alert-template-basic": "^1.0.0",
|
"react-alert-template-basic": "^1.0.0",
|
||||||
|
"react-alice-carousel": "^2.6.4",
|
||||||
"react-avatar-editor": "^11.1.0",
|
"react-avatar-editor": "^11.1.0",
|
||||||
"react-beforeunload": "^2.2.1",
|
"react-beforeunload": "^2.2.1",
|
||||||
"react-chartjs-2": "^2.11.1",
|
"react-chartjs-2": "^2.11.1",
|
||||||
|
|||||||
+214
-35
@@ -1,12 +1,15 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
|
|
||||||
import { securityFramework } from "./LandingpageUsecases.jsx";
|
import { securityFramework} from "./LandingpageUsecases.jsx";
|
||||||
import CytoscapeComponent from 'react-cytoscapejs';
|
import CytoscapeComponent from 'react-cytoscapejs';
|
||||||
import frameworkStyle from '../frameworkStyle.jsx';
|
import frameworkStyle from '../frameworkStyle.jsx';
|
||||||
import AppSearch from './Appsearch.jsx';
|
import AppSearch from './Appsearch.jsx';
|
||||||
import { v4 as uuidv4 } from "uuid";
|
import { v4 as uuidv4 } from "uuid";
|
||||||
import theme from '../theme';
|
import theme from '../theme';
|
||||||
import { useAlert } from "react-alert";
|
import { useAlert } from "react-alert";
|
||||||
|
import { usecaseTypes } from "../components/UsecaseSearch.jsx"
|
||||||
|
import PaperComponent from "../components/PaperComponent.jsx"
|
||||||
|
import SuggestedWorkflows from "../components/SuggestedWorkflows.jsx"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Paper,
|
Paper,
|
||||||
@@ -16,6 +19,7 @@ import {
|
|||||||
Badge,
|
Badge,
|
||||||
CircularProgress,
|
CircularProgress,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
|
Dialog,
|
||||||
} from "@material-ui/core";
|
} from "@material-ui/core";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -518,24 +522,105 @@ export const usecases = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const Framework = (props) => {
|
const Framework = (props) => {
|
||||||
const { globalUrl, isLoaded, showOptions, selectedOption, rolling, frameworkData, size, inputUsecase, isLoggedIn, color, discoveryWrapper, setDiscoveryWrapper} = props;
|
const { globalUrl, isLoaded, showOptions, selectedOption, rolling, frameworkData, setFrameworkData, size, inputUsecase, isLoggedIn, color, discoveryWrapper, setDiscoveryWrapper, userdata, apps, } = props;
|
||||||
|
|
||||||
const [cy, setCy] = React.useState()
|
const [cy, setCy] = React.useState()
|
||||||
const [edgesStarted, setEdgesStarted] = React.useState(false)
|
const [edgesStarted, setEdgesStarted] = React.useState(false)
|
||||||
const [graphDone, setGraphDone] = React.useState(false)
|
const [graphDone, setGraphDone] = React.useState(false)
|
||||||
const [cyDone, setCyDone] = React.useState(false)
|
const [cyDone, setCyDone] = React.useState(false)
|
||||||
const [discoveryData, setDiscoveryData] = React.useState({})
|
const [discoveryData, setDiscoveryData] = React.useState({})
|
||||||
const [selectionOpen, setSelectionOpen] = React.useState(true)
|
const [selectionOpen, setSelectionOpen] = React.useState(true)
|
||||||
|
const [frameworkSuggestions, setFrameworkSuggestions] = React.useState([])
|
||||||
const [newSelectedApp, setNewSelectedApp] = React.useState({})
|
const [newSelectedApp, setNewSelectedApp] = React.useState({})
|
||||||
const [defaultSearch, setDefaultSearch] = React.useState("")
|
const [defaultSearch, setDefaultSearch] = React.useState("")
|
||||||
const [animationStarted, setAnimationStarted] = React.useState(false)
|
const [animationStarted, setAnimationStarted] = React.useState(false)
|
||||||
const [paperTitle, setPaperTitle] = React.useState("")
|
const [paperTitle, setPaperTitle] = React.useState("")
|
||||||
|
const [changedApp, setChangedApp] = React.useState("")
|
||||||
|
|
||||||
|
|
||||||
|
const [usecaseType, setUsecaseType] = React.useState(0)
|
||||||
|
const [selectedUsecase, setSelectedUsecase] = React.useState(selectedOption !== undefined ? selectedOption : "Phishing")
|
||||||
|
|
||||||
const scale = size === undefined ? 1 : size > 5 ? 3 : size
|
const scale = size === undefined ? 1 : size > 5 ? 3 : size
|
||||||
|
|
||||||
const alert = useAlert()
|
const alert = useAlert()
|
||||||
|
|
||||||
|
const showRecommendations = (changed, frameworkData) => {
|
||||||
|
console.log("Inside recommendation loader")
|
||||||
|
setChangedApp(changed)
|
||||||
|
|
||||||
|
// FIX:
|
||||||
|
// 0. Get workflows loaded in from usecasesearch
|
||||||
|
// 1. Search through workflow templates for matching app types
|
||||||
|
// 2. Validate if template is already in use~ (workflows with same tools)
|
||||||
|
// 3. Generate the workflow(s) - PS: Fix new workflow templates
|
||||||
|
// 4. Moving on!
|
||||||
|
|
||||||
|
// How can we load templates? UsecaseSearch?
|
||||||
|
var showusecases = []
|
||||||
|
//const foundusecase = usecaseTypes.find(data => data.name.toLowerCase() === defaultSearch.toLowerCase())
|
||||||
|
for (var key in usecaseTypes) {
|
||||||
|
for (var subkey in usecaseTypes[key].value) {
|
||||||
|
const usecase = usecaseTypes[key].value[subkey]
|
||||||
|
|
||||||
|
if (usecase.active === false) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var potential = false
|
||||||
|
var matches = []
|
||||||
|
for (var itemtype in usecase.items) {
|
||||||
|
const apptype = usecase.items[itemtype].app_type.toLowerCase()
|
||||||
|
//console.log("OLD: ", changed, "USECASE: ", apptype)
|
||||||
|
|
||||||
|
if (changed.toLowerCase() === apptype || changed.toLowerCase().includes(apptype)) {
|
||||||
|
potential = true
|
||||||
|
|
||||||
|
if (frameworkData[apptype].name !== undefined && frameworkData[apptype].name !== null && frameworkData[apptype].name.length > 0) {
|
||||||
|
usecase.items[itemtype].app = frameworkData[apptype]
|
||||||
|
}
|
||||||
|
|
||||||
|
matches.push(usecase.items[itemtype])
|
||||||
|
} else {
|
||||||
|
// Check if the type is done in frameworkData
|
||||||
|
if (frameworkData[apptype] !== undefined) {
|
||||||
|
//console.log("NOT UNDEFINED: ", frameworkData[apptype])
|
||||||
|
if (frameworkData[apptype].name !== undefined && frameworkData[apptype].name !== null && frameworkData[apptype].name.length > 0) {
|
||||||
|
console.log("FOUND: ", frameworkData[apptype])
|
||||||
|
usecase.items[itemtype].app = frameworkData[apptype]
|
||||||
|
|
||||||
|
//console.log("Real app!")
|
||||||
|
matches.push(usecase.items[itemtype])
|
||||||
|
}
|
||||||
|
|
||||||
|
//if (frameworkData[apptype] !== undefined) {
|
||||||
|
} else {
|
||||||
|
console.log("UNDEFINED APP (bad name?): ", apptype)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (potential) {
|
||||||
|
if (matches.length === usecase.items.length) {
|
||||||
|
usecase.color = "#c51152"
|
||||||
|
usecase.type = usecaseTypes[key].name
|
||||||
|
showusecases.push(usecase)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: Check if a usecase has already been handled
|
||||||
|
console.log("")
|
||||||
|
console.log("GOT USECASES: ", showusecases)
|
||||||
|
|
||||||
|
// FIXME: Just showing one usecase at a time for now
|
||||||
|
if (showusecases.length > 0) {
|
||||||
|
setFrameworkSuggestions(showusecases.slice(0,1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
//console.log("DISCWRAP CHANG: ", discoveryWrapper)
|
console.log("DISCWRAP CHANG: ", discoveryWrapper)
|
||||||
if (discoveryWrapper === undefined || discoveryWrapper.id === "SHUFFLE" || discoveryWrapper.id === undefined || cy === undefined) {
|
if (discoveryWrapper === undefined || discoveryWrapper.id === "SHUFFLE" || discoveryWrapper.id === undefined || cy === undefined) {
|
||||||
setDiscoveryData({})
|
setDiscoveryData({})
|
||||||
|
|
||||||
@@ -548,26 +633,30 @@ const Framework = (props) => {
|
|||||||
|
|
||||||
|
|
||||||
// Find the node and click it?
|
// Find the node and click it?
|
||||||
setTimeout(() => {
|
//setTimeout(() => {
|
||||||
const nodes = cy.nodes().jsons()
|
const nodes = cy.nodes().jsons()
|
||||||
for (var key in nodes) {
|
for (var key in nodes) {
|
||||||
const node = nodes[key]
|
const node = nodes[key]
|
||||||
var newSearchName = discoveryWrapper.id.valueOf()
|
var newSearchName = discoveryWrapper.id.valueOf()
|
||||||
if (newSearchName === "EMAIL") {
|
if (newSearchName === "EMAIL") {
|
||||||
newSearchName = "COMMS"
|
newSearchName = "COMMS"
|
||||||
}
|
|
||||||
|
|
||||||
if (node.data.id === newSearchName) {
|
|
||||||
const tmpnode = cy.getElementById(node.data.id)
|
|
||||||
if (tmpnode !== undefined) {
|
|
||||||
tmpnode.select()
|
|
||||||
}
|
|
||||||
|
|
||||||
setDefaultSearch(discoveryWrapper.id)
|
|
||||||
setPaperTitle(discoveryWrapper.id)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}, 50,)
|
|
||||||
|
if (newSearchName === "ERADICATION" || newSearchName === "ENDPOINT") {
|
||||||
|
newSearchName = "EDR & AV"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.data.id === newSearchName) {
|
||||||
|
const tmpnode = cy.getElementById(node.data.id)
|
||||||
|
if (tmpnode !== undefined) {
|
||||||
|
tmpnode.select()
|
||||||
|
}
|
||||||
|
|
||||||
|
setDefaultSearch(discoveryWrapper.id)
|
||||||
|
setPaperTitle(discoveryWrapper.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//}, 50,)
|
||||||
//setDiscoveryData(discoveryWrapper)
|
//setDiscoveryData(discoveryWrapper)
|
||||||
}, [discoveryWrapper])
|
}, [discoveryWrapper])
|
||||||
|
|
||||||
@@ -652,17 +741,23 @@ const Framework = (props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (discoveryData.id === undefined) {
|
console.log(newSelectedApp, discoveryData)
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newSelectedApp.objectID === undefined || newSelectedApp.objectID === undefined || newSelectedApp.objectID.length === 0) {
|
if (newSelectedApp.objectID === undefined || newSelectedApp.objectID === undefined || newSelectedApp.objectID.length === 0) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (paperTitle.length > 0) {
|
//if (paperTitle.length > 0) {
|
||||||
setDiscoveryWrapper({})
|
// console.log("No papertitle (parent button)")
|
||||||
setSelectionOpen(false)
|
|
||||||
|
// cy.elements().unselect()
|
||||||
|
// return
|
||||||
|
//}
|
||||||
|
|
||||||
|
if (discoveryData.id === undefined) {
|
||||||
|
console.log("No discoverydata (parent button)")
|
||||||
|
|
||||||
|
cy.elements().unselect()
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const submitValue = {
|
const submitValue = {
|
||||||
@@ -683,7 +778,29 @@ const Framework = (props) => {
|
|||||||
foundelement.data("height", `${85*scale}px`)
|
foundelement.data("height", `${85*scale}px`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (setFrameworkData !== undefined) {
|
||||||
|
// Find discoveryData.id
|
||||||
|
var keys = []
|
||||||
|
for (const [key, value] of Object.entries(frameworkData)) {
|
||||||
|
if (key.toLowerCase() === discoveryData.id.toLowerCase()) {
|
||||||
|
keys.push(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keys.length === 0) {
|
||||||
|
console.log("Failed to find: ", discoveryData.id, " IN ", frameworkData)
|
||||||
|
} else {
|
||||||
|
for (var key in keys) {
|
||||||
|
frameworkData[keys[key]] = submitValue
|
||||||
|
}
|
||||||
|
|
||||||
|
setFrameworkData(frameworkData)
|
||||||
|
showRecommendations(discoveryData.id, frameworkData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setFrameworkItem(submitValue)
|
setFrameworkItem(submitValue)
|
||||||
|
cy.elements().unselect()
|
||||||
|
|
||||||
}, [newSelectedApp])
|
}, [newSelectedApp])
|
||||||
|
|
||||||
@@ -792,8 +909,6 @@ const Framework = (props) => {
|
|||||||
//console.log("Framework - update? ", parsedFrameworkData)
|
//console.log("Framework - update? ", parsedFrameworkData)
|
||||||
|
|
||||||
// 0 = automated, 1 = manual
|
// 0 = automated, 1 = manual
|
||||||
const [usecaseType, setUsecaseType] = React.useState(0)
|
|
||||||
const [selectedUsecase, setSelectedUsecase] = React.useState(selectedOption !== undefined ? selectedOption : "Phishing")
|
|
||||||
|
|
||||||
const elements = []
|
const elements = []
|
||||||
const surfaceColor = "#27292D"
|
const surfaceColor = "#27292D"
|
||||||
@@ -966,14 +1081,60 @@ const Framework = (props) => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const onNodeUnselect = (event) => {
|
||||||
|
var data = event.target.data();
|
||||||
|
console.log("UNSELECT: ", data)
|
||||||
|
|
||||||
|
var parsedStyle = {
|
||||||
|
"border-width": "10px",
|
||||||
|
"border-opacity": ".7",
|
||||||
|
"border-color": "#7fe57f",
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.target !== undefined && event.target !== null) {
|
||||||
|
event.target.animate(
|
||||||
|
{
|
||||||
|
style: parsedStyle,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
duration: animationDuration,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
event.target.animate(
|
||||||
|
{
|
||||||
|
style: {
|
||||||
|
"border-width": "3px",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
duration: animationDuration,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}, 2500)
|
||||||
|
}
|
||||||
|
|
||||||
|
//setDiscoveryData({})
|
||||||
|
setDiscoveryWrapper({})
|
||||||
|
setSelectionOpen(false)
|
||||||
|
setDefaultSearch("")
|
||||||
|
setPaperTitle("")
|
||||||
|
|
||||||
|
//setDiscoveryData({})
|
||||||
|
}
|
||||||
|
|
||||||
const onNodeSelect = (event) => {
|
const onNodeSelect = (event) => {
|
||||||
const data = event.target.data();
|
var data = event.target.data();
|
||||||
console.log("Node: ", data)
|
console.log("Node: ", data)
|
||||||
if (data.id === "SHUFFLE") {
|
if (data.id === "SHUFFLE") {
|
||||||
event.target.unselect()
|
event.target.unselect()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (data.label === "EDR & AV") {
|
||||||
|
data.label = "ERADICATION"
|
||||||
|
}
|
||||||
|
|
||||||
setDiscoveryData(data)
|
setDiscoveryData(data)
|
||||||
setSelectionOpen(true)
|
setSelectionOpen(true)
|
||||||
@@ -1009,6 +1170,9 @@ const Framework = (props) => {
|
|||||||
cy.on("select", "node", (e) => {
|
cy.on("select", "node", (e) => {
|
||||||
onNodeSelect(e)
|
onNodeSelect(e)
|
||||||
})
|
})
|
||||||
|
cy.on("unselect", "node", (e) => {
|
||||||
|
onNodeUnselect(e)
|
||||||
|
})
|
||||||
|
|
||||||
cy.on("mouseover", "node", (e) => {onNodeHover(e)})
|
cy.on("mouseover", "node", (e) => {onNodeHover(e)})
|
||||||
cy.on("mouseout", "node", (e) => onNodeHoverOut(e));
|
cy.on("mouseout", "node", (e) => onNodeHoverOut(e));
|
||||||
@@ -1547,8 +1711,21 @@ const Framework = (props) => {
|
|||||||
var usecasediff = -100
|
var usecasediff = -100
|
||||||
const bgColor = color === undefined || color === null || color.length === 0 ? theme.palette.surfaceColor : color
|
const bgColor = color === undefined || color === null || color.length === 0 ? theme.palette.surfaceColor : color
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{margin: "auto", backgroundColor: bgColor, position: "relative", }}>
|
<div style={{margin: "auto", backgroundColor: bgColor, position: "relative", }}>
|
||||||
|
<div style={{position: "absolute"}}>
|
||||||
|
|
||||||
|
<SuggestedWorkflows
|
||||||
|
globalUrl={globalUrl}
|
||||||
|
userdata={userdata}
|
||||||
|
frameworkData={frameworkData}
|
||||||
|
usecaseSuggestions={frameworkSuggestions}
|
||||||
|
setUsecaseSuggestions={setFrameworkSuggestions}
|
||||||
|
inputSearch={changedApp}
|
||||||
|
apps={apps}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
{showOptions === false ? null :
|
{showOptions === false ? null :
|
||||||
<div style={{textAlign: "center",}}>
|
<div style={{textAlign: "center",}}>
|
||||||
{Object.keys(usecases).map((data, index) => {
|
{Object.keys(usecases).map((data, index) => {
|
||||||
@@ -1580,7 +1757,7 @@ const Framework = (props) => {
|
|||||||
|
|
||||||
{
|
{
|
||||||
Object.getOwnPropertyNames(discoveryData).length > 0 ?
|
Object.getOwnPropertyNames(discoveryData).length > 0 ?
|
||||||
<Paper style={{width: 275, maxHeight: 400, overflow: "hidden", zIndex: 12500, padding: 25, paddingRight: 35, backgroundColor: theme.palette.surfaceColor, border: "1px solid rgba(255,255,255,0.2)", position: "absolute", top: 25, left: 50, }}>
|
<Paper style={{width: 275, maxHeight: 400, overflow: "hidden", zIndex: 12500, padding: 25, paddingRight: 35, backgroundColor: theme.palette.surfaceColor, border: "1px solid rgba(255,255,255,0.2)", position: "absolute", top: -50, left: 50, }}>
|
||||||
{paperTitle.length > 0 ?
|
{paperTitle.length > 0 ?
|
||||||
<span>
|
<span>
|
||||||
<Typography variant="h6" style={{textAlign: "center"}}>
|
<Typography variant="h6" style={{textAlign: "center"}}>
|
||||||
@@ -1718,6 +1895,8 @@ const Framework = (props) => {
|
|||||||
defaultSearch={defaultSearch}
|
defaultSearch={defaultSearch}
|
||||||
newSelectedApp={newSelectedApp}
|
newSelectedApp={newSelectedApp}
|
||||||
setNewSelectedApp={setNewSelectedApp}
|
setNewSelectedApp={setNewSelectedApp}
|
||||||
|
userdata={userdata}
|
||||||
|
cy={cy}
|
||||||
/>
|
/>
|
||||||
: null}
|
: null}
|
||||||
</div>
|
</div>
|
||||||
@@ -10,10 +10,11 @@ import { Search as SearchIcon, CloudQueue as CloudQueueIcon, Code as CodeIcon }
|
|||||||
import algoliasearch from 'algoliasearch';
|
import algoliasearch from 'algoliasearch';
|
||||||
import { InstantSearch, connectSearchBox, connectHits } from 'react-instantsearch-dom';
|
import { InstantSearch, connectSearchBox, connectHits } from 'react-instantsearch-dom';
|
||||||
import { Grid, Paper, TextField, ButtonBase, InputAdornment, Typography, Button, Tooltip} from '@material-ui/core';
|
import { Grid, Paper, TextField, ButtonBase, InputAdornment, Typography, Button, Tooltip} from '@material-ui/core';
|
||||||
|
import aa from 'search-insights'
|
||||||
|
|
||||||
const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240")
|
const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240")
|
||||||
const WorkflowSearch = props => {
|
const Appsearch = props => {
|
||||||
const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs, newSelectedApp, setNewSelectedApp, defaultSearch, showSearch, ConfiguredHits } = props
|
const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs, newSelectedApp, setNewSelectedApp, defaultSearch, showSearch, ConfiguredHits, userdata, cy, } = 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";
|
||||||
|
|
||||||
@@ -140,19 +141,19 @@ const WorkflowSearch = props => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
counted += 1
|
counted += 1
|
||||||
var parsedname = ""
|
var parsedname = data.name.valueOf()
|
||||||
for (var key = 0; key < data.name.length; key++) {
|
//for (var key = 0; key < data.name.length; key++) {
|
||||||
var character = data.name.charAt(key)
|
// var character = data.name.charAt(key)
|
||||||
if (character === character.toUpperCase()) {
|
// if (character === character.toUpperCase()) {
|
||||||
//console.log(data.name[key], data.name[key+1])
|
// //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()) {
|
// if (data.name.charAt(key+1) !== undefined && data.name.charAt(key+1) === data.name.charAt(key+1).toUpperCase()) {
|
||||||
} else {
|
// } else {
|
||||||
parsedname += " "
|
// parsedname += " "
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
parsedname += character
|
// parsedname += character
|
||||||
}
|
//}
|
||||||
|
|
||||||
parsedname = (parsedname.charAt(0).toUpperCase()+parsedname.substring(1)).replaceAll("_", " ")
|
parsedname = (parsedname.charAt(0).toUpperCase()+parsedname.substring(1)).replaceAll("_", " ")
|
||||||
|
|
||||||
@@ -180,6 +181,32 @@ const WorkflowSearch = props => {
|
|||||||
label: "",
|
label: "",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const queryID = ""
|
||||||
|
|
||||||
|
if (queryID !== undefined && queryID !== null) {
|
||||||
|
try {
|
||||||
|
aa('init', {
|
||||||
|
appId: searchClient.appId,
|
||||||
|
apiKey: searchClient.transporter.headers["x-algolia-api-key"]
|
||||||
|
})
|
||||||
|
|
||||||
|
const timestamp = new Date().getTime()
|
||||||
|
aa('sendEvents', [
|
||||||
|
{
|
||||||
|
eventType: 'conversion',
|
||||||
|
eventName: 'App Framework Activation',
|
||||||
|
index: 'appsearch',
|
||||||
|
objectIDs: [data.objectID],
|
||||||
|
timestamp: timestamp,
|
||||||
|
queryID: queryID,
|
||||||
|
userToken: userdata === undefined || userdata === null || userdata.id === undefined ? "unauthenticated" : userdata.id,
|
||||||
|
}
|
||||||
|
])
|
||||||
|
} catch (e) {
|
||||||
|
console.log("Failed algolia search update: ", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
}}>
|
}}>
|
||||||
<div style={{display: "flex"}}>
|
<div style={{display: "flex"}}>
|
||||||
<img alt={data.name} src={data.image_url} style={{width: "100%", maxWidth: 30, minWidth: 30, minHeight: 30, maxHeight: 30, display: "block", }} />
|
<img alt={data.name} src={data.image_url} style={{width: "100%", maxWidth: 30, minWidth: 30, minHeight: 30, maxHeight: 30, display: "block", }} />
|
||||||
@@ -215,4 +242,4 @@ const WorkflowSearch = props => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default WorkflowSearch;
|
export default Appsearch;
|
||||||
|
|||||||
@@ -0,0 +1,189 @@
|
|||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
|
||||||
|
import theme from '../theme';
|
||||||
|
import AppSearch from './Appsearch.jsx';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Paper,
|
||||||
|
Typography,
|
||||||
|
Divider,
|
||||||
|
IconButton,
|
||||||
|
Badge,
|
||||||
|
CircularProgress,
|
||||||
|
Tooltip,
|
||||||
|
Button,
|
||||||
|
} from "@material-ui/core";
|
||||||
|
|
||||||
|
import {
|
||||||
|
Close as CloseIcon,
|
||||||
|
Delete as DeleteIcon,
|
||||||
|
} from "@material-ui/icons";
|
||||||
|
|
||||||
|
const AppSearchPopout = (props) => {
|
||||||
|
const {
|
||||||
|
cy,
|
||||||
|
paperTitle,
|
||||||
|
setPaperTitle,
|
||||||
|
newSelectedApp,
|
||||||
|
setNewSelectedApp,
|
||||||
|
selectionOpen,
|
||||||
|
setSelectionOpen,
|
||||||
|
discoveryData,
|
||||||
|
setDiscoveryData,
|
||||||
|
userdata,
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
const [defaultSearch, setDefaultSearch] = React.useState(paperTitle !== undefined ? paperTitle : "")
|
||||||
|
|
||||||
|
if (selectionOpen !== true) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Paper style={{width: 275, maxHeight: 400, zIndex: 12500, padding: 25, paddingRight: 35, backgroundColor: theme.palette.surfaceColor, border: "1px solid rgba(255,255,255,0.2)", position: "absolute", top: -15, left: 50, overflow: "hidden", }}>
|
||||||
|
{paperTitle !== undefined && paperTitle.length > 0 ?
|
||||||
|
<span>
|
||||||
|
<Typography variant="h6" style={{textAlign: "center"}}>
|
||||||
|
{paperTitle}
|
||||||
|
</Typography>
|
||||||
|
<Divider style={{marginTop: 5, marginBottom: 5 }} />
|
||||||
|
</span>
|
||||||
|
: null}
|
||||||
|
<Tooltip
|
||||||
|
title="Close window"
|
||||||
|
placement="top"
|
||||||
|
style={{ zIndex: 10011 }}
|
||||||
|
>
|
||||||
|
<IconButton
|
||||||
|
style={{ zIndex: 12501, position: "absolute", top: 10, right: 10}}
|
||||||
|
onClick={(e) => {
|
||||||
|
//cy.elements().unselectify();
|
||||||
|
if (cy !== undefined) {
|
||||||
|
cy.elements().unselect()
|
||||||
|
}
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
setSelectionOpen(false)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CloseIcon style={{ color: "white", height: 15, width: 15, }} />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
{/* {/*Causes errors in Cytoscape. Removing for now.}
|
||||||
|
<Tooltip
|
||||||
|
title="Unselect app"
|
||||||
|
placement="top"
|
||||||
|
style={{ zIndex: 10011 }}
|
||||||
|
>
|
||||||
|
<IconButton
|
||||||
|
style={{ zIndex: 12501, position: "absolute", top: 32, right: 10}}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setDiscoveryData({
|
||||||
|
"id": discoveryData.id,
|
||||||
|
"label": discoveryData.label,
|
||||||
|
"name": ""
|
||||||
|
})
|
||||||
|
setNewSelectedApp({
|
||||||
|
"image_url": "",
|
||||||
|
"name": "",
|
||||||
|
"id": "",
|
||||||
|
"objectID": "remove",
|
||||||
|
})
|
||||||
|
setSelectionOpen(true)
|
||||||
|
setDefaultSearch("")
|
||||||
|
|
||||||
|
const foundelement = cy.getElementById(discoveryData.id)
|
||||||
|
if (foundelement !== undefined && foundelement !== null) {
|
||||||
|
console.log("element: ", foundelement)
|
||||||
|
foundelement.data("large_image", discoveryData.large_image)
|
||||||
|
foundelement.data("text_margin_y", "14px")
|
||||||
|
foundelement.data("margin_x", "32px")
|
||||||
|
foundelement.data("margin_y", "19x")
|
||||||
|
foundelement.data("width", "45px")
|
||||||
|
foundelement.data("height", "45px")
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
setDiscoveryData({})
|
||||||
|
setNewSelectedApp({})
|
||||||
|
}, 1000)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DeleteIcon style={{ color: "white", height: 15, width: 15, }} />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
*/}
|
||||||
|
<div style={{display: "flex"}}>
|
||||||
|
{discoveryData.name !== undefined && discoveryData.name !== null && discoveryData.name.length > 0 ?
|
||||||
|
<div style={{border: "1px solid rgba(255,255,255,0.2)", borderRadius: 25, height: 40, width: 40, textAlign: "center", overflow: "hidden",}}>
|
||||||
|
|
||||||
|
<img alt={discoveryData.id} src={newSelectedApp.image_url !== undefined && newSelectedApp.image_url !== null && newSelectedApp.image_url.length > 0 ? newSelectedApp.image_url : discoveryData.large_image} style={{height: 40, width: 40, margin: "auto",}}/>
|
||||||
|
</div>
|
||||||
|
:
|
||||||
|
<img alt={discoveryData.id} src={discoveryData.large_image} style={{height: 40,}}/>
|
||||||
|
}
|
||||||
|
<Typography variant="body1" style={{marginLeft: 10, marginTop: 6}}>
|
||||||
|
{discoveryData.name !== undefined && discoveryData.name !== null && discoveryData.name.length > 0 ?
|
||||||
|
discoveryData.name
|
||||||
|
:
|
||||||
|
newSelectedApp.name !== undefined && newSelectedApp.name !== null && newSelectedApp.name.length > 0 ?
|
||||||
|
newSelectedApp.name
|
||||||
|
:
|
||||||
|
`No ${discoveryData.label} app chosen`
|
||||||
|
}
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{discoveryData !== undefined && discoveryData.name !== undefined && discoveryData.name !== null && discoveryData.name.length > 0 ?
|
||||||
|
<span>
|
||||||
|
<Typography variant="body2" color="textSecondary" style={{marginTop: 10, marginBottom: 10, maxHeight: 75, overflowY: "auto", overflowX: "hidden", }}>
|
||||||
|
{discoveryData.description}
|
||||||
|
</Typography>
|
||||||
|
{/*isCloud && defaultSearch !== undefined && defaultSearch.length > 0 ?
|
||||||
|
{<
|
||||||
|
newSelectedApp={newSelectedApp}
|
||||||
|
setNewSelectedApp={setNewSelectedApp}
|
||||||
|
defaultSearch={defaultSearch}
|
||||||
|
/>}
|
||||||
|
:
|
||||||
|
null
|
||||||
|
*/}
|
||||||
|
</span>
|
||||||
|
:
|
||||||
|
selectionOpen
|
||||||
|
?
|
||||||
|
<span>
|
||||||
|
<Typography variant="body2" color="textSecondary" style={{marginTop: 10}}>
|
||||||
|
Click an app below to select it
|
||||||
|
</Typography>
|
||||||
|
</span>
|
||||||
|
:
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
style={{marginTop: 10, }}
|
||||||
|
onClick={() => {
|
||||||
|
setSelectionOpen(true)
|
||||||
|
setDefaultSearch(discoveryData.label)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Choose {discoveryData.label} app
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<div style={{marginTop: 10}}>
|
||||||
|
{selectionOpen ?
|
||||||
|
<AppSearch
|
||||||
|
defaultSearch={defaultSearch}
|
||||||
|
newSelectedApp={newSelectedApp}
|
||||||
|
setNewSelectedApp={setNewSelectedApp}
|
||||||
|
userdata={userdata}
|
||||||
|
/>
|
||||||
|
: null}
|
||||||
|
</div>
|
||||||
|
</Paper>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AppSearchPopout;
|
||||||
@@ -0,0 +1,278 @@
|
|||||||
|
import React, { useState, useEffect } from "react";
|
||||||
|
|
||||||
|
import theme from '../theme';
|
||||||
|
import { useAlert } from "react-alert";
|
||||||
|
import {
|
||||||
|
Tooltip,
|
||||||
|
IconButton,
|
||||||
|
ListItem,
|
||||||
|
ListItemText,
|
||||||
|
FormGroup,
|
||||||
|
FormControl,
|
||||||
|
InputLabel,
|
||||||
|
FormLabel,
|
||||||
|
FormControlLabel,
|
||||||
|
Select,
|
||||||
|
MenuItem,
|
||||||
|
Grid,
|
||||||
|
Paper,
|
||||||
|
Typography,
|
||||||
|
TextField,
|
||||||
|
Zoom,
|
||||||
|
} from "@material-ui/core";
|
||||||
|
|
||||||
|
import {
|
||||||
|
Edit as EditIcon,
|
||||||
|
Delete as DeleteIcon,
|
||||||
|
SelectAll as SelectAllIcon,
|
||||||
|
} from "@material-ui/icons";
|
||||||
|
|
||||||
|
const AuthenticationItem = (props) => {
|
||||||
|
const { data, index, globalUrl, getAppAuthentication } = props
|
||||||
|
|
||||||
|
const [selectedAuthentication, setSelectedAuthentication] = React.useState({})
|
||||||
|
const [selectedAuthenticationModalOpen, setSelectedAuthenticationModalOpen] = React.useState(false);
|
||||||
|
const [authenticationFields, setAuthenticationFields] = React.useState([]);
|
||||||
|
|
||||||
|
const alert = useAlert();
|
||||||
|
var bgColor = "#27292d";
|
||||||
|
if (index % 2 === 0) {
|
||||||
|
bgColor = "#1f2023";
|
||||||
|
}
|
||||||
|
|
||||||
|
//console.log("Auth data: ", data)
|
||||||
|
if (data.type === "oauth2") {
|
||||||
|
data.fields = [
|
||||||
|
{
|
||||||
|
key: "url",
|
||||||
|
value: "Secret. Replaced during app execution!",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "client_id",
|
||||||
|
value: "Secret. Replaced during app execution!",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "client_secret",
|
||||||
|
value: "Secret. Replaced during app execution!",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "scope",
|
||||||
|
value: "Secret. Replaced during app execution!",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteAuthentication = (data) => {
|
||||||
|
alert.info("Deleting auth " + data.label);
|
||||||
|
|
||||||
|
// Just use this one?
|
||||||
|
const url = globalUrl + "/api/v1/apps/authentication/" + data.id;
|
||||||
|
console.log("URL: ", url);
|
||||||
|
fetch(url, {
|
||||||
|
method: "DELETE",
|
||||||
|
credentials: "include",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((response) =>
|
||||||
|
response.json().then((responseJson) => {
|
||||||
|
console.log("RESP: ", responseJson);
|
||||||
|
if (responseJson["success"] === false) {
|
||||||
|
alert.error("Failed deleting auth");
|
||||||
|
} else {
|
||||||
|
// Need to wait because query in ES is too fast
|
||||||
|
setTimeout(() => {
|
||||||
|
getAppAuthentication();
|
||||||
|
}, 1000);
|
||||||
|
//alert.success("Successfully deleted authentication!")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.catch((error) => {
|
||||||
|
console.log("Error in userdata: ", error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const editAuthenticationConfig = (id) => {
|
||||||
|
const data = {
|
||||||
|
id: id,
|
||||||
|
action: "assign_everywhere",
|
||||||
|
};
|
||||||
|
const url = globalUrl + "/api/v1/apps/authentication/" + id + "/config";
|
||||||
|
|
||||||
|
fetch(url, {
|
||||||
|
mode: "cors",
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify(data),
|
||||||
|
credentials: "include",
|
||||||
|
crossDomain: true,
|
||||||
|
withCredentials: true,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json; charset=utf-8",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((response) =>
|
||||||
|
response.json().then((responseJson) => {
|
||||||
|
if (responseJson["success"] === false) {
|
||||||
|
alert.error("Failed overwriting appauth in workflows");
|
||||||
|
} else {
|
||||||
|
alert.success("Successfully updated auth everywhere!");
|
||||||
|
//setSelectedUserModalOpen(false);
|
||||||
|
setTimeout(() => {
|
||||||
|
getAppAuthentication();
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.catch((error) => {
|
||||||
|
alert.error("Err: " + error.toString());
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateAppAuthentication = (field) => {
|
||||||
|
setSelectedAuthenticationModalOpen(true);
|
||||||
|
setSelectedAuthentication(field);
|
||||||
|
//{selectedAuthentication.fields.map((data, index) => {
|
||||||
|
var newfields = [];
|
||||||
|
for (var key in field.fields) {
|
||||||
|
newfields.push({
|
||||||
|
key: field.fields[key].key,
|
||||||
|
value: "",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
setAuthenticationFields(newfields);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ListItem key={index} style={{ backgroundColor: bgColor }}>
|
||||||
|
<ListItemText
|
||||||
|
primary=<img
|
||||||
|
alt=""
|
||||||
|
src={data.app.large_image}
|
||||||
|
style={{
|
||||||
|
maxWidth: 50,
|
||||||
|
borderRadius: theme.palette.borderRadius,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
style={{ minWidth: 75, maxWidth: 75 }}
|
||||||
|
/>
|
||||||
|
<ListItemText
|
||||||
|
primary={data.label}
|
||||||
|
style={{
|
||||||
|
minWidth: 225,
|
||||||
|
maxWidth: 225,
|
||||||
|
overflow: "hidden",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<ListItemText
|
||||||
|
primary={data.app.name}
|
||||||
|
style={{ minWidth: 175, maxWidth: 175, marginLeft: 10 }}
|
||||||
|
/>
|
||||||
|
{/*
|
||||||
|
<ListItemText
|
||||||
|
primary={data.defined === false ? "No" : "Yes"}
|
||||||
|
style={{ minWidth: 100, maxWidth: 100, }}
|
||||||
|
/>
|
||||||
|
*/}
|
||||||
|
<ListItemText
|
||||||
|
primary={
|
||||||
|
data.workflow_count === null ? 0 : data.workflow_count
|
||||||
|
}
|
||||||
|
style={{
|
||||||
|
minWidth: 100,
|
||||||
|
maxWidth: 100,
|
||||||
|
textAlign: "center",
|
||||||
|
overflow: "hidden",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{/*
|
||||||
|
<ListItemText
|
||||||
|
primary={data.node_count}
|
||||||
|
style={{
|
||||||
|
minWidth: 110,
|
||||||
|
maxWidth: 110,
|
||||||
|
textAlign: "center",
|
||||||
|
overflow: "hidden",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
*/}
|
||||||
|
<ListItemText
|
||||||
|
primary={
|
||||||
|
data.fields === null || data.fields === undefined
|
||||||
|
? ""
|
||||||
|
: data.fields
|
||||||
|
.map((data) => {
|
||||||
|
return data.key;
|
||||||
|
})
|
||||||
|
.join(", ")
|
||||||
|
}
|
||||||
|
style={{
|
||||||
|
minWidth: 125,
|
||||||
|
maxWidth: 125,
|
||||||
|
overflow: "hidden",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<ListItemText
|
||||||
|
style={{
|
||||||
|
maxWidth: 230,
|
||||||
|
minWidth: 230,
|
||||||
|
overflow: "hidden",
|
||||||
|
}}
|
||||||
|
primary={new Date(data.created * 1000).toISOString()}
|
||||||
|
/>
|
||||||
|
<ListItemText>
|
||||||
|
<IconButton
|
||||||
|
onClick={() => {
|
||||||
|
updateAppAuthentication(data);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<EditIcon color="primary" />
|
||||||
|
</IconButton>
|
||||||
|
{data.defined ? (
|
||||||
|
<Tooltip
|
||||||
|
color="primary"
|
||||||
|
title="Set in EVERY workflow"
|
||||||
|
placement="top"
|
||||||
|
>
|
||||||
|
<IconButton
|
||||||
|
style={{ marginRight: 10 }}
|
||||||
|
disabled={data.defined === false}
|
||||||
|
onClick={() => {
|
||||||
|
editAuthenticationConfig(data.id);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<SelectAllIcon
|
||||||
|
color={data.defined ? "primary" : "secondary"}
|
||||||
|
/>
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
) : (
|
||||||
|
<Tooltip
|
||||||
|
color="primary"
|
||||||
|
title="Must edit before you can set in all workflows"
|
||||||
|
placement="top"
|
||||||
|
>
|
||||||
|
<IconButton
|
||||||
|
style={{ marginRight: 10 }}
|
||||||
|
onClick={() => {}}
|
||||||
|
>
|
||||||
|
<SelectAllIcon
|
||||||
|
color={data.defined ? "primary" : "secondary"}
|
||||||
|
/>
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
<IconButton
|
||||||
|
onClick={() => {
|
||||||
|
deleteAuthentication(data);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DeleteIcon color="primary" />
|
||||||
|
</IconButton>
|
||||||
|
</ListItemText>
|
||||||
|
</ListItem>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AuthenticationItem
|
||||||
@@ -0,0 +1,366 @@
|
|||||||
|
import React, { useState, useEffect } from "react";
|
||||||
|
import theme from '../theme';
|
||||||
|
import { v4 as uuidv4 } from "uuid";
|
||||||
|
|
||||||
|
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Divider,
|
||||||
|
Select,
|
||||||
|
MenuItem,
|
||||||
|
TextField,
|
||||||
|
DialogActions,
|
||||||
|
DialogTitle,
|
||||||
|
DialogContent,
|
||||||
|
Typography,
|
||||||
|
} from "@material-ui/core";
|
||||||
|
|
||||||
|
import {
|
||||||
|
LockOpen as LockOpenIcon,
|
||||||
|
} from "@material-ui/icons";
|
||||||
|
|
||||||
|
const AuthenticationData = (props) => {
|
||||||
|
const {
|
||||||
|
globalUrl,
|
||||||
|
saveWorkflow,
|
||||||
|
selectedApp,
|
||||||
|
workflow,
|
||||||
|
selectedAction,
|
||||||
|
authenticationType,
|
||||||
|
getAppAuthentication,
|
||||||
|
appAuthentication,
|
||||||
|
setSelectedAction,
|
||||||
|
setAuthenticationModalOpen,
|
||||||
|
isCloud,
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
const setNewAppAuth = (appAuthData) => {
|
||||||
|
console.log("DAta: ", appAuthData);
|
||||||
|
fetch(globalUrl + "/api/v1/apps/authentication", {
|
||||||
|
method: "PUT",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
Accept: "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify(appAuthData),
|
||||||
|
credentials: "include",
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
if (response.status !== 200) {
|
||||||
|
console.log("Status not 200 for setting app auth :O!");
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then((responseJson) => {
|
||||||
|
if (!responseJson.success) {
|
||||||
|
alert.error("Failed to set app auth: " + responseJson.reason);
|
||||||
|
} else {
|
||||||
|
if (getAppAuthentication !== undefined) {
|
||||||
|
getAppAuthentication()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setAuthenticationModalOpen !== undefined) {
|
||||||
|
setAuthenticationModalOpen(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Needs a refresh with the new authentication..
|
||||||
|
//alert.success("Successfully saved new app auth")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
//alert.error(error.toString());
|
||||||
|
console.log("New auth error: ", error.toString());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const [authenticationOption, setAuthenticationOptions] = React.useState({
|
||||||
|
app: JSON.parse(JSON.stringify(selectedApp)),
|
||||||
|
fields: {},
|
||||||
|
label: "",
|
||||||
|
usage: [
|
||||||
|
{
|
||||||
|
workflow_id: workflow.id,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
id: uuidv4(),
|
||||||
|
active: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (
|
||||||
|
selectedApp.authentication === undefined ||
|
||||||
|
selectedApp.authentication.parameters === null ||
|
||||||
|
selectedApp.authentication.parameters === undefined ||
|
||||||
|
selectedApp.authentication.parameters.length === 0
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
<DialogContent style={{ textAlign: "center", marginTop: 50 }}>
|
||||||
|
<Typography variant="h4" id="draggable-dialog-title" style={{cursor: "move",}}>
|
||||||
|
{selectedApp.name} does not require authentication
|
||||||
|
</Typography>
|
||||||
|
</DialogContent>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
authenticationOption.app.actions = [];
|
||||||
|
|
||||||
|
for (var key in selectedApp.authentication.parameters) {
|
||||||
|
if (
|
||||||
|
authenticationOption.fields[
|
||||||
|
selectedApp.authentication.parameters[key].name
|
||||||
|
] === undefined
|
||||||
|
) {
|
||||||
|
authenticationOption.fields[
|
||||||
|
selectedApp.authentication.parameters[key].name
|
||||||
|
] = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSubmitCheck = () => {
|
||||||
|
console.log("NEW AUTH: ", authenticationOption);
|
||||||
|
if (authenticationOption.label.length === 0) {
|
||||||
|
authenticationOption.label = `Auth for ${selectedApp.name}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Automatically mapping fields that already exist (predefined).
|
||||||
|
// Warning if fields are NOT filled
|
||||||
|
for (var key in selectedApp.authentication.parameters) {
|
||||||
|
if (
|
||||||
|
authenticationOption.fields[
|
||||||
|
selectedApp.authentication.parameters[key].name
|
||||||
|
].length === 0
|
||||||
|
) {
|
||||||
|
if (
|
||||||
|
selectedApp.authentication.parameters[key].value !== undefined &&
|
||||||
|
selectedApp.authentication.parameters[key].value !== null &&
|
||||||
|
selectedApp.authentication.parameters[key].value.length > 0
|
||||||
|
) {
|
||||||
|
authenticationOption.fields[
|
||||||
|
selectedApp.authentication.parameters[key].name
|
||||||
|
] = selectedApp.authentication.parameters[key].value;
|
||||||
|
} else {
|
||||||
|
if (
|
||||||
|
selectedApp.authentication.parameters[key].schema.type === "bool"
|
||||||
|
) {
|
||||||
|
authenticationOption.fields[
|
||||||
|
selectedApp.authentication.parameters[key].name
|
||||||
|
] = "false";
|
||||||
|
} else {
|
||||||
|
alert.info(
|
||||||
|
"Field " +
|
||||||
|
selectedApp.authentication.parameters[key].name +
|
||||||
|
" can't be empty"
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("Action: ", selectedAction);
|
||||||
|
selectedAction.authentication_id = authenticationOption.id;
|
||||||
|
selectedAction.selectedAuthentication = authenticationOption;
|
||||||
|
if (
|
||||||
|
selectedAction.authentication === undefined ||
|
||||||
|
selectedAction.authentication === null
|
||||||
|
) {
|
||||||
|
selectedAction.authentication = [authenticationOption];
|
||||||
|
} else {
|
||||||
|
selectedAction.authentication.push(authenticationOption);
|
||||||
|
}
|
||||||
|
|
||||||
|
setSelectedAction(selectedAction);
|
||||||
|
|
||||||
|
var newAuthOption = JSON.parse(JSON.stringify(authenticationOption));
|
||||||
|
var newFields = [];
|
||||||
|
for (const key in newAuthOption.fields) {
|
||||||
|
const value = newAuthOption.fields[key];
|
||||||
|
newFields.push({
|
||||||
|
key: key,
|
||||||
|
value: value,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("FIELDS: ", newFields);
|
||||||
|
newAuthOption.fields = newFields;
|
||||||
|
setNewAppAuth(newAuthOption);
|
||||||
|
|
||||||
|
//if (configureWorkflowModalOpen) {
|
||||||
|
// setSelectedAction({});
|
||||||
|
//}
|
||||||
|
|
||||||
|
//setUpdate(authenticationOption.id);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (
|
||||||
|
authenticationOption.label === null ||
|
||||||
|
authenticationOption.label === undefined
|
||||||
|
) {
|
||||||
|
authenticationOption.label = selectedApp.name + " authentication";
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<DialogTitle id="draggable-dialog-title" style={{cursor: "move",}}>
|
||||||
|
<div style={{ color: "white" }}>
|
||||||
|
Authentication for {selectedApp.name}
|
||||||
|
</div>
|
||||||
|
</DialogTitle>
|
||||||
|
<DialogContent>
|
||||||
|
<a
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
href="https://shuffler.io/docs/apps#authentication"
|
||||||
|
style={{ textDecoration: "none", color: "#f85a3e" }}
|
||||||
|
>
|
||||||
|
What is app authentication?
|
||||||
|
</a>
|
||||||
|
<div />
|
||||||
|
These are required fields for authenticating with {selectedApp.name}
|
||||||
|
<div style={{ marginTop: 15 }} />
|
||||||
|
<b>Name - what is this used for?</b>
|
||||||
|
<TextField
|
||||||
|
style={{
|
||||||
|
backgroundColor: theme.palette.inputColor,
|
||||||
|
borderRadius: theme.palette.borderRadius,
|
||||||
|
}}
|
||||||
|
InputProps={{
|
||||||
|
style: {
|
||||||
|
color: "white",
|
||||||
|
marginLeft: "5px",
|
||||||
|
maxWidth: "95%",
|
||||||
|
height: 50,
|
||||||
|
fontSize: "1em",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
fullWidth
|
||||||
|
color="primary"
|
||||||
|
placeholder={"Auth july 2020"}
|
||||||
|
defaultValue={`Auth for ${selectedApp.name}`}
|
||||||
|
onChange={(event) => {
|
||||||
|
authenticationOption.label = event.target.value;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Divider
|
||||||
|
style={{
|
||||||
|
marginTop: 15,
|
||||||
|
marginBottom: 15,
|
||||||
|
backgroundColor: "rgb(91, 96, 100)",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div />
|
||||||
|
{selectedApp.authentication.parameters.map((data, index) => {
|
||||||
|
return (
|
||||||
|
<div key={index} style={{ marginTop: 10 }}>
|
||||||
|
<LockOpenIcon style={{ marginRight: 10 }} />
|
||||||
|
<b>{data.name}</b>
|
||||||
|
|
||||||
|
{data.schema !== undefined &&
|
||||||
|
data.schema !== null &&
|
||||||
|
data.schema.type === "bool" ? (
|
||||||
|
<Select
|
||||||
|
MenuProps={{
|
||||||
|
disableScrollLock: true,
|
||||||
|
}}
|
||||||
|
SelectDisplayProps={{
|
||||||
|
style: {
|
||||||
|
marginLeft: 10,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
defaultValue={"false"}
|
||||||
|
fullWidth
|
||||||
|
onChange={(e) => {
|
||||||
|
console.log("Value: ", e.target.value);
|
||||||
|
authenticationOption.fields[data.name] = e.target.value;
|
||||||
|
}}
|
||||||
|
style={{
|
||||||
|
backgroundColor: theme.palette.surfaceColor,
|
||||||
|
color: "white",
|
||||||
|
height: 50,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<MenuItem
|
||||||
|
key={"false"}
|
||||||
|
style={{
|
||||||
|
backgroundColor: theme.palette.inputColor,
|
||||||
|
color: "white",
|
||||||
|
}}
|
||||||
|
value={"false"}
|
||||||
|
>
|
||||||
|
false
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem
|
||||||
|
key={"true"}
|
||||||
|
style={{
|
||||||
|
backgroundColor: theme.palette.inputColor,
|
||||||
|
color: "white",
|
||||||
|
}}
|
||||||
|
value={"true"}
|
||||||
|
>
|
||||||
|
true
|
||||||
|
</MenuItem>
|
||||||
|
</Select>
|
||||||
|
) : (
|
||||||
|
<TextField
|
||||||
|
style={{
|
||||||
|
backgroundColor: theme.palette.inputColor,
|
||||||
|
borderRadius: theme.palette.borderRadius,
|
||||||
|
}}
|
||||||
|
InputProps={{
|
||||||
|
style: {
|
||||||
|
color: "white",
|
||||||
|
marginLeft: "5px",
|
||||||
|
maxWidth: "95%",
|
||||||
|
height: 50,
|
||||||
|
fontSize: "1em",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
fullWidth
|
||||||
|
type={
|
||||||
|
data.example !== undefined && data.example.includes("***")
|
||||||
|
? "password"
|
||||||
|
: "text"
|
||||||
|
}
|
||||||
|
color="primary"
|
||||||
|
defaultValue={
|
||||||
|
data.value !== undefined && data.value !== null
|
||||||
|
? data.value
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
placeholder={data.example}
|
||||||
|
onChange={(event) => {
|
||||||
|
authenticationOption.fields[data.name] =
|
||||||
|
event.target.value;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button
|
||||||
|
style={{ borderRadius: "0px" }}
|
||||||
|
onClick={() => {
|
||||||
|
setAuthenticationModalOpen(false);
|
||||||
|
}}
|
||||||
|
color="primary"
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
style={{ borderRadius: "0px" }}
|
||||||
|
onClick={() => {
|
||||||
|
setAuthenticationOptions(authenticationOption);
|
||||||
|
handleSubmitCheck();
|
||||||
|
}}
|
||||||
|
color="primary"
|
||||||
|
>
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AuthenticationData
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
InputAdornment,
|
InputAdornment,
|
||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
List,
|
List,
|
||||||
ListItem,
|
ListItem,
|
||||||
ListItemText,
|
ListItemText,
|
||||||
|
Fade,
|
||||||
} from "@material-ui/core";
|
} from "@material-ui/core";
|
||||||
import { FavoriteBorder as FavoriteBorderIcon } from "@material-ui/icons";
|
import { FavoriteBorder as FavoriteBorderIcon } from "@material-ui/icons";
|
||||||
import { FixName } from "../views/Apps.jsx";
|
import { FixName } from "../views/Apps.jsx";
|
||||||
@@ -43,14 +44,15 @@ const ConfigureWorkflow = (props) => {
|
|||||||
isCloud,
|
isCloud,
|
||||||
setAuthenticationType,
|
setAuthenticationType,
|
||||||
alert,
|
alert,
|
||||||
|
showTriggers,
|
||||||
} = props;
|
} = props;
|
||||||
const [requiredActions, setRequiredActions] = React.useState([]);
|
const [requiredActions, setRequiredActions] = React.useState([]);
|
||||||
const [requiredVariables, setRequiredVariables] = React.useState([]);
|
const [requiredVariables, setRequiredVariables] = React.useState([]);
|
||||||
const [requiredTriggers, setRequiredTriggers] = React.useState([]);
|
const [requiredTriggers, setRequiredTriggers] = React.useState([]);
|
||||||
const [previousAuth, setPreviousAuth] = React.useState(appAuthentication);
|
const [previousAuth, setPreviousAuth] = React.useState(appAuthentication);
|
||||||
const [firstLoad, setFirstLoad] = React.useState("");
|
|
||||||
const [itemChanged, setItemChanged] = React.useState(false);
|
const [itemChanged, setItemChanged] = React.useState(false);
|
||||||
var finished = false;
|
const [firstLoad, setFirstLoad] = React.useState("");
|
||||||
|
const [showFinalizeAnimation, setShowFinalizeAnimation] = React.useState(false);
|
||||||
|
|
||||||
if (workflow === undefined || workflow === null) {
|
if (workflow === undefined || workflow === null) {
|
||||||
return null;
|
return null;
|
||||||
@@ -94,18 +96,13 @@ const ConfigureWorkflow = (props) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (firstLoad.length === 0 || firstLoad !== workflow.id) {
|
if (firstLoad.length === 0 || firstLoad !== workflow.id) {
|
||||||
if (finished) {
|
|
||||||
setConfigureWorkflowModalOpen(false);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (apps === undefined || apps === null || apps.length === 0) {
|
if (apps === undefined || apps === null || apps.length === 0) {
|
||||||
console.log("No apps loaded: ", apps);
|
console.log("No apps loaded: ", apps);
|
||||||
setConfigureWorkflowModalOpen(false);
|
setConfigureWorkflowModalOpen(false);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
setFirstLoad(workflow.id);
|
setFirstLoad(workflow.id)
|
||||||
const newactions = [];
|
const newactions = [];
|
||||||
for (var key in workflow.actions) {
|
for (var key in workflow.actions) {
|
||||||
const action = workflow.actions[key];
|
const action = workflow.actions[key];
|
||||||
@@ -272,17 +269,19 @@ const ConfigureWorkflow = (props) => {
|
|||||||
setRequiredTriggers(requiredTriggers);
|
setRequiredTriggers(requiredTriggers);
|
||||||
setRequiredVariables(requiredVariables);
|
setRequiredVariables(requiredVariables);
|
||||||
setRequiredActions(newactions);
|
setRequiredActions(newactions);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (appAuthentication.length !== previousAuth.length) {
|
if (appAuthentication.length !== previousAuth.length) {
|
||||||
var newactions = [];
|
var newactions = []
|
||||||
for (var actionkey in requiredActions) {
|
for (var actionkey in requiredActions) {
|
||||||
var newaction = requiredActions[actionkey];
|
var newaction = requiredActions[actionkey];
|
||||||
const app = newaction.app;
|
const app = newaction.app;
|
||||||
|
|
||||||
for (var key in appAuthentication) {
|
for (var key in appAuthentication) {
|
||||||
const auth = appAuthentication[key];
|
const auth = appAuthentication[key];
|
||||||
if (auth.app.name === app.name && auth.active) {
|
|
||||||
|
// Does this account for all the different ones of the same?
|
||||||
|
if (auth.app.name === app.name && auth.active === true) {
|
||||||
newaction.auth_done = true;
|
newaction.auth_done = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -502,6 +501,7 @@ const ConfigureWorkflow = (props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<ListItem>
|
<ListItem>
|
||||||
|
{/*
|
||||||
<ListItemAvatar>
|
<ListItemAvatar>
|
||||||
<Avatar variant="rounded">
|
<Avatar variant="rounded">
|
||||||
<img
|
<img
|
||||||
@@ -516,19 +516,28 @@ const ConfigureWorkflow = (props) => {
|
|||||||
secondary={action.app_version}
|
secondary={action.app_version}
|
||||||
style={{}}
|
style={{}}
|
||||||
/>
|
/>
|
||||||
{action.must_authenticate ? (
|
*/}
|
||||||
action.auth_done ? (
|
{action.must_authenticate ?
|
||||||
<Button color="primary" variant="outlined" onClick={() => {}}>
|
<Button
|
||||||
Authenticated
|
fullWidth
|
||||||
</Button>
|
variant="contained"
|
||||||
) : selectedAction.app_name === action.app_name ? (
|
disabled={action.auth_done}
|
||||||
<CircularProgress />
|
style={{
|
||||||
) : (
|
flex: 1,
|
||||||
<Button
|
textTransform: "none",
|
||||||
color="primary"
|
textAlign: "left",
|
||||||
variant="contained"
|
justifyContent: "flex-start",
|
||||||
onClick={() => {
|
backgroundColor: action.auth_done ? theme.palette.surfaceColor : "#ffffff",
|
||||||
setAuthenticationType(
|
color: action.auth_done ? "#686a6c" : "#2f2f2f",
|
||||||
|
borderRadius: theme.palette.borderRadius,
|
||||||
|
minWidth: 350,
|
||||||
|
maxHeight: 50,
|
||||||
|
overflow: "hidden",
|
||||||
|
border: `1px solid ${theme.palette.inputColor}`,
|
||||||
|
}}
|
||||||
|
color="primary"
|
||||||
|
onClick={() => {
|
||||||
|
setAuthenticationType(
|
||||||
action.app.authentication.type === "oauth2" &&
|
action.app.authentication.type === "oauth2" &&
|
||||||
action.app.authentication.redirect_uri !== undefined &&
|
action.app.authentication.redirect_uri !== undefined &&
|
||||||
action.app.authentication.redirect_uri !== null
|
action.app.authentication.redirect_uri !== null
|
||||||
@@ -541,37 +550,86 @@ const ConfigureWorkflow = (props) => {
|
|||||||
: {
|
: {
|
||||||
type: "",
|
type: "",
|
||||||
}
|
}
|
||||||
);
|
)
|
||||||
|
|
||||||
setItemChanged(true);
|
setItemChanged(true);
|
||||||
setSelectedAction(action.action);
|
|
||||||
setSelectedApp(action.app);
|
if (setSelectedAction !== undefined) {
|
||||||
|
setSelectedAction(action.action);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setSelectedApp !== undefined) {
|
||||||
|
setSelectedApp(action.app);
|
||||||
|
}
|
||||||
|
|
||||||
setAuthenticationModalOpen(true);
|
setAuthenticationModalOpen(true);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Authenticate
|
<img
|
||||||
</Button>
|
alt={action.app_name}
|
||||||
)
|
style={{ margin: 4, minHeight: 30, maxHeight: 30, borderRadius: theme.palette.borderRadius, }}
|
||||||
) : null}
|
src={action.large_image}
|
||||||
|
/>
|
||||||
|
<Typography style={{ margin: 0, marginLeft: 10 }} variant="body1">
|
||||||
|
{action.auth_done ? "Authenticated" : `Authenticate ${action.app_name}`}
|
||||||
|
</Typography>
|
||||||
|
</Button>
|
||||||
|
: null}
|
||||||
{action.must_activate ? (
|
{action.must_activate ? (
|
||||||
<Button
|
<Button
|
||||||
color="primary"
|
fullWidth
|
||||||
variant="contained"
|
variant="contained"
|
||||||
onClick={() => {
|
disabled={action.auth_done}
|
||||||
console.log("ACTION: ", action)
|
style={{
|
||||||
activateApp(action.action.app_id, action.app_name, action.app_version);
|
flex: 1,
|
||||||
setItemChanged(true);
|
textTransform: "none",
|
||||||
}}
|
textAlign: "left",
|
||||||
|
justifyContent: "flex-start",
|
||||||
|
backgroundColor: action.auth_done ? theme.palette.surfaceColor : "#ffffff",
|
||||||
|
color: action.auth_done ? "#686a6c" : "#2f2f2f",
|
||||||
|
borderRadius: theme.palette.borderRadius,
|
||||||
|
minWidth: 350,
|
||||||
|
maxHeight: 50,
|
||||||
|
overflow: "hidden",
|
||||||
|
border: `1px solid ${theme.palette.inputColor}`,
|
||||||
|
}}
|
||||||
|
color="primary"
|
||||||
|
onClick={() => {
|
||||||
|
console.log("ACTION: ", action)
|
||||||
|
activateApp(action.action.app_id, action.app_name, action.app_version);
|
||||||
|
setItemChanged(true);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
Activate
|
<img
|
||||||
|
alt={action.app_name}
|
||||||
|
style={{ margin: 4, minHeight: 30, maxHeight: 30, borderRadius: theme.palette.borderRadius, }}
|
||||||
|
src={action.large_image}
|
||||||
|
/>
|
||||||
|
<Typography style={{ margin: 0, marginLeft: 10 }} variant="body1">
|
||||||
|
Activate
|
||||||
|
</Typography>
|
||||||
</Button>
|
</Button>
|
||||||
) : null}
|
) : null}
|
||||||
{action.update_version !== action.app_version ? (
|
{action.update_version !== action.app_version ? (
|
||||||
<Button
|
<Button
|
||||||
color="primary"
|
fullWidth
|
||||||
variant="contained"
|
variant="contained"
|
||||||
style={{marginLeft: 5}}
|
disabled={action.auth_done}
|
||||||
onClick={() => {
|
style={{
|
||||||
|
flex: 1,
|
||||||
|
textTransform: "none",
|
||||||
|
textAlign: "left",
|
||||||
|
justifyContent: "flex-start",
|
||||||
|
backgroundColor: action.auth_done ? theme.palette.surfaceColor : "#ffffff",
|
||||||
|
color: action.auth_done ? "#686a6c" : "#2f2f2f",
|
||||||
|
borderRadius: theme.palette.borderRadius,
|
||||||
|
minWidth: 350,
|
||||||
|
maxHeight: 50,
|
||||||
|
overflow: "hidden",
|
||||||
|
border: `1px solid ${theme.palette.inputColor}`,
|
||||||
|
}}
|
||||||
|
color="primary"
|
||||||
|
onClick={() => {
|
||||||
console.log("Set version to: ", action.update_version)
|
console.log("Set version to: ", action.update_version)
|
||||||
|
|
||||||
if (workflow.actions !== null) {
|
if (workflow.actions !== null) {
|
||||||
@@ -592,84 +650,118 @@ const ConfigureWorkflow = (props) => {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{action.update_version}
|
<img
|
||||||
|
alt={action.app_name}
|
||||||
|
style={{ margin: 4, minHeight: 30, maxHeight: 30, borderRadius: theme.palette.borderRadius, }}
|
||||||
|
src={action.large_image}
|
||||||
|
/>
|
||||||
|
<Typography style={{ margin: 0, marginLeft: 10 }} variant="body1">
|
||||||
|
{action.update_version}
|
||||||
|
</Typography>
|
||||||
</Button>
|
</Button>
|
||||||
) : null}
|
) : null}
|
||||||
</ListItem>
|
</ListItem>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
|
// Based on the color here. Default: #f86a3e
|
||||||
|
//backgroundColor: selectedUsecaseCategory === usecase.name ? usecase.color : theme.palette.surfaceColor,
|
||||||
|
|
||||||
|
const topColor = "#f86a3e, #fc3922"
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Typography variant="h6">{workflow.name}</Typography>
|
<div style={{height: 125, width: "100%", background: `linear-gradient(to right, ${topColor}`, position: "relative",}}>
|
||||||
<Typography variant="body1" color="textSecondary">
|
</div>
|
||||||
The following configuration makes the workflow ready immediately.
|
<div style={{margin: "25px 50px 50px 50px", maxHeight: 475, }}>
|
||||||
</Typography>
|
<Typography variant="h6">{workflow.name}</Typography>
|
||||||
{requiredActions.length > 0 ? (
|
<Typography variant="body2" color="textSecondary">
|
||||||
<span>
|
The following configuration makes the workflow ready immediately.
|
||||||
<Typography variant="body1" style={{ marginTop: 10 }}>
|
</Typography>
|
||||||
Actions
|
{requiredActions.length > 0 ? (
|
||||||
</Typography>
|
<span>
|
||||||
<List>
|
<Typography variant="body1" style={{ marginTop: 10 }}>
|
||||||
{requiredActions.map((data, index) => {
|
Required Actions
|
||||||
return <AppSection key={index} action={data} />;
|
</Typography>
|
||||||
})}
|
<List>
|
||||||
</List>
|
{requiredActions.map((data, index) => {
|
||||||
</span>
|
return (
|
||||||
) : null}
|
<AppSection key={index} action={data} />
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</List>
|
||||||
|
</span>
|
||||||
|
) : null}
|
||||||
|
|
||||||
{requiredVariables.length > 0 ? (
|
{requiredVariables.length > 0 ? (
|
||||||
<span>
|
<span>
|
||||||
<Typography variant="body1" style={{ marginTop: 10 }}>
|
<Typography variant="body1" style={{ marginTop: 10 }}>
|
||||||
Variables
|
Variables
|
||||||
</Typography>
|
</Typography>
|
||||||
<List>
|
<List>
|
||||||
{requiredVariables.map((data, index) => {
|
{requiredVariables.map((data, index) => {
|
||||||
return <VariableSection key={index} variable={data} />;
|
return <VariableSection key={index} variable={data} />;
|
||||||
})}
|
})}
|
||||||
</List>
|
</List>
|
||||||
</span>
|
</span>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{requiredTriggers.length > 0 ? (
|
{requiredTriggers.length > 0 && showTriggers !== false ? (
|
||||||
<span>
|
<span>
|
||||||
<Typography variant="body1" style={{ marginTop: 10 }}>
|
<Typography variant="body1" style={{ marginTop: 10 }}>
|
||||||
Triggers
|
Triggers
|
||||||
</Typography>
|
</Typography>
|
||||||
<List>
|
<List>
|
||||||
{requiredTriggers.map((data, index) => {
|
{requiredTriggers.map((data, index) => {
|
||||||
return <TriggerSection key={index} trigger={data} />;
|
return <TriggerSection key={index} trigger={data} />;
|
||||||
})}
|
})}
|
||||||
</List>
|
</List>
|
||||||
</span>
|
</span>
|
||||||
) : null}
|
) : null}
|
||||||
<div style={{ textAlign: "center", display: "flex", marginTop: 20 }}>
|
|
||||||
<ButtonGroup style={{ margin: "auto" }}>
|
<div style={{ textAlign: "center", display: "flex", marginTop: 20 }}>
|
||||||
{/*
|
{showFinalizeAnimation ?
|
||||||
<Button color="primary" variant={"outlined"} style={{
|
<img id="finalize_gif" src="/images/finalize.gif" alt="finalize workflow animation" style={{width: 150, margin: "auto",}} onLoad={() => {
|
||||||
}} onClick={() => {
|
console.log("Img loaded.")
|
||||||
setConfigureWorkflowModalOpen(false)
|
setTimeout(() => {
|
||||||
}}>
|
console.log("Img closing.")
|
||||||
Skip
|
setConfigureWorkflowModalOpen(false);
|
||||||
</Button>
|
}, 1250)
|
||||||
*/}
|
|
||||||
<Button
|
}}/>
|
||||||
color="primary"
|
:
|
||||||
variant={itemChanged ? "contained" : "outlined"}
|
<ButtonGroup style={{ margin: "auto" }}>
|
||||||
style={{}}
|
{/*
|
||||||
onClick={() => {
|
<Button color="primary" variant={"outlined"} style={{
|
||||||
if (itemChanged) {
|
}} onClick={() => {
|
||||||
saveWorkflow(workflow);
|
setConfigureWorkflowModalOpen(false)
|
||||||
window.location.reload();
|
}}>
|
||||||
} else {
|
Skip
|
||||||
setConfigureWorkflowModalOpen(false);
|
</Button>
|
||||||
}
|
*/}
|
||||||
}}
|
<Button
|
||||||
>
|
color="textSecondary"
|
||||||
Close window
|
variant={"outlined"}
|
||||||
</Button>
|
style={{}}
|
||||||
</ButtonGroup>
|
onClick={() => {
|
||||||
</div>
|
setShowFinalizeAnimation(true)
|
||||||
|
setTimeout(() => {
|
||||||
|
if (itemChanged) {
|
||||||
|
if (saveWorkflow !== undefined) {
|
||||||
|
saveWorkflow(workflow);
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
}, 1000)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Finalize
|
||||||
|
</Button>
|
||||||
|
</ButtonGroup>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import React, { useEffect, useContext } from "react";
|
|||||||
import theme from '../theme';
|
import theme from '../theme';
|
||||||
import { isMobile } from "react-device-detect"
|
import { isMobile } from "react-device-detect"
|
||||||
import ChipInput from "material-ui-chip-input";
|
import ChipInput from "material-ui-chip-input";
|
||||||
|
import UsecaseSearch from "../components/UsecaseSearch.jsx"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Badge,
|
Badge,
|
||||||
@@ -45,19 +46,91 @@ import {
|
|||||||
} from "@material-ui/icons";
|
} from "@material-ui/icons";
|
||||||
|
|
||||||
const EditWorkflow = (props) => {
|
const EditWorkflow = (props) => {
|
||||||
const { workflow, setWorkflow, modalOpen, setModalOpen, showUpload, usecases, } = props
|
const { globalUrl, workflow, setWorkflow, modalOpen, setModalOpen, showUpload, usecases, setNewWorkflow, appFramework, isEditing, userdata, } = props
|
||||||
|
|
||||||
const [submitLoading, setSubmitLoading] = React.useState(false);
|
const [submitLoading, setSubmitLoading] = React.useState(false);
|
||||||
const [selectedUsecases, setSelectedUsecases] = React.useState([]);
|
|
||||||
const [showMoreClicked, setShowMoreClicked] = React.useState(false);
|
const [showMoreClicked, setShowMoreClicked] = React.useState(false);
|
||||||
const [innerWorkflow, setInnerWorkflow] = React.useState(workflow)
|
const [innerWorkflow, setInnerWorkflow] = React.useState(workflow)
|
||||||
const [_, setUpdate] = React.useState(""); // Used for rendering, don't remove
|
const [_, setUpdate] = React.useState(""); // Used for rendering, don't remove
|
||||||
const [newWorkflowTags, setNewWorkflowTags] = React.useState(workflow.tags !== undefined && workflow.tags !== null ? JSON.parse(JSON.stringify(workflow.tags)) : [])
|
const [newWorkflowTags, setNewWorkflowTags] = React.useState(workflow.tags !== undefined && workflow.tags !== null ? JSON.parse(JSON.stringify(workflow.tags)) : [])
|
||||||
|
const [selectedUsecases, setSelectedUsecases] = React.useState(workflow.usecase_ids !== undefined && workflow.usecase_ids !== null ? JSON.parse(JSON.stringify(workflow.usecase_ids)) : []);
|
||||||
|
const [foundWorkflowId, setFoundWorkflowId] = React.useState("")
|
||||||
|
const [name, setName] = React.useState(workflow.name !== undefined ? workflow.name : "")
|
||||||
|
const [description, setDescription] = React.useState(workflow.description !== undefined ? workflow.description : "")
|
||||||
|
|
||||||
if (workflow === undefined || workflow.id === undefined || setWorkflow === undefined || modalOpen !== true) {
|
|
||||||
|
// Gets the generated workflow
|
||||||
|
const getGeneratedWorkflow = (workflow_id) => {
|
||||||
|
fetch(globalUrl + "/api/v1/workflows/" + workflow_id, {
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
Accept: "application/json",
|
||||||
|
},
|
||||||
|
credentials: "include",
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
if (response.status !== 200) {
|
||||||
|
console.log("Status not 200 when getting workflow");
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then((responseJson) => {
|
||||||
|
if (responseJson.id === workflow_id) {
|
||||||
|
console.log("GOT WORKFLOW: ", responseJson)
|
||||||
|
if (name === "") {
|
||||||
|
innerWorkflow.name = responseJson.name
|
||||||
|
setName(responseJson.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (description === "") {
|
||||||
|
innerWorkflow.description = responseJson.description
|
||||||
|
setDescription(description)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newWorkflowTags === []) {
|
||||||
|
innerWorkflow.tags = responseJson.tags
|
||||||
|
setNewWorkflowTags(responseJson.tags)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedUsecases === []) {
|
||||||
|
selectedUsecases = responseJson.usecase_ids
|
||||||
|
}
|
||||||
|
|
||||||
|
innerWorkflow.id = responseJson.id
|
||||||
|
innerWorkflow.blogpost = responseJson.blogpost
|
||||||
|
innerWorkflow.actions = responseJson.actions
|
||||||
|
innerWorkflow.triggers = responseJson.triggers
|
||||||
|
innerWorkflow.branches = responseJson.branches
|
||||||
|
innerWorkflow.comments = responseJson.comments
|
||||||
|
innerWorkflow.workflow_variables = responseJson.workflow_variables
|
||||||
|
innerWorkflow.execution_variables = responseJson.execution_variables
|
||||||
|
|
||||||
|
|
||||||
|
setInnerWorkflow(innerWorkflow)
|
||||||
|
setUpdate(Math.random())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
//alert.error(error.toString());
|
||||||
|
console.log("Get workflow error: ", error.toString());
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (foundWorkflowId.length > 0) {
|
||||||
|
getGeneratedWorkflow(foundWorkflowId)
|
||||||
|
|
||||||
|
setFoundWorkflowId("")
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modalOpen !== true) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const newWorkflow = isEditing === true ? false : true
|
||||||
|
|
||||||
var upload = "";
|
var upload = "";
|
||||||
var total_count = 0
|
var total_count = 0
|
||||||
|
|
||||||
@@ -71,264 +144,299 @@ const EditWorkflow = (props) => {
|
|||||||
style: {
|
style: {
|
||||||
backgroundColor: theme.palette.surfaceColor,
|
backgroundColor: theme.palette.surfaceColor,
|
||||||
color: "white",
|
color: "white",
|
||||||
minWidth: isMobile ? "90%" : "800px",
|
minWidth: isMobile ? "90%" : newWorkflow === true ? 1000 : 550,
|
||||||
maxWidth: isMobile ? "90%" : "800px",
|
maxWidth: isMobile ? "90%" : newWorkflow === true ? 1000 : 550,
|
||||||
|
minHeight: 400,
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<DialogTitle>
|
<DialogTitle style={{padding: 30, paddingBottom: 0, zIndex: 1000,}}>
|
||||||
<div style={{ color: "rgba(255,255,255,0.9)" }}>
|
<div style={{display: "flex"}}>
|
||||||
{workflow.id !== undefined ? "Editing" : "New"} workflow
|
<div style={{flex: 1, color: "rgba(255,255,255,0.9)" }}>
|
||||||
{showUpload === true ?
|
<Typography variant="h6">
|
||||||
<div style={{ float: "right" }}>
|
{newWorkflow ? "New" : "Editing"} workflow
|
||||||
<Tooltip color="primary" title={"Import manually"} placement="top">
|
</Typography>
|
||||||
<Button
|
<Typography variant="body2" color="textSecondary" style={{maxWidth: 440,}}>
|
||||||
color="primary"
|
Workflows can be built from scratch, or from templates. <a href="/usecases" rel="noopener noreferrer" target="_blank" style={{ textDecoration: "none", color: "#f86a3e" }}>Usecases</a> can help you discover next steps, and you can <a href="/search?tab=workflows" rel="noopener noreferrer" target="_blank" style={{ textDecoration: "none", color: "#f86a3e" }}>search</a> for them directly. <a href="/docs/workflows" rel="noopener noreferrer" target="_blank" style={{ textDecoration: "none", color: "#f86a3e" }}>Learn more</a>
|
||||||
style={{}}
|
</Typography>
|
||||||
variant="text"
|
{showUpload === true ?
|
||||||
onClick={() => upload.click()}
|
<div style={{ float: "right" }}>
|
||||||
>
|
<Tooltip color="primary" title={"Import manually"} placement="top">
|
||||||
<PublishIcon />
|
<Button
|
||||||
</Button>
|
color="primary"
|
||||||
</Tooltip>
|
style={{}}
|
||||||
</div>
|
variant="text"
|
||||||
|
onClick={() => upload.click()}
|
||||||
|
>
|
||||||
|
<PublishIcon />
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
: null}
|
||||||
|
</div>
|
||||||
|
{newWorkflow === true ?
|
||||||
|
<div style={{flex: 1, marginLeft: 45, }}>
|
||||||
|
<Typography variant="h6">
|
||||||
|
Use a Template
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body2" color="textSecondary" style={{maxWidth: 440,}}>
|
||||||
|
Start your workflow from our templating system. This uses publied workflows from our <a href="/creators" rel="noopener noreferrer" target="_blank" style={{ textDecoration: "none", color: "#f86a3e"}}>Creators</a> to generate full Usecases or parts of your Workflow.
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
: null}
|
: null}
|
||||||
</div>
|
</div>
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<DialogContent>
|
<DialogContent style={{paddingTop: 10, display: "flex", minHeight: 350, zIndex: 1001, }}>
|
||||||
<TextField
|
<div style={{minWidth: newWorkflow ? 450 : 500, maxWidth: newWorkflow ? 450 : 500, }}>
|
||||||
onBlur={(event) => {
|
<TextField
|
||||||
innerWorkflow.name = event.target.value
|
onBlur={(event) => {
|
||||||
}}
|
setName(event.target.value)
|
||||||
InputProps={{
|
|
||||||
style: {
|
|
||||||
color: "white",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
color="primary"
|
|
||||||
placeholder="Name"
|
|
||||||
required
|
|
||||||
margin="dense"
|
|
||||||
defaultValue={innerWorkflow.name}
|
|
||||||
label="Name"
|
|
||||||
autoFocus
|
|
||||||
fullWidth
|
|
||||||
/>
|
|
||||||
<TextField
|
|
||||||
onBlur={(event) => {
|
|
||||||
innerWorkflow.description = event.target.value
|
|
||||||
setInnerWorkflow(innerWorkflow)
|
|
||||||
}}
|
|
||||||
InputProps={{
|
|
||||||
style: {
|
|
||||||
color: "white",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
color="primary"
|
|
||||||
defaultValue={innerWorkflow.description}
|
|
||||||
placeholder="Description"
|
|
||||||
multiline
|
|
||||||
label="Description"
|
|
||||||
margin="dense"
|
|
||||||
fullWidth
|
|
||||||
/>
|
|
||||||
<div style={{display: "flex", marginTop: 10, }}>
|
|
||||||
<ChipInput
|
|
||||||
style={{ flex: 1}}
|
|
||||||
InputProps={{
|
|
||||||
style: {
|
|
||||||
color: "white",
|
|
||||||
},
|
|
||||||
}}
|
}}
|
||||||
placeholder="Tags"
|
InputProps={{
|
||||||
color="primary"
|
style: {
|
||||||
fullWidth
|
color: "white",
|
||||||
value={newWorkflowTags}
|
},
|
||||||
onAdd={(chip) => {
|
}}
|
||||||
newWorkflowTags.push(chip);
|
color="primary"
|
||||||
setNewWorkflowTags(newWorkflowTags);
|
placeholder="Name"
|
||||||
|
required
|
||||||
|
margin="dense"
|
||||||
|
defaultValue={innerWorkflow.name}
|
||||||
|
label="Name"
|
||||||
|
autoFocus
|
||||||
|
fullWidth
|
||||||
|
/>
|
||||||
|
<TextField
|
||||||
|
onBlur={(event) => {
|
||||||
|
setDescription(event.target.value)
|
||||||
}}
|
}}
|
||||||
onDelete={(chip, index) => {
|
InputProps={{
|
||||||
newWorkflowTags.splice(index, 1);
|
style: {
|
||||||
setNewWorkflowTags(newWorkflowTags);
|
color: "white",
|
||||||
}}
|
},
|
||||||
/>
|
}}
|
||||||
{usecases !== null && usecases !== undefined && usecases.length > 0 ?
|
maxRows={4}
|
||||||
<FormControl style={{flex: 1, marginLeft: 5, }}>
|
color="primary"
|
||||||
<InputLabel htmlFor="grouped-select-usecase">Usecases</InputLabel>
|
defaultValue={innerWorkflow.description}
|
||||||
<Select
|
placeholder="Description"
|
||||||
defaultValue=""
|
multiline
|
||||||
id="grouped-select"
|
label="Description"
|
||||||
label="Matching Usecase"
|
margin="dense"
|
||||||
multiple
|
fullWidth
|
||||||
value={selectedUsecases}
|
/>
|
||||||
renderValue={(selected) => selected.join(', ')}
|
<div style={{display: "flex", marginTop: 10, }}>
|
||||||
onChange={(event) => {
|
<ChipInput
|
||||||
console.log("Changed: ", event)
|
style={{ flex: 1, maxHeight: 40, marginTop: 12, overflow: "auto", }}
|
||||||
}}
|
InputProps={{
|
||||||
>
|
style: {
|
||||||
<MenuItem value="">
|
color: "white",
|
||||||
<em>None</em>
|
},
|
||||||
</MenuItem>
|
}}
|
||||||
{usecases.map((usecase, index) => {
|
placeholder="Tags"
|
||||||
//console.log(usecase)
|
color="primary"
|
||||||
return (
|
fullWidth
|
||||||
<span key={index}>
|
value={newWorkflowTags}
|
||||||
<ListSubheader
|
onAdd={(chip) => {
|
||||||
style={{color: usecase.color}}
|
newWorkflowTags.push(chip);
|
||||||
>
|
setNewWorkflowTags(newWorkflowTags);
|
||||||
{usecase.name}
|
}}
|
||||||
</ListSubheader>
|
onDelete={(chip, index) => {
|
||||||
{usecase.list.map((subcase, subindex) => {
|
newWorkflowTags.splice(index, 1);
|
||||||
//console.log(subcase)
|
setNewWorkflowTags(newWorkflowTags);
|
||||||
total_count += 1
|
}}
|
||||||
return (
|
/>
|
||||||
<MenuItem key={subindex} value={total_count} onClick={(event) => {
|
{usecases !== null && usecases !== undefined && usecases.length > 0 ?
|
||||||
if (selectedUsecases.includes(subcase.name)) {
|
<FormControl style={{flex: 1, marginLeft: 5, }}>
|
||||||
const itemIndex = selectedUsecases.indexOf(subcase.name)
|
<InputLabel htmlFor="grouped-select-usecase">Usecases</InputLabel>
|
||||||
if (itemIndex > -1) {
|
<Select
|
||||||
selectedUsecases.splice(itemIndex, 1)
|
defaultValue=""
|
||||||
|
id="grouped-select"
|
||||||
|
label="Matching Usecase"
|
||||||
|
multiple
|
||||||
|
value={selectedUsecases}
|
||||||
|
renderValue={(selected) => selected.join(', ')}
|
||||||
|
onChange={(event) => {
|
||||||
|
console.log("Changed: ", event)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<MenuItem value="">
|
||||||
|
<em>None</em>
|
||||||
|
</MenuItem>
|
||||||
|
{usecases.map((usecase, index) => {
|
||||||
|
//console.log(usecase)
|
||||||
|
return (
|
||||||
|
<span key={index}>
|
||||||
|
<ListSubheader
|
||||||
|
style={{color: usecase.color}}
|
||||||
|
>
|
||||||
|
{usecase.name}
|
||||||
|
</ListSubheader>
|
||||||
|
{usecase.list.map((subcase, subindex) => {
|
||||||
|
//console.log(subcase)
|
||||||
|
total_count += 1
|
||||||
|
return (
|
||||||
|
<MenuItem key={subindex} value={total_count} onClick={(event) => {
|
||||||
|
if (selectedUsecases.includes(subcase.name)) {
|
||||||
|
const itemIndex = selectedUsecases.indexOf(subcase.name)
|
||||||
|
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>
|
||||||
|
|
||||||
|
{showMoreClicked === true ?
|
||||||
|
<span style={{marginTop: 25, }}>
|
||||||
|
|
||||||
|
<FormControl style={{marginTop: 15, }}>
|
||||||
|
<FormLabel id="demo-row-radio-buttons-group-label">Status</FormLabel>
|
||||||
|
<RadioGroup
|
||||||
|
row
|
||||||
|
aria-labelledby="demo-row-radio-buttons-group-label"
|
||||||
|
name="row-radio-buttons-group"
|
||||||
|
defaultValue={innerWorkflow.status}
|
||||||
|
onChange={(e) => {
|
||||||
|
console.log("Data: ", e.target.value)
|
||||||
|
|
||||||
|
innerWorkflow.workflow_type = e.target.value
|
||||||
|
setInnerWorkflow(innerWorkflow)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<FormControlLabel value="test" control={<Radio />} label="Test" />
|
||||||
|
<FormControlLabel value="production" control={<Radio />} label="Production" />
|
||||||
|
|
||||||
|
</RadioGroup>
|
||||||
|
</FormControl>
|
||||||
|
<div />
|
||||||
|
|
||||||
|
<FormControl style={{marginTop: 15, }}>
|
||||||
|
<FormLabel id="demo-row-radio-buttons-group-label">Type</FormLabel>
|
||||||
|
<RadioGroup
|
||||||
|
row
|
||||||
|
aria-labelledby="demo-row-radio-buttons-group-label"
|
||||||
|
name="row-radio-buttons-group"
|
||||||
|
defaultValue={innerWorkflow.workflow_type}
|
||||||
|
onChange={(e) => {
|
||||||
|
console.log("Data: ", e.target.value)
|
||||||
|
|
||||||
|
innerWorkflow.workflow_type = e.target.value
|
||||||
|
setInnerWorkflow(innerWorkflow)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<FormControlLabel value="trigger" control={<Radio />} label="Trigger" />
|
||||||
|
<FormControlLabel value="subflow" control={<Radio />} label="Subflow" />
|
||||||
|
<FormControlLabel value="standalone" control={<Radio />} label="Standalone" />
|
||||||
|
|
||||||
|
</RadioGroup>
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
|
||||||
|
<TextField
|
||||||
|
onBlur={(event) => {
|
||||||
|
innerWorkflow.blogpost = event.target.value
|
||||||
|
setInnerWorkflow(innerWorkflow)
|
||||||
|
}}
|
||||||
|
InputProps={{
|
||||||
|
style: {
|
||||||
|
color: "white",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
color="primary"
|
||||||
|
defaultValue={innerWorkflow.blogpost}
|
||||||
|
placeholder="A blogpost or other reference for how this work workflow was built, and what it's for."
|
||||||
|
rows="1"
|
||||||
|
label="blogpost"
|
||||||
|
margin="dense"
|
||||||
|
fullWidth
|
||||||
|
/>
|
||||||
|
<TextField
|
||||||
|
onBlur={(event) => {
|
||||||
|
innerWorkflow.video = event.target.value
|
||||||
|
setInnerWorkflow(innerWorkflow)
|
||||||
|
}}
|
||||||
|
InputProps={{
|
||||||
|
style: {
|
||||||
|
color: "white",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
color="primary"
|
||||||
|
defaultValue={innerWorkflow.video}
|
||||||
|
placeholder="A youtube or loom link to the video"
|
||||||
|
rows="1"
|
||||||
|
label="Video"
|
||||||
|
margin="dense"
|
||||||
|
fullWidth
|
||||||
|
/>
|
||||||
|
<TextField
|
||||||
|
onBlur={(event) => {
|
||||||
|
innerWorkflow.default_return_value = event.target.value
|
||||||
|
setInnerWorkflow(innerWorkflow)
|
||||||
|
}}
|
||||||
|
InputProps={{
|
||||||
|
style: {
|
||||||
|
color: "white",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
color="primary"
|
||||||
|
defaultValue={innerWorkflow.default_return_value}
|
||||||
|
placeholder="Default return value (used for Subflows if the subflow fails)"
|
||||||
|
rows="3"
|
||||||
|
multiline
|
||||||
|
label="Default return value"
|
||||||
|
margin="dense"
|
||||||
|
fullWidth
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
: null}
|
: null}
|
||||||
|
<Tooltip color="primary" title={"Add more details"} placement="top">
|
||||||
|
<IconButton
|
||||||
|
style={{ color: "white", margin: "auto", marginTop: 10, textAlign: "center", width: 50,}}
|
||||||
|
onClick={() => {
|
||||||
|
setShowMoreClicked(!showMoreClicked);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{showMoreClicked ? <ExpandLessIcon /> : <ExpandMoreIcon/>}
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
{newWorkflow === true ?
|
||||||
{showMoreClicked === true ?
|
<div style={{marginLeft: 50, maxWidth: 400, minWidth: 400, position: "relative",}}>
|
||||||
<span style={{marginTop: 25, }}>
|
<UsecaseSearch
|
||||||
|
globalUrl={globalUrl}
|
||||||
<FormControl style={{marginTop: 15, }}>
|
appFramework={appFramework}
|
||||||
<FormLabel id="demo-row-radio-buttons-group-label">Status</FormLabel>
|
defaultSearch={undefined}
|
||||||
<RadioGroup
|
apps={undefined}
|
||||||
row
|
setFoundWorkflowId={setFoundWorkflowId}
|
||||||
aria-labelledby="demo-row-radio-buttons-group-label"
|
userdata={userdata}
|
||||||
name="row-radio-buttons-group"
|
|
||||||
defaultValue={innerWorkflow.status}
|
|
||||||
onChange={(e) => {
|
|
||||||
console.log("Data: ", e.target.value)
|
|
||||||
|
|
||||||
innerWorkflow.workflow_type = e.target.value
|
|
||||||
setInnerWorkflow(innerWorkflow)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<FormControlLabel value="test" control={<Radio />} label="Test" />
|
|
||||||
<FormControlLabel value="production" control={<Radio />} label="Production" />
|
|
||||||
|
|
||||||
</RadioGroup>
|
|
||||||
</FormControl>
|
|
||||||
<div />
|
|
||||||
|
|
||||||
<FormControl style={{marginTop: 15, }}>
|
|
||||||
<FormLabel id="demo-row-radio-buttons-group-label">Type</FormLabel>
|
|
||||||
<RadioGroup
|
|
||||||
row
|
|
||||||
aria-labelledby="demo-row-radio-buttons-group-label"
|
|
||||||
name="row-radio-buttons-group"
|
|
||||||
defaultValue={innerWorkflow.workflow_type}
|
|
||||||
onChange={(e) => {
|
|
||||||
console.log("Data: ", e.target.value)
|
|
||||||
|
|
||||||
innerWorkflow.workflow_type = e.target.value
|
|
||||||
setInnerWorkflow(innerWorkflow)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<FormControlLabel value="trigger" control={<Radio />} label="Trigger" />
|
|
||||||
<FormControlLabel value="subflow" control={<Radio />} label="Subflow" />
|
|
||||||
<FormControlLabel value="standalone" control={<Radio />} label="Standalone" />
|
|
||||||
|
|
||||||
</RadioGroup>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
|
|
||||||
<TextField
|
|
||||||
onBlur={(event) => {
|
|
||||||
innerWorkflow.blogpost = event.target.value
|
|
||||||
setInnerWorkflow(innerWorkflow)
|
|
||||||
}}
|
|
||||||
InputProps={{
|
|
||||||
style: {
|
|
||||||
color: "white",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
color="primary"
|
|
||||||
defaultValue={innerWorkflow.blogpost}
|
|
||||||
placeholder="A blogpost or other reference for how this work workflow was built, and what it's for."
|
|
||||||
rows="1"
|
|
||||||
label="blogpost"
|
|
||||||
margin="dense"
|
|
||||||
fullWidth
|
|
||||||
/>
|
/>
|
||||||
<TextField
|
</div>
|
||||||
onBlur={(event) => {
|
|
||||||
innerWorkflow.video = event.target.value
|
|
||||||
setInnerWorkflow(innerWorkflow)
|
|
||||||
}}
|
|
||||||
InputProps={{
|
|
||||||
style: {
|
|
||||||
color: "white",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
color="primary"
|
|
||||||
defaultValue={innerWorkflow.video}
|
|
||||||
placeholder="A youtube or loom link to the video"
|
|
||||||
rows="1"
|
|
||||||
label="Video"
|
|
||||||
margin="dense"
|
|
||||||
fullWidth
|
|
||||||
/>
|
|
||||||
<TextField
|
|
||||||
onBlur={(event) => {
|
|
||||||
innerWorkflow.default_return_value = event.target.value
|
|
||||||
setInnerWorkflow(innerWorkflow)
|
|
||||||
}}
|
|
||||||
InputProps={{
|
|
||||||
style: {
|
|
||||||
color: "white",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
color="primary"
|
|
||||||
defaultValue={innerWorkflow.default_return_value}
|
|
||||||
placeholder="Default return value (used for Subflows if the subflow fails)"
|
|
||||||
rows="3"
|
|
||||||
multiline
|
|
||||||
label="Default return value"
|
|
||||||
margin="dense"
|
|
||||||
fullWidth
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
: null}
|
: null}
|
||||||
<Tooltip color="primary" title={"Add more details"} placement="top">
|
|
||||||
<IconButton
|
|
||||||
style={{ color: "white", margin: "auto", marginTop: 10, textAlign: "center", width: 50,}}
|
|
||||||
onClick={() => {
|
|
||||||
setShowMoreClicked(!showMoreClicked);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{showMoreClicked ? <ExpandLessIcon /> : <ExpandMoreIcon />}
|
|
||||||
</IconButton>
|
|
||||||
</Tooltip>
|
|
||||||
|
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<Button
|
<Button
|
||||||
style={{}}
|
style={{}}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
if (setNewWorkflow !== undefined) {
|
||||||
|
setWorkflow({})
|
||||||
|
}
|
||||||
|
|
||||||
setModalOpen(false)
|
setModalOpen(false)
|
||||||
}}
|
}}
|
||||||
color="primary"
|
color="primary"
|
||||||
@@ -338,13 +446,35 @@ const EditWorkflow = (props) => {
|
|||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
style={{}}
|
style={{}}
|
||||||
disabled={innerWorkflow.name.length === 0}
|
disabled={name.length === 0}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
innerWorkflow.name = name
|
||||||
|
innerWorkflow.description = description
|
||||||
if (newWorkflowTags.length > 0) {
|
if (newWorkflowTags.length > 0) {
|
||||||
innerWorkflow.tags = newWorkflowTags
|
innerWorkflow.tags = newWorkflowTags
|
||||||
}
|
}
|
||||||
|
|
||||||
setWorkflow(innerWorkflow)
|
if (selectedUsecases.length > 0) {
|
||||||
|
innerWorkflow.usecase_ids = selectedUsecases
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (setNewWorkflow !== undefined) {
|
||||||
|
setNewWorkflow(
|
||||||
|
innerWorkflow.name,
|
||||||
|
innerWorkflow.description,
|
||||||
|
innerWorkflow.tags,
|
||||||
|
innerWorkflow.default_return_value,
|
||||||
|
innerWorkflow,
|
||||||
|
newWorkflow,
|
||||||
|
innerWorkflow.usecase_ids,
|
||||||
|
innerWorkflow.blogpost,
|
||||||
|
innerWorkflow.status,
|
||||||
|
)
|
||||||
|
setWorkflow({})
|
||||||
|
} else {
|
||||||
|
setWorkflow(innerWorkflow)
|
||||||
|
}
|
||||||
setModalOpen(false)
|
setModalOpen(false)
|
||||||
}}
|
}}
|
||||||
color="primary"
|
color="primary"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import {isMobile} from "react-device-detect";
|
import {isMobile} from "react-device-detect";
|
||||||
import DetectionFramework, { usecases } from "../components/DetectionFramework.jsx";
|
import AppFramework, { usecases } from "../components/AppFramework.jsx";
|
||||||
import {Link} from 'react-router-dom';
|
import {Link} from 'react-router-dom';
|
||||||
import ReactGA from 'react-ga';
|
import ReactGA from 'react-ga';
|
||||||
|
|
||||||
@@ -169,7 +169,7 @@ const LandingpageUsecases = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
{isMobile ? null :
|
{isMobile ? null :
|
||||||
<div style={{marginLeft: 200, marginTop: 125, zIndex: 1000}}>
|
<div style={{marginLeft: 200, marginTop: 125, zIndex: 1000}}>
|
||||||
<DetectionFramework showOptions={false} selectedOption={selectedUsecase} rolling={true} />
|
<AppFramework showOptions={false} selectedOption={selectedUsecase} rolling={true} />
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
{isMobile ? null :
|
{isMobile ? null :
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import React, { useRef, useState, useEffect, useLayoutEffect } from "react";
|
import React, { useRef, useState, useEffect, useLayoutEffect } from "react";
|
||||||
import { useTheme } from "@material-ui/core/styles";
|
import { useTheme } from "@material-ui/core/styles";
|
||||||
|
import theme from '../theme';
|
||||||
|
|
||||||
import { v4 as uuidv4 } from "uuid";
|
import { v4 as uuidv4 } from "uuid";
|
||||||
import {
|
import {
|
||||||
@@ -62,6 +63,7 @@ const registeredApps = [
|
|||||||
"outlook_office365",
|
"outlook_office365",
|
||||||
"microsoft_teams",
|
"microsoft_teams",
|
||||||
"microsoft_teams_user_access",
|
"microsoft_teams_user_access",
|
||||||
|
"todoist",
|
||||||
]
|
]
|
||||||
|
|
||||||
const AuthenticationOauth2 = (props) => {
|
const AuthenticationOauth2 = (props) => {
|
||||||
@@ -77,8 +79,8 @@ const AuthenticationOauth2 = (props) => {
|
|||||||
setNewAppAuth,
|
setNewAppAuth,
|
||||||
setAuthenticationModalOpen,
|
setAuthenticationModalOpen,
|
||||||
isCloud,
|
isCloud,
|
||||||
|
autoAuth,
|
||||||
} = props;
|
} = props;
|
||||||
const theme = useTheme();
|
|
||||||
|
|
||||||
//const [update, setUpdate] = React.useState("|")
|
//const [update, setUpdate] = React.useState("|")
|
||||||
const [defaultConfigSet, setDefaultConfigSet] = React.useState(
|
const [defaultConfigSet, setDefaultConfigSet] = React.useState(
|
||||||
@@ -88,7 +90,7 @@ const AuthenticationOauth2 = (props) => {
|
|||||||
authenticationType.client_secret !== undefined &&
|
authenticationType.client_secret !== undefined &&
|
||||||
authenticationType.client_secret !== null &&
|
authenticationType.client_secret !== null &&
|
||||||
authenticationType.client_secret.length > 0
|
authenticationType.client_secret.length > 0
|
||||||
);
|
);
|
||||||
|
|
||||||
const [clientId, setClientId] = React.useState(
|
const [clientId, setClientId] = React.useState(
|
||||||
defaultConfigSet ? authenticationType.client_id : ""
|
defaultConfigSet ? authenticationType.client_id : ""
|
||||||
@@ -123,6 +125,73 @@ const AuthenticationOauth2 = (props) => {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const startOauth2Request = () => {
|
||||||
|
console.log("APP: ", selectedApp)
|
||||||
|
if (selectedApp.name.toLowerCase() == "outlook_graph" || selectedApp.name.toLowerCase() == "outlook_office365") {
|
||||||
|
handleOauth2Request(
|
||||||
|
"efe4c3fe-84a1-4821-a84f-23a6cfe8e72d",
|
||||||
|
"",
|
||||||
|
"https://graph.microsoft.com",
|
||||||
|
["Mail.ReadWrite"],
|
||||||
|
);
|
||||||
|
} else if (selectedApp.name.toLowerCase() == "gmail") {
|
||||||
|
handleOauth2Request(
|
||||||
|
"253565968129-c0a35knic7q1pdk6i6qk9gdkvr07ci49.apps.googleusercontent.com",
|
||||||
|
"",
|
||||||
|
"https://gmail.googleapis.com",
|
||||||
|
["https://www.googleapis.com/auth/gmail.modify",
|
||||||
|
"https://www.googleapis.com/auth/gmail.send",
|
||||||
|
"https://www.googleapis.com/auth/gmail.insert",
|
||||||
|
"https://www.googleapis.com/auth/gmail.compose"]
|
||||||
|
)
|
||||||
|
} else if (selectedApp.name.toLowerCase() == "zoho_desk") {
|
||||||
|
handleOauth2Request(
|
||||||
|
"1000.ZR5MHUW6B0L6W1VUENFGIATFS0TOJT",
|
||||||
|
"",
|
||||||
|
"https://desk.zoho.com",
|
||||||
|
["Desk.tickets.READ",
|
||||||
|
"Desk.tickets.UPDATE",
|
||||||
|
"Desk.tickets.DELETE",
|
||||||
|
"Desk.tickets.CREATE"]
|
||||||
|
)
|
||||||
|
} else if (selectedApp.name.toLowerCase() == "slack") {
|
||||||
|
handleOauth2Request(
|
||||||
|
"151779186901.2448678750935",
|
||||||
|
"",
|
||||||
|
"https://slack.com",
|
||||||
|
["admin", "chat:write", "im:read", "im:write", "search:read", "usergroups:read", "usergroups:write"]
|
||||||
|
)
|
||||||
|
} else if (selectedApp.name.toLowerCase() == "webex") {
|
||||||
|
handleOauth2Request(
|
||||||
|
"Cab184f3d7271f540443c79b5b79845e3387abbbdb3db4233a87ea3a5432fb3d5",
|
||||||
|
"",
|
||||||
|
"https://webexapis.com",
|
||||||
|
["spark:all"]
|
||||||
|
)
|
||||||
|
} else if (selectedApp.name.toLowerCase().includes("microsoft_teams")) {
|
||||||
|
handleOauth2Request(
|
||||||
|
"31cb4c84-658e-43d5-ae84-22c9142e967a",
|
||||||
|
"",
|
||||||
|
"https://graph.microsoft.com",
|
||||||
|
["ChannelMessage.Edit", "ChannelMessage.Read.All", "ChannelMessage.Send", "Chat.Create", "Chat.ReadWrite", "Chat.Read"]
|
||||||
|
)
|
||||||
|
} else if (selectedApp.name.toLowerCase().includes("todoist")) {
|
||||||
|
handleOauth2Request(
|
||||||
|
"35fa3a384040470db0c8527e90a3c2eb",
|
||||||
|
"",
|
||||||
|
"https://api.todoist.com",
|
||||||
|
["task:add"]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
console.log("Should automatically click the auto-auth button?")
|
||||||
|
if (autoAuth === true && selectedApp !== undefined) {
|
||||||
|
startOauth2Request()
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
const handleOauth2Request = (client_id, client_secret, oauth_url, scopes) => {
|
const handleOauth2Request = (client_id, client_secret, oauth_url, scopes) => {
|
||||||
setButtonClicked(true);
|
setButtonClicked(true);
|
||||||
console.log("SCOPES: ", scopes);
|
console.log("SCOPES: ", scopes);
|
||||||
@@ -164,19 +233,15 @@ const AuthenticationOauth2 = (props) => {
|
|||||||
state += `%26refresh_uri%3d${authentication_url}`;
|
state += `%26refresh_uri%3d${authentication_url}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// No prompt forcing
|
||||||
|
const url = `${authenticationType.redirect_uri}?client_id=${client_id}&redirect_uri=${redirectUri}&response_type=code&prompt=login&scope=${resources}&state=${state}&access_type=offline`;
|
||||||
// Force new consent
|
// Force new consent
|
||||||
//const url = `${authenticationType.redirect_uri}?client_id=${client_id}&redirect_uri=${redirectUri}&response_type=code&scope=${resources}&prompt=consent&state=${state}&access_type=offline`;
|
//const url = `${authenticationType.redirect_uri}?client_id=${client_id}&redirect_uri=${redirectUri}&response_type=code&scope=${resources}&prompt=consent&state=${state}&access_type=offline`;
|
||||||
|
|
||||||
// Admin consent
|
// Admin consent
|
||||||
//const url = `${authenticationType.redirect_uri}?client_id=${client_id}&redirect_uri=${redirectUri}&response_type=code&scope=${resources}&prompt=admin_consent&state=${state}&access_type=offline`;
|
|
||||||
|
|
||||||
// Skip consent
|
|
||||||
const url = `${authenticationType.redirect_uri}?client_id=${client_id}&redirect_uri=${redirectUri}&response_type=code&scope=${resources}&state=${state}&access_type=offline`;
|
|
||||||
|
|
||||||
//const url = `https://accounts.zoho.com/oauth/v2/auth?response_type=code&client_id=${client_id}&scope=AaaServer.profile.Read&redirect_uri=${redirectUri}&prompt=consent`
|
//const url = `https://accounts.zoho.com/oauth/v2/auth?response_type=code&client_id=${client_id}&scope=AaaServer.profile.Read&redirect_uri=${redirectUri}&prompt=consent`
|
||||||
//console.log("Full URI: ", url)
|
|
||||||
//console.log("Redirect Uri: ", redirectUri)
|
// &resource=https%3A%2F%2Fgraph.microsoft.com&
|
||||||
// &resource=https%3A%2F%2Fgraph.microsoft.com&
|
|
||||||
|
|
||||||
// FIXME: Awful, but works for prototyping
|
// FIXME: Awful, but works for prototyping
|
||||||
// How can we get a callback properly realtime?
|
// How can we get a callback properly realtime?
|
||||||
@@ -188,12 +253,20 @@ const AuthenticationOauth2 = (props) => {
|
|||||||
var open = true;
|
var open = true;
|
||||||
const timer = setInterval(() => {
|
const timer = setInterval(() => {
|
||||||
if (newwin.closed) {
|
if (newwin.closed) {
|
||||||
|
console.log("Closed!")
|
||||||
|
|
||||||
|
if (setAuthenticationModalOpen !== undefined) {
|
||||||
|
setAuthenticationModalOpen(false)
|
||||||
|
}
|
||||||
|
|
||||||
setButtonClicked(false);
|
setButtonClicked(false);
|
||||||
clearInterval(timer);
|
clearInterval(timer);
|
||||||
//alert('"Secure Payment" window closed!');
|
//alert('"Secure Payment" window closed!');
|
||||||
|
|
||||||
getAppAuthentication(true, true);
|
getAppAuthentication(true, true);
|
||||||
}
|
} else {
|
||||||
|
console.log("Not closed")
|
||||||
|
}
|
||||||
}, 1000);
|
}, 1000);
|
||||||
//do {
|
//do {
|
||||||
// setTimeout(() => {
|
// setTimeout(() => {
|
||||||
@@ -361,76 +434,48 @@ const AuthenticationOauth2 = (props) => {
|
|||||||
{isCloud && registeredApps.includes(selectedApp.name.toLowerCase()) ?
|
{isCloud && registeredApps.includes(selectedApp.name.toLowerCase()) ?
|
||||||
<span>
|
<span>
|
||||||
<Button
|
<Button
|
||||||
style={{
|
fullWidth
|
||||||
marginBottom: 20,
|
variant="contained"
|
||||||
marginTop: 20,
|
style={{
|
||||||
|
marginBottom: 20,
|
||||||
|
marginTop: 20,
|
||||||
|
flex: 1,
|
||||||
|
textTransform: "none",
|
||||||
|
textAlign: "left",
|
||||||
|
justifyContent: "flex-start",
|
||||||
|
backgroundColor: "#ffffff",
|
||||||
|
color: "#2f2f2f",
|
||||||
borderRadius: theme.palette.borderRadius,
|
borderRadius: theme.palette.borderRadius,
|
||||||
}}
|
minWidth: 350,
|
||||||
|
maxHeight: 50,
|
||||||
|
overflow: "hidden",
|
||||||
|
border: `1px solid ${theme.palette.inputColor}`,
|
||||||
|
}}
|
||||||
|
color="primary"
|
||||||
disabled={
|
disabled={
|
||||||
clientSecret.length > 0 || clientId.length > 0
|
clientSecret.length > 0 || clientId.length > 0
|
||||||
}
|
}
|
||||||
variant="contained"
|
|
||||||
fullWidth
|
fullWidth
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
// Hardcode some stuff?
|
// Hardcode some stuff?
|
||||||
// This could prolly be added to the app itself with a "default" client ID
|
// This could prolly be added to the app itself with a "default" client ID
|
||||||
console.log("APP: ", selectedApp)
|
startOauth2Request()
|
||||||
if (selectedApp.name.toLowerCase() == "outlook_graph" || selectedApp.name.toLowerCase() == "outlook_office365") {
|
|
||||||
handleOauth2Request(
|
|
||||||
"efe4c3fe-84a1-4821-a84f-23a6cfe8e72d",
|
|
||||||
"",
|
|
||||||
"https://graph.microsoft.com",
|
|
||||||
["Mail.ReadWrite", "Mail.Send"],
|
|
||||||
);
|
|
||||||
} else if (selectedApp.name.toLowerCase() == "gmail") {
|
|
||||||
handleOauth2Request(
|
|
||||||
"253565968129-c0a35knic7q1pdk6i6qk9gdkvr07ci49.apps.googleusercontent.com",
|
|
||||||
"",
|
|
||||||
"https://gmail.googleapis.com",
|
|
||||||
["https://www.googleapis.com/auth/gmail.modify",
|
|
||||||
"https://www.googleapis.com/auth/gmail.send",
|
|
||||||
"https://www.googleapis.com/auth/gmail.insert",
|
|
||||||
"https://www.googleapis.com/auth/gmail.compose"]
|
|
||||||
)
|
|
||||||
} else if (selectedApp.name.toLowerCase() == "zoho_desk") {
|
|
||||||
handleOauth2Request(
|
|
||||||
"1000.ZR5MHUW6B0L6W1VUENFGIATFS0TOJT",
|
|
||||||
"",
|
|
||||||
"https://desk.zoho.com",
|
|
||||||
["Desk.tickets.READ",
|
|
||||||
"Desk.tickets.UPDATE",
|
|
||||||
"Desk.tickets.DELETE",
|
|
||||||
"Desk.tickets.CREATE"]
|
|
||||||
)
|
|
||||||
} else if (selectedApp.name.toLowerCase() == "slack") {
|
|
||||||
handleOauth2Request(
|
|
||||||
"151779186901.2448678750935",
|
|
||||||
"",
|
|
||||||
"https://slack.com",
|
|
||||||
["admin", "chat:write", "im:read", "im:write", "search:read", "usergroups:read", "usergroups:write"]
|
|
||||||
)
|
|
||||||
} else if (selectedApp.name.toLowerCase() == "webex") {
|
|
||||||
handleOauth2Request(
|
|
||||||
"Cab184f3d7271f540443c79b5b79845e3387abbbdb3db4233a87ea3a5432fb3d5",
|
|
||||||
"",
|
|
||||||
"https://webexapis.com",
|
|
||||||
["spark:all"]
|
|
||||||
)
|
|
||||||
} else if (selectedApp.name.toLowerCase().includes("microsoft_teams")) {
|
|
||||||
handleOauth2Request(
|
|
||||||
"31cb4c84-658e-43d5-ae84-22c9142e967a",
|
|
||||||
"",
|
|
||||||
"https://graph.microsoft.com",
|
|
||||||
["ChannelMessage.Edit", "ChannelMessage.Read.All", "ChannelMessage.Send", "Chat.Create", "Chat.ReadWrite", "Chat.Read"]
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
color="primary"
|
color="primary"
|
||||||
>
|
>
|
||||||
{buttonClicked ? (
|
{buttonClicked ? (
|
||||||
<CircularProgress style={{ color: "white" }} />
|
<CircularProgress style={{ color: "#f86a3e", width: 45, height: 45, margin: "auto", }} />
|
||||||
) : (
|
) : (
|
||||||
"Auto-authenticate"
|
<span style={{display: "flex"}}>
|
||||||
|
<img
|
||||||
|
alt={selectedAction.app_name}
|
||||||
|
style={{ margin: 4, minHeight: 30, maxHeight: 30, borderRadius: theme.palette.borderRadius, }}
|
||||||
|
src={selectedAction.large_image}
|
||||||
|
/>
|
||||||
|
<Typography style={{ margin: 0, marginLeft: 10, marginTop: 5,}} variant="body1">
|
||||||
|
Auto-Authenticate
|
||||||
|
</Typography>
|
||||||
|
</span>
|
||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
<Typography style={{textAlign: "center", marginTop: 0, marginBottom: 10, }}>
|
<Typography style={{textAlign: "center", marginTop: 0, marginBottom: 10, }}>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React, {useState } from 'react';
|
import React, {useState } from 'react';
|
||||||
import {isMobile} from "react-device-detect";
|
import {isMobile} from "react-device-detect";
|
||||||
import DetectionFramework, { usecases } from "../components/DetectionFramework.jsx";
|
import AppFramework, { usecases } from "../components/AppFramework.jsx";
|
||||||
import {Link} from 'react-router-dom';
|
import {Link} from 'react-router-dom';
|
||||||
import ReactGA from 'react-ga';
|
import ReactGA from 'react-ga';
|
||||||
|
|
||||||
@@ -56,6 +56,8 @@ export const securityFramework = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
const LandingpageUsecases = (props) => {
|
const LandingpageUsecases = (props) => {
|
||||||
|
const { userdata } = props
|
||||||
|
|
||||||
const [selectedUsecase, setSelectedUsecase] = useState("Phishing")
|
const [selectedUsecase, setSelectedUsecase] = useState("Phishing")
|
||||||
const usecasekeys = usecases === undefined || usecases === null ? [] : Object.keys(usecases)
|
const usecasekeys = usecases === undefined || usecases === null ? [] : Object.keys(usecases)
|
||||||
const buttonBackground = "linear-gradient(to right, #f86a3e, #f34079)"
|
const buttonBackground = "linear-gradient(to right, #f86a3e, #f34079)"
|
||||||
@@ -169,7 +171,12 @@ const LandingpageUsecases = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
{isMobile ? null :
|
{isMobile ? null :
|
||||||
<div style={{marginLeft: 200, marginTop: 125, zIndex: 1000}}>
|
<div style={{marginLeft: 200, marginTop: 125, zIndex: 1000}}>
|
||||||
<DetectionFramework showOptions={false} selectedOption={selectedUsecase} rolling={true} />
|
<AppFramework
|
||||||
|
userdata={userdata}
|
||||||
|
showOptions={false}
|
||||||
|
selectedOption={selectedUsecase}
|
||||||
|
rolling={true}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
{isMobile ? null :
|
{isMobile ? null :
|
||||||
|
|||||||
@@ -0,0 +1,235 @@
|
|||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import ReactGA from 'react-ga';
|
||||||
|
import theme from '../theme';
|
||||||
|
import PaperComponent from "../components/PaperComponent.jsx"
|
||||||
|
import UsecaseSearch, { usecaseTypes } from "../components/UsecaseSearch.jsx"
|
||||||
|
|
||||||
|
import {
|
||||||
|
Paper,
|
||||||
|
Typography,
|
||||||
|
Divider,
|
||||||
|
IconButton,
|
||||||
|
Badge,
|
||||||
|
CircularProgress,
|
||||||
|
Tooltip,
|
||||||
|
Dialog,
|
||||||
|
} from "@material-ui/core";
|
||||||
|
|
||||||
|
import {
|
||||||
|
Close as CloseIcon,
|
||||||
|
Delete as DeleteIcon,
|
||||||
|
AutoFixHigh as AutoFixHighIcon,
|
||||||
|
Done as DoneIcon,
|
||||||
|
} from "@mui/icons-material";
|
||||||
|
|
||||||
|
const SuggestedWorkflows = (props) => {
|
||||||
|
const { globalUrl, userdata, usecaseSuggestions, frameworkData, setUsecaseSuggestions, inputSearch, apps, } = props
|
||||||
|
|
||||||
|
const [usecaseSearch, setUsecaseSearch] = React.useState("")
|
||||||
|
const [usecaseSearchType, setUsecaseSearchType] = React.useState("")
|
||||||
|
const [finishedUsecases, setFinishedUsecases] = React.useState([])
|
||||||
|
const [previousUsecase, setPreviousUsecase] = React.useState("")
|
||||||
|
const [closeWindow, setCloseWindow] = React.useState(false)
|
||||||
|
|
||||||
|
const isCloud =
|
||||||
|
window.location.host === "localhost:3002" ||
|
||||||
|
window.location.host === "shuffler.io";
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (closeWindow === true) {
|
||||||
|
console.log("WINDOW CLOSED")
|
||||||
|
finishedUsecases.push(usecaseSearch)
|
||||||
|
setFinishedUsecases(finishedUsecases)
|
||||||
|
|
||||||
|
setCloseWindow(false)
|
||||||
|
}
|
||||||
|
}, [closeWindow])
|
||||||
|
|
||||||
|
if (usecaseSuggestions === undefined || usecaseSuggestions.length === 0) {
|
||||||
|
console.log("Closing finished usecases 1")
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inputSearch !== previousUsecase) {
|
||||||
|
setPreviousUsecase(inputSearch)
|
||||||
|
setFinishedUsecases([])
|
||||||
|
}
|
||||||
|
|
||||||
|
if (finishedUsecases.length === usecaseSuggestions.length) {
|
||||||
|
console.log("Closing finished usecases 2")
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//useEffect(() => {
|
||||||
|
// //if (defaultSearch ===
|
||||||
|
// //setFinishedUsecases(finishedUsecases)
|
||||||
|
// console.log("Finished default usecase?", usecaseSearch)
|
||||||
|
//}, [usecaseSearch])
|
||||||
|
|
||||||
|
const foundZindex = usecaseSearch.length > 0 && usecaseSearchType.length > 0 ? -1 : 12500
|
||||||
|
|
||||||
|
const IndividualUsecase = (props) => {
|
||||||
|
const { usecase, index } = props
|
||||||
|
const [hovering, setHovering] = React.useState(false)
|
||||||
|
|
||||||
|
const usecasename = usecase.name
|
||||||
|
const bordercolor = usecase.color !== undefined ? usecase.color : "rgba(255,255,255,0.3)"
|
||||||
|
|
||||||
|
|
||||||
|
const srcimage = usecase.items[0].app
|
||||||
|
var dstimage = usecase.items[1].app
|
||||||
|
if (usecase.items.length > 2) {
|
||||||
|
dstimage = usecase.items[2].app
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(finishedUsecases)
|
||||||
|
|
||||||
|
const finished = finishedUsecases.includes(usecasename)
|
||||||
|
const selectedIcon = finished ? <DoneIcon /> : <AutoFixHighIcon />
|
||||||
|
|
||||||
|
if (finished) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simple visual of the usecase
|
||||||
|
return (
|
||||||
|
<Tooltip
|
||||||
|
title={`Try usecase "${usecasename}"`}
|
||||||
|
placement="top"
|
||||||
|
style={{ zIndex: 10011 }}
|
||||||
|
>
|
||||||
|
<div key={index} style={{cursor: finished ? "auto" : "pointer", marginTop: 10, padding: 10, borderRadius: theme.palette.borderRadius, border: `1px solid ${bordercolor}`, display: "flex", backgroundColor: hovering === true ? theme.palette.inputColor : theme.palette.surfaceColor, }} onMouseOver={() => {
|
||||||
|
setHovering(true)
|
||||||
|
}} onMouseOut={() => {
|
||||||
|
setHovering(false)
|
||||||
|
}} onClick={() => {
|
||||||
|
if (isCloud) {
|
||||||
|
ReactGA.event({
|
||||||
|
category: "welcome",
|
||||||
|
action: "click_suggested_workflow",
|
||||||
|
label: usecasename,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("Try usecase ", usecasename)
|
||||||
|
setUsecaseSearchType(usecase.type)
|
||||||
|
setUsecaseSearch(usecasename)
|
||||||
|
|
||||||
|
}}>
|
||||||
|
<div style={{flex: 10}}>
|
||||||
|
<Typography variant="body2">
|
||||||
|
{usecasename}
|
||||||
|
</Typography>
|
||||||
|
<div style={{display: "flex", marginTop: 5, }}>
|
||||||
|
<img alt={srcimage.name} src={srcimage.large_image} style={{borderRadius: 20, height: 30, width: 30, marginRight: 15, }}/>
|
||||||
|
<img alt={dstimage.name} src={dstimage.large_image} style={{borderRadius: 20, height: 30, width: 30, }}/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div style={{flex: 1}}>
|
||||||
|
{selectedIcon}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
//<Paper style={{width: 275, maxHeight: 400, overflow: "hidden", zIndex: 12500, padding: 25, paddingRight: 35, backgroundColor: theme.palette.surfaceColor, border: "1px solid rgba(255,255,255,0.2)", position: "absolute", top: -50, left: 50, }}>
|
||||||
|
return (
|
||||||
|
<Paper style={{margin: "auto", position: "relative", backgroundColor: theme.palette.surfaceColor, borderRadius: theme.palette.borderRadius, zIndex: foundZindex, border: "1px solid rgba(255,255,255,0.2)"}}>
|
||||||
|
<Dialog
|
||||||
|
open={usecaseSearch.length > 0 && usecaseSearchType.length > 0}
|
||||||
|
onClose={() => {
|
||||||
|
finishedUsecases.push(usecaseSearch)
|
||||||
|
setFinishedUsecases(finishedUsecases)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
setUsecaseSearch("")
|
||||||
|
setUsecaseSearchType("")
|
||||||
|
}}
|
||||||
|
PaperProps={{
|
||||||
|
style: {
|
||||||
|
pointerEvents: "auto",
|
||||||
|
backgroundColor: theme.palette.surfaceColor,
|
||||||
|
color: "white",
|
||||||
|
minWidth: 450,
|
||||||
|
padding: 50,
|
||||||
|
overflow: "hidden",
|
||||||
|
zIndex: 10012,
|
||||||
|
border: theme.palette.defaultBorder,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<IconButton
|
||||||
|
style={{
|
||||||
|
zIndex: 5000,
|
||||||
|
position: "absolute",
|
||||||
|
top: 14,
|
||||||
|
right: 18,
|
||||||
|
color: "grey",
|
||||||
|
}}
|
||||||
|
onClick={() => {
|
||||||
|
finishedUsecases.push(usecaseSearch)
|
||||||
|
setFinishedUsecases(finishedUsecases)
|
||||||
|
|
||||||
|
setUsecaseSearch("")
|
||||||
|
setUsecaseSearchType("")
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CloseIcon />
|
||||||
|
</IconButton>
|
||||||
|
<UsecaseSearch
|
||||||
|
globalUrl={globalUrl}
|
||||||
|
defaultSearch={usecaseSearchType}
|
||||||
|
usecaseSearch={usecaseSearch}
|
||||||
|
appFramework={frameworkData}
|
||||||
|
userdata={userdata}
|
||||||
|
autotry={true}
|
||||||
|
setCloseWindow={setCloseWindow}
|
||||||
|
setUsecaseSearch={setUsecaseSearch}
|
||||||
|
apps={apps}
|
||||||
|
/>
|
||||||
|
</Dialog>
|
||||||
|
|
||||||
|
|
||||||
|
<div style={{minWidth: 250, maxWidth: 250, padding: 15, borderRadius: theme.palette.borderRadius, position: "relative", }}>
|
||||||
|
<Typography variant="body1" style={{textAlign: "center"}}>
|
||||||
|
Suggested Workflows ({finishedUsecases.length}/{usecaseSuggestions.length})
|
||||||
|
</Typography>
|
||||||
|
<IconButton
|
||||||
|
style={{
|
||||||
|
zIndex: 5000,
|
||||||
|
position: "absolute",
|
||||||
|
top: 8,
|
||||||
|
right: 8,
|
||||||
|
color: "grey",
|
||||||
|
padding: 2,
|
||||||
|
}}
|
||||||
|
onClick={() => {
|
||||||
|
if (setUsecaseSuggestions !== undefined) {
|
||||||
|
setUsecaseSuggestions([])
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CloseIcon style={{height: 18, width: 18, }} />
|
||||||
|
</IconButton>
|
||||||
|
{usecaseSuggestions.map((usecase, index) => {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<IndividualUsecase
|
||||||
|
key={index}
|
||||||
|
usecase={usecase}
|
||||||
|
index={index}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</Paper>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SuggestedWorkflows;
|
||||||
File diff suppressed because it is too large
Load Diff
+348
-166
@@ -1,17 +1,23 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import Stepper from "@material-ui/core/Stepper";
|
import ReactGA from 'react-ga';
|
||||||
import Step from "@material-ui/core/Step";
|
|
||||||
import StepLabel from "@material-ui/core/StepLabel";
|
|
||||||
import Button from "@material-ui/core/Button";
|
import Button from "@material-ui/core/Button";
|
||||||
import Checkbox from '@mui/material/Checkbox';
|
import Checkbox from '@mui/material/Checkbox';
|
||||||
|
|
||||||
|
import AliceCarousel from 'react-alice-carousel';
|
||||||
|
import 'react-alice-carousel/lib/alice-carousel.css';
|
||||||
|
|
||||||
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';
|
||||||
import ExtensionIcon from '@mui/icons-material/Extension';
|
import ExtensionIcon from '@mui/icons-material/Extension';
|
||||||
|
import LightbulbIcon from '@mui/icons-material/Lightbulb';
|
||||||
|
import ArrowBackIosNewIcon from '@mui/icons-material/ArrowBackIosNew';
|
||||||
|
import ArrowForwardIosIcon from '@mui/icons-material/ArrowForwardIos';
|
||||||
|
|
||||||
import theme from '../theme';
|
import theme from '../theme';
|
||||||
import {
|
import {
|
||||||
|
Fade,
|
||||||
|
IconButton,
|
||||||
FormGroup,
|
FormGroup,
|
||||||
FormControl,
|
FormControl,
|
||||||
InputLabel,
|
InputLabel,
|
||||||
@@ -24,23 +30,91 @@ import {
|
|||||||
Typography,
|
Typography,
|
||||||
TextField,
|
TextField,
|
||||||
Zoom,
|
Zoom,
|
||||||
|
List,
|
||||||
|
ListItem,
|
||||||
|
ListItemText,
|
||||||
|
Divider,
|
||||||
|
Tooltip,
|
||||||
|
Chip,
|
||||||
} from "@material-ui/core";
|
} from "@material-ui/core";
|
||||||
|
import { useAlert } from "react-alert";
|
||||||
|
|
||||||
import { useNavigate, Link } from "react-router-dom";
|
import { useNavigate, Link } from "react-router-dom";
|
||||||
import WorkflowSearch from '../components/Workflowsearch.jsx';
|
import WorkflowSearch from '../components/Workflowsearch.jsx';
|
||||||
|
import AuthenticationItem from '../components/AuthenticationItem.jsx';
|
||||||
import WorkflowPaper from "../components/WorkflowPaper.jsx"
|
import WorkflowPaper from "../components/WorkflowPaper.jsx"
|
||||||
|
import UsecaseSearch from "../components/UsecaseSearch.jsx"
|
||||||
|
|
||||||
|
|
||||||
|
const responsive = {
|
||||||
|
0: { items: 1 },
|
||||||
|
};
|
||||||
|
|
||||||
const WelcomeForm = (props) => {
|
const WelcomeForm = (props) => {
|
||||||
const { userdata, globalUrl, discoveryWrapper, setDiscoveryWrapper } = props
|
const { userdata, globalUrl, discoveryWrapper, setDiscoveryWrapper, appFramework, getFramework, activeStep, setActiveStep, steps, skipped, setSkipped, getApps, apps, handleSetSearch, usecaseButtons, defaultSearch, setDefaultSearch, selectionOpen, setSelectionOpen, } = props
|
||||||
|
|
||||||
|
const usecaseItems = [
|
||||||
|
<div style={{minWidth: "95%", maxWidth: "95%", marginLeft: 5, marginRight: 5, }}>
|
||||||
|
<UsecaseSearch
|
||||||
|
globalUrl={globalUrl}
|
||||||
|
defaultSearch={"Phishing"}
|
||||||
|
appFramework={appFramework}
|
||||||
|
apps={apps}
|
||||||
|
getFramework={getFramework}
|
||||||
|
userdata={userdata}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
,
|
||||||
|
<div style={{minWidth: "95%", maxWidth: "95%", marginLeft: 5, marginRight: 5, }}>
|
||||||
|
<UsecaseSearch
|
||||||
|
globalUrl={globalUrl}
|
||||||
|
defaultSearch={"Enrichment"}
|
||||||
|
appFramework={appFramework}
|
||||||
|
apps={apps}
|
||||||
|
getFramework={getFramework}
|
||||||
|
userdata={userdata}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
,
|
||||||
|
<div style={{minWidth: "95%", maxWidth: "95%", marginLeft: 5, marginRight: 5, }}>
|
||||||
|
<UsecaseSearch
|
||||||
|
globalUrl={globalUrl}
|
||||||
|
defaultSearch={"Enrichment"}
|
||||||
|
usecaseSearch={"SIEM alert enrichment"}
|
||||||
|
appFramework={appFramework}
|
||||||
|
apps={apps}
|
||||||
|
getFramework={getFramework}
|
||||||
|
userdata={userdata}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
,
|
||||||
|
<div style={{minWidth: "95%", maxWidth: "95%", marginLeft: 5, marginRight: 5, }}>
|
||||||
|
<UsecaseSearch
|
||||||
|
globalUrl={globalUrl}
|
||||||
|
defaultSearch={"Build your own"}
|
||||||
|
appFramework={appFramework}
|
||||||
|
apps={apps}
|
||||||
|
getFramework={getFramework}
|
||||||
|
userdata={userdata}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
]
|
||||||
|
|
||||||
const [activeStep, setActiveStep] = React.useState(0);
|
|
||||||
const [skipped, setSkipped] = React.useState(new Set());
|
|
||||||
const [discoveryData, setDiscoveryData] = React.useState({})
|
const [discoveryData, setDiscoveryData] = React.useState({})
|
||||||
const [name, setName] = React.useState("")
|
const [name, setName] = React.useState("")
|
||||||
const [orgName, setOrgName] = React.useState("")
|
const [orgName, setOrgName] = React.useState("")
|
||||||
const [role, setRole] = React.useState("")
|
const [role, setRole] = React.useState("")
|
||||||
const [orgType, setOrgType] = React.useState("")
|
const [orgType, setOrgType] = React.useState("")
|
||||||
const [finishedApps, setFinishedApps] = React.useState([])
|
const [finishedApps, setFinishedApps] = React.useState([])
|
||||||
|
const [authentication, setAuthentication] = React.useState([]);
|
||||||
|
const [newSelectedApp, setNewSelectedApp] = React.useState({})
|
||||||
|
const [thumbIndex, setThumbIndex] = useState(0);
|
||||||
|
const [thumbAnimation, setThumbAnimation] = useState(false);
|
||||||
|
const [clickdiff, setclickdiff] = useState(0);
|
||||||
|
|
||||||
|
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
|
||||||
|
|
||||||
|
const alert = useAlert();
|
||||||
let navigate = useNavigate();
|
let navigate = useNavigate();
|
||||||
|
|
||||||
const onNodeSelect = (label) => {
|
const onNodeSelect = (label) => {
|
||||||
@@ -77,8 +151,7 @@ const WelcomeForm = (props) => {
|
|||||||
}
|
}
|
||||||
}, [discoveryWrapper])
|
}, [discoveryWrapper])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
||||||
if (
|
if (
|
||||||
window.location.search !== undefined &&
|
window.location.search !== undefined &&
|
||||||
window.location.search !== null
|
window.location.search !== null
|
||||||
@@ -87,24 +160,20 @@ const WelcomeForm = (props) => {
|
|||||||
const params = Object.fromEntries(urlSearchParams.entries());
|
const params = Object.fromEntries(urlSearchParams.entries());
|
||||||
const foundTab = params["tab"];
|
const foundTab = params["tab"];
|
||||||
if (foundTab !== null && foundTab !== undefined && !isNaN(foundTab)) {
|
if (foundTab !== null && foundTab !== undefined && !isNaN(foundTab)) {
|
||||||
setActiveStep(foundTab-1)
|
if (foundTab === 3 || foundTab === "3") {
|
||||||
|
console.log("SET SEARCH!!")
|
||||||
|
//setclickdiff(240)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
navigate(`/welcome?tab=1`)
|
//navigate(`/welcome?tab=1`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const steps = getSteps();
|
|
||||||
|
|
||||||
const isStepOptional = step => {
|
const isStepOptional = step => {
|
||||||
return step === 1
|
return step === 1
|
||||||
}
|
}
|
||||||
|
|
||||||
const isStepSkipped = step => {
|
|
||||||
return skipped.has(step)
|
|
||||||
}
|
|
||||||
|
|
||||||
const sendUserUpdate = (name, role, userId) => {
|
const sendUserUpdate = (name, role, userId) => {
|
||||||
const data = {
|
const data = {
|
||||||
"tutorial": "welcome",
|
"tutorial": "welcome",
|
||||||
@@ -177,7 +246,6 @@ const WelcomeForm = (props) => {
|
|||||||
console.log("Update of org failed")
|
console.log("Update of org failed")
|
||||||
//alert.error("Failed updating org: ", responseJson.reason);
|
//alert.error("Failed updating org: ", responseJson.reason);
|
||||||
} else {
|
} else {
|
||||||
console.log("Update success!")
|
|
||||||
//alert.success("Successfully edited org!");
|
//alert.success("Successfully edited org!");
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -221,12 +289,25 @@ const WelcomeForm = (props) => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isStepSkipped = step => {
|
||||||
|
return skipped.has(step)
|
||||||
|
}
|
||||||
|
|
||||||
const handleNext = () => {
|
const handleNext = () => {
|
||||||
setDefaultSearch("")
|
setDefaultSearch("")
|
||||||
|
|
||||||
if (activeStep === 0) {
|
if (activeStep === 0) {
|
||||||
console.log("Should send basic information about org (fetch)")
|
console.log("Should send basic information about org (fetch)")
|
||||||
|
setclickdiff(0)
|
||||||
navigate(`/welcome?tab=2`)
|
navigate(`/welcome?tab=2`)
|
||||||
|
|
||||||
|
if (isCloud) {
|
||||||
|
ReactGA.event({
|
||||||
|
category: "welcome",
|
||||||
|
action: "click_page_one_next",
|
||||||
|
label: "",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
if (userdata.active_org !== undefined && userdata.active_org.id !== undefined && userdata.active_org.id !== null && userdata.active_org.id.length > 0) {
|
if (userdata.active_org !== undefined && userdata.active_org.id !== undefined && userdata.active_org.id !== null && userdata.active_org.id.length > 0) {
|
||||||
sendOrgUpdate(orgName, orgType, userdata.active_org.id, "")
|
sendOrgUpdate(orgName, orgType, userdata.active_org.id, "")
|
||||||
@@ -240,11 +321,22 @@ const WelcomeForm = (props) => {
|
|||||||
console.log("Should send secondary info about apps and other things")
|
console.log("Should send secondary info about apps and other things")
|
||||||
setDiscoveryWrapper({})
|
setDiscoveryWrapper({})
|
||||||
|
|
||||||
|
setclickdiff(240)
|
||||||
|
//setclickdiff(0)
|
||||||
navigate(`/welcome?tab=3`)
|
navigate(`/welcome?tab=3`)
|
||||||
|
//handleSetSearch("Enrichment", "2. Enrich")
|
||||||
|
handleSetSearch(usecaseButtons[0].name, usecaseButtons[0].usecase)
|
||||||
|
getApps()
|
||||||
|
|
||||||
|
// Make sure it's up to date
|
||||||
|
if (getFramework !== undefined) {
|
||||||
|
getFramework()
|
||||||
|
}
|
||||||
} else if (activeStep === 2) {
|
} else if (activeStep === 2) {
|
||||||
console.log("Should send third page with workflows activated and the like")
|
console.log("Should send third page with workflows activated and the like")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
let newSkipped = skipped;
|
let newSkipped = skipped;
|
||||||
if (isStepSkipped(activeStep)) {
|
if (isStepSkipped(activeStep)) {
|
||||||
newSkipped = new Set(newSkipped.values());
|
newSkipped = new Set(newSkipped.values());
|
||||||
@@ -253,11 +345,27 @@ const WelcomeForm = (props) => {
|
|||||||
|
|
||||||
setActiveStep(prevActiveStep => prevActiveStep + 1);
|
setActiveStep(prevActiveStep => prevActiveStep + 1);
|
||||||
setSkipped(newSkipped);
|
setSkipped(newSkipped);
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleBack = () => {
|
const handleBack = () => {
|
||||||
setActiveStep(prevActiveStep => prevActiveStep - 1);
|
setActiveStep(prevActiveStep => prevActiveStep - 1);
|
||||||
|
|
||||||
|
if (activeStep === 2) {
|
||||||
|
setDiscoveryWrapper({})
|
||||||
|
|
||||||
|
if (getFramework !== undefined) {
|
||||||
|
getFramework()
|
||||||
|
}
|
||||||
|
setclickdiff(0)
|
||||||
|
navigate("/welcome?tab=2")
|
||||||
|
} else if (activeStep === 1) {
|
||||||
|
//setclickdiff(0)
|
||||||
|
navigate("/welcome?tab=1")
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSkip = () => {
|
const handleSkip = () => {
|
||||||
|
setclickdiff(240)
|
||||||
if (!isStepOptional(activeStep)) {
|
if (!isStepOptional(activeStep)) {
|
||||||
throw new Error("You can't skip a step that isn't optional.");
|
throw new Error("You can't skip a step that isn't optional.");
|
||||||
}
|
}
|
||||||
@@ -271,27 +379,16 @@ const WelcomeForm = (props) => {
|
|||||||
const handleReset = () => {
|
const handleReset = () => {
|
||||||
setActiveStep(0);
|
setActiveStep(0);
|
||||||
};
|
};
|
||||||
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
|
|
||||||
const [selectionOpen, setSelectionOpen] = React.useState(false)
|
|
||||||
const [newSelectedApp, setNewSelectedApp] = React.useState({})
|
|
||||||
const [defaultSearch, setDefaultSearch] = React.useState("")
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log("Selected app changed (effect)")
|
console.log("Selected app changed (effect)")
|
||||||
}, [newSelectedApp])
|
}, [newSelectedApp])
|
||||||
|
|
||||||
function getSteps() {
|
|
||||||
return [
|
|
||||||
"Help us get to know you",
|
|
||||||
"Discover integrations",
|
|
||||||
"Personalize Usecases",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
//const buttonWidth = 145
|
//const buttonWidth = 145
|
||||||
const buttonWidth = 450
|
const buttonWidth = 450
|
||||||
const buttonMargin = 10
|
const buttonMargin = 10
|
||||||
const sizing = 435
|
const sizing = 475
|
||||||
const buttonStyle = {
|
const buttonStyle = {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
width: "100%",
|
width: "100%",
|
||||||
@@ -300,6 +397,23 @@ const WelcomeForm = (props) => {
|
|||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const slideNext = () => {
|
||||||
|
if (!thumbAnimation && thumbIndex < usecaseItems.length - 1) {
|
||||||
|
//handleSetSearch(usecaseButtons[0].name, usecaseButtons[0].usecase)
|
||||||
|
setThumbIndex(thumbIndex + 1);
|
||||||
|
} else if (!thumbAnimation && thumbIndex === usecaseItems.length - 1) {
|
||||||
|
setThumbIndex(0)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const slidePrev = () => {
|
||||||
|
if (!thumbAnimation && thumbIndex > 0) {
|
||||||
|
setThumbIndex(thumbIndex - 1);
|
||||||
|
} else if (!thumbAnimation && thumbIndex === 0) {
|
||||||
|
setThumbIndex(usecaseItems.length-1)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const newButtonStyle = {
|
const newButtonStyle = {
|
||||||
padding: 22,
|
padding: 22,
|
||||||
flex: 1,
|
flex: 1,
|
||||||
@@ -307,22 +421,20 @@ const WelcomeForm = (props) => {
|
|||||||
minWidth: buttonWidth,
|
minWidth: buttonWidth,
|
||||||
maxWidth: buttonWidth,
|
maxWidth: buttonWidth,
|
||||||
}
|
}
|
||||||
|
|
||||||
const getStepContent = (step) => {
|
const getStepContent = (step) => {
|
||||||
switch (step) {
|
switch (step) {
|
||||||
case 0:
|
case 0:
|
||||||
return (
|
return (
|
||||||
<Grid container spacing={1} style={{width: "100%", marginTop: 20, minHeight: sizing, maxHeight: sizing, }}>
|
<Fade in={true}>
|
||||||
|
<Grid container spacing={1} style={{margin: "auto", maxWidth: 500, minWidth: 500, minHeight: sizing, maxHeight: sizing, }}>
|
||||||
{/*isCloud ? null :
|
{/*isCloud ? null :
|
||||||
<Typography variant="body1" style={{marginLeft: 8, marginTop: 10, marginRight: 30, }} color="textSecondary">
|
<Typography variant="body1" style={{marginLeft: 8, marginTop: 10, marginRight: 30, }} color="textSecondary">
|
||||||
This data will be used within the product and NOT be shared unless <a href="https://shuffler.io/docs/organizations#cloud_synchronization" target="_blank" rel="norefferer" style={{color: "#f86a3e", textDecoration: "none"}}>cloud synchronization</a> is configured.
|
This data will be used within the product and NOT be shared unless <a href="https://shuffler.io/docs/organizations#cloud_synchronization" target="_blank" rel="norefferer" style={{color: "#f86a3e", textDecoration: "none"}}>cloud synchronization</a> is configured.
|
||||||
</Typography>
|
</Typography>
|
||||||
*/}
|
*/}
|
||||||
<Typography variant="h6" style={{marginLeft: 8, marginTop: 10, marginRight: 30, }} color="textSecondary">
|
|
||||||
Welcome to Shuffle!
|
|
||||||
</Typography>
|
|
||||||
<Typography variant="body1" style={{marginLeft: 8, marginTop: 10, marginRight: 30, }} color="textSecondary">
|
<Typography variant="body1" style={{marginLeft: 8, marginTop: 10, marginRight: 30, }} color="textSecondary">
|
||||||
We need some more information in order to understand how we best can help you find relevant Usecases in Shuffle.
|
First, we need some more information in order to understand how we best can help you find relevant Usecases. This is optional, but highly encouraged.
|
||||||
</Typography>
|
</Typography>
|
||||||
<Grid item xs={11} style={{marginTop: 16, padding: 0,}}>
|
<Grid item xs={11} style={{marginTop: 16, padding: 0,}}>
|
||||||
<TextField
|
<TextField
|
||||||
@@ -395,12 +507,20 @@ const WelcomeForm = (props) => {
|
|||||||
</FormControl>
|
</FormControl>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
</Fade>
|
||||||
)
|
)
|
||||||
case 1:
|
case 1:
|
||||||
return (
|
return (
|
||||||
<div style={{minHeight: sizing, maxHeight: sizing, marginTop: 20,}}>
|
<Fade in={true}>
|
||||||
|
<div style={{minHeight: sizing, maxHeight: sizing, marginTop: 20, maxWidth: 500, }}>
|
||||||
<Typography variant="body1" style={{marginLeft: 8, marginTop: 25, marginRight: 30, marginBottom: 0, }} color="textSecondary">
|
<Typography variant="body1" style={{marginLeft: 8, marginTop: 25, marginRight: 30, marginBottom: 0, }} color="textSecondary">
|
||||||
Find your apps, then we'll help you find relevant workflows. Can't find what you're looking for? Contact support: <a href="mailto:support@shuffler.io" style={{color: "#f86a3e", textDecoration: "none"}}>support@shuffler.io</a>
|
Clicks the buttons below to find your apps, then we will help you find relevant workflows. Can't find your app? <span style={{color: "#f86a3e", cursor: "pointer"}} onClick={() => {
|
||||||
|
if (window.drift !== undefined) {
|
||||||
|
window.drift.api.startInteraction({ interactionId: 340043 })
|
||||||
|
} else {
|
||||||
|
console.log("Couldn't find drift in window.drift and not .drift-open-chat with querySelector: ", window.drift)
|
||||||
|
}
|
||||||
|
}}>Contact our App Developers!</span>
|
||||||
</Typography>
|
</Typography>
|
||||||
{/*The app framework helps us access and authenticate the most important APIs for you. */}
|
{/*The app framework helps us access and authenticate the most important APIs for you. */}
|
||||||
|
|
||||||
@@ -420,11 +540,16 @@ const WelcomeForm = (props) => {
|
|||||||
*/}
|
*/}
|
||||||
<Grid item xs={11} style={{marginTop: 25, }}>
|
<Grid item xs={11} style={{marginTop: 25, }}>
|
||||||
{/*<FormLabel style={{ color: "#B9B9BA" }}>Find your integrations!</FormLabel>*/}
|
{/*<FormLabel style={{ color: "#B9B9BA" }}>Find your integrations!</FormLabel>*/}
|
||||||
|
<div style={{display: "flex"}}>
|
||||||
|
<Button disabled={finishedApps.includes("CASES")} variant={defaultSearch === "CASES" ? "contained" : "outlined"} style={buttonStyle} startIcon={<LightbulbIcon />} onClick={(event) => { onNodeSelect("CASES") }} >
|
||||||
|
Case Management
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
<div style={{display: "flex"}}>
|
<div style={{display: "flex"}}>
|
||||||
<Button disabled={finishedApps.includes("SIEM")} variant={defaultSearch === "SIEM" ? "contained" : "outlined"} style={buttonStyle} startIcon={<SearchIcon />} onClick={(event) => { onNodeSelect("SIEM") }} >
|
<Button disabled={finishedApps.includes("SIEM")} variant={defaultSearch === "SIEM" ? "contained" : "outlined"} style={buttonStyle} startIcon={<SearchIcon />} onClick={(event) => { onNodeSelect("SIEM") }} >
|
||||||
SIEM
|
SIEM
|
||||||
</Button>
|
</Button>
|
||||||
<Button disabled={finishedApps.includes("EDR & AV")} variant={defaultSearch === "EDR & AV" ? "contained" : "outlined"} style={buttonStyle} startIcon={<NewReleasesIcon />} onClick={(event) => { onNodeSelect("EDR & AV") }} >
|
<Button disabled={finishedApps.includes("EDR & AV") || finishedApps.includes("ERADICATION")} variant={defaultSearch === "Eradication" ? "contained" : "outlined"} style={buttonStyle} startIcon={<NewReleasesIcon />} onClick={(event) => { onNodeSelect("ERADICATION") }} >
|
||||||
Endpoint
|
Endpoint
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
@@ -434,7 +559,7 @@ const WelcomeForm = (props) => {
|
|||||||
|
|
||||||
Intel
|
Intel
|
||||||
</Button>
|
</Button>
|
||||||
<Button disabled={finishedApps.includes("COMMS")} variant={defaultSearch === "EMAIL" ? "contained" : "outlined"} style={buttonStyle} startIcon={<EmailIcon />} onClick={(event) => { onNodeSelect("EMAIL") }} >
|
<Button disabled={finishedApps.includes("COMMS") || finishedApps.includes("EMAIL")} variant={defaultSearch === "EMAIL" ? "contained" : "outlined"} style={buttonStyle} startIcon={<EmailIcon />} onClick={(event) => { onNodeSelect("EMAIL") }} >
|
||||||
Email
|
Email
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
@@ -478,94 +603,123 @@ const WelcomeForm = (props) => {
|
|||||||
</Grid>
|
</Grid>
|
||||||
*/}
|
*/}
|
||||||
</div>
|
</div>
|
||||||
|
</Fade>
|
||||||
)
|
)
|
||||||
case 2:
|
case 2:
|
||||||
return (
|
return (
|
||||||
<div style={{display: "flex", marginTop: 25, width: 1200, minHeight: sizing, maxHeight: sizing, }}>
|
<Fade in={true}>
|
||||||
<Grid item xs={10} style={{width: "100%", flex: 5, }}>
|
<div style={{marginTop: 0, maxWidth: 700, minWidth: 700, margin: "auto", minHeight: sizing, maxHeight: sizing, }}>
|
||||||
<Typography variant="body1" style={{marginLeft: 8, marginTop: 0, marginRight: 30, marginBottom: 0, maxWidth: 500, }} color="textSecondary">
|
<Typography variant="body1" style={{marginTop: 15, marginBottom: 0, maxWidth: 500, margin: "auto", marginBottom: 15, }} color="textSecondary">
|
||||||
What usecases are you interested in? This will help us suggest relevant Workflows.
|
These are some of our Workflow templates, used to start new Workflows. Use the right and left buttons to find <a href="/usecases" target="_blank" rel="norefferer" style={{color: "#f86a3e", textDecoration: "none", }}>new Usecases</a>, and click the orange button to build it.
|
||||||
</Typography>
|
</Typography>
|
||||||
|
{/*<Divider />*/}
|
||||||
<Grid item xs={5} style={{marginTop: 15, display: "flex", flexDirection: "column", width: "100%",}}>
|
|
||||||
<Button variant={defaultSearch === "Enrichment" ? "contained" : "outlined"} startIcon={<SearchIcon />} style={newButtonStyle} onClick={() => {
|
|
||||||
setDefaultSearch("Enrichment")
|
|
||||||
|
|
||||||
setSelectionOpen(false)
|
|
||||||
|
|
||||||
setTimeout(function(){
|
|
||||||
setSelectionOpen(true)
|
|
||||||
}, 150)
|
|
||||||
|
|
||||||
sendOrgUpdate("", "", userdata.active_org.id, "2. Enrich")
|
|
||||||
}}>
|
|
||||||
Enrichment
|
|
||||||
</Button>
|
|
||||||
<Button variant={defaultSearch === "Phishing" ? "contained" : "outlined"} startIcon={<EmailIcon />} style={newButtonStyle} onClick={() => {
|
|
||||||
setDefaultSearch("Phishing")
|
|
||||||
setSelectionOpen(false)
|
|
||||||
|
|
||||||
setTimeout(function(){
|
|
||||||
setSelectionOpen(true)
|
|
||||||
}, 150)
|
|
||||||
|
|
||||||
sendOrgUpdate("", "", userdata.active_org.id, "Email management")
|
|
||||||
}}>
|
|
||||||
Phishing
|
|
||||||
</Button>
|
|
||||||
<Button variant={defaultSearch === "Detection" ? "contained" : "outlined"} startIcon={<NewReleasesIcon />} style={newButtonStyle} onClick={() => {
|
|
||||||
setDefaultSearch("Detection")
|
|
||||||
setSelectionOpen(false)
|
|
||||||
|
|
||||||
setTimeout(function(){
|
|
||||||
setSelectionOpen(true)
|
|
||||||
}, 150);
|
|
||||||
|
|
||||||
sendOrgUpdate("", "", userdata.active_org.id, "3. Detect")
|
|
||||||
}}>
|
|
||||||
Detection
|
|
||||||
</Button>
|
|
||||||
<Button variant={defaultSearch === "Response" ? "contained" : "outlined"} startIcon={<NewReleasesIcon />} style={newButtonStyle} onClick={() => {
|
|
||||||
setDefaultSearch("Response")
|
|
||||||
setSelectionOpen(false)
|
|
||||||
|
|
||||||
setTimeout(function(){
|
|
||||||
setSelectionOpen(true)
|
|
||||||
}, 150);
|
|
||||||
|
|
||||||
sendOrgUpdate("", "", userdata.active_org.id, "4. Respond")
|
|
||||||
}}>
|
|
||||||
Response
|
|
||||||
</Button>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
{/*
|
{/*
|
||||||
<Grid item xs={10} paddingBottom="20px">
|
<div style={{width: 475, margin: "auto",}}>
|
||||||
<TextField
|
{usecaseButtons.map((usecase, index) => {
|
||||||
required
|
|
||||||
fullWidth={true}
|
return (
|
||||||
placeholder="Workflows as suggested from tools"
|
<Chip
|
||||||
label="Workflows as suggested from tools"
|
key={usecase.name}
|
||||||
type="astoolsworkflow"
|
style={{
|
||||||
id="standard-required"
|
backgroundColor: defaultSearch === usecase.name ? usecase.color : theme.palette.surfaceColor,
|
||||||
autoComplete="astoolsworkflow"
|
marginRight: 10,
|
||||||
margin="normal"
|
paddingLeft: 5,
|
||||||
variant="outlined"
|
paddingRight: 5,
|
||||||
/>
|
height: 28,
|
||||||
</Grid>
|
cursor: "pointer",
|
||||||
|
border: `1px solid ${usecase.color}`,
|
||||||
|
color: "white",
|
||||||
|
borderRadius: theme.palette.borderRadius,
|
||||||
|
}}
|
||||||
|
label={`${index+1}. ${usecase.name}`}
|
||||||
|
onClick={() => {
|
||||||
|
console.log("Clicked: ", usecase.name)
|
||||||
|
if (defaultSearch === usecase.name) {
|
||||||
|
//setSelectedUsecaseCategory("")
|
||||||
|
} else {
|
||||||
|
handleSetSearch(usecase.name, usecase.usecase)
|
||||||
|
}
|
||||||
|
//addFilter(usecase.name.slice(3,usecase.name.length))
|
||||||
|
}}
|
||||||
|
variant="outlined"
|
||||||
|
color="primary"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
*/}
|
*/}
|
||||||
<div style={{marginTop: 15, flex: 6, }}>
|
<div style={{marginTop: 0, }}>
|
||||||
{selectionOpen === true ?
|
{/*
|
||||||
<WorkflowSearch
|
<UsecaseSearch
|
||||||
ConfiguredHits={NewHits}
|
globalUrl={globalUrl}
|
||||||
showSearch={false}
|
|
||||||
defaultSearch={defaultSearch}
|
defaultSearch={defaultSearch}
|
||||||
newSelectedApp={newSelectedApp}
|
appFramework={appFramework}
|
||||||
setNewSelectedApp={setNewSelectedApp}
|
apps={apps}
|
||||||
/>
|
/>
|
||||||
: null}
|
*/}
|
||||||
|
|
||||||
|
<div className="thumbs" style={{display: "flex"}}>
|
||||||
|
<Tooltip title={"Previous usecase"}>
|
||||||
|
<IconButton
|
||||||
|
style={{
|
||||||
|
backgroundColor: thumbIndex === 0 ? "inherit" : "white",
|
||||||
|
zIndex: 5000,
|
||||||
|
minHeight: 50,
|
||||||
|
maxHeight: 50,
|
||||||
|
color: "grey",
|
||||||
|
marginTop: 150,
|
||||||
|
borderRadius: 50,
|
||||||
|
border: "1px solid rgba(255,255,255,0.3)",
|
||||||
|
}}
|
||||||
|
onClick={() => {
|
||||||
|
slidePrev()
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ArrowBackIosNewIcon />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
<div style={{minWidth: 554, maxWidth: 554, borderRadius: theme.palette.borderRadius, padding: 25, }}>
|
||||||
|
<AliceCarousel
|
||||||
|
style={{ backgroundColor: theme.palette.surfaceColor, minHeight: 750, maxHeight: 750, }}
|
||||||
|
items={usecaseItems}
|
||||||
|
activeIndex={thumbIndex}
|
||||||
|
infiniteLoop
|
||||||
|
mouseTracking
|
||||||
|
responsive={responsive}
|
||||||
|
// activeIndex={activeIndex}
|
||||||
|
controlsStrategy="responsive"
|
||||||
|
autoPlay={false}
|
||||||
|
infinite={true}
|
||||||
|
animationType="fadeout"
|
||||||
|
animationDuration={800}
|
||||||
|
disableButtonsControls
|
||||||
|
disableDotsControls
|
||||||
|
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Tooltip title={"Next usecase"}>
|
||||||
|
<IconButton
|
||||||
|
style={{
|
||||||
|
backgroundColor: thumbIndex === usecaseButtons.length-1 ? "inherit" : "white",
|
||||||
|
zIndex: 5000,
|
||||||
|
minHeight: 50,
|
||||||
|
maxHeight: 50,
|
||||||
|
color: "grey",
|
||||||
|
marginTop: 150,
|
||||||
|
borderRadius: 50,
|
||||||
|
border: "1px solid rgba(255,255,255,0.3)",
|
||||||
|
}}
|
||||||
|
onClick={() => {
|
||||||
|
slideNext()
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ArrowForwardIosIcon />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</Fade>
|
||||||
)
|
)
|
||||||
default:
|
default:
|
||||||
return "unknown step"
|
return "unknown step"
|
||||||
@@ -573,26 +727,7 @@ const WelcomeForm = (props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{paddingTop: 20}}>
|
<div style={{}}>
|
||||||
<Stepper activeStep={activeStep} style={{backgroundColor: theme.palette.platformColor, borderRadius: theme.palette.borderRadius, padding: 12, border: "1px solid rgba(255,255,255,0.3)",}}>
|
|
||||||
{steps.map((label, index) => {
|
|
||||||
const stepProps = {}
|
|
||||||
const labelProps = {}
|
|
||||||
//if (isStepOptional(index)) {
|
|
||||||
// labelProps.optional = "optional"
|
|
||||||
//}
|
|
||||||
|
|
||||||
if (isStepSkipped(index)) {
|
|
||||||
stepProps.completed = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Step key={label} {...stepProps}>
|
|
||||||
<StepLabel {...labelProps} style={{marginLeft: 10}}>{label}</StepLabel>
|
|
||||||
</Step>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</Stepper>
|
|
||||||
{/*selectionOpen ?
|
{/*selectionOpen ?
|
||||||
<WorkflowSearch
|
<WorkflowSearch
|
||||||
defaultSearch={defaultSearch}
|
defaultSearch={defaultSearch}
|
||||||
@@ -619,31 +754,78 @@ const WelcomeForm = (props) => {
|
|||||||
) : (
|
) : (
|
||||||
<div>
|
<div>
|
||||||
{getStepContent(activeStep)}
|
{getStepContent(activeStep)}
|
||||||
<div style={{paddingTop: 20}}>
|
<div style={{marginBottom: 20, }}/>
|
||||||
<Button disabled={activeStep === 0} onClick={handleBack}>
|
{activeStep === 2 || activeStep === 1 ?
|
||||||
Back
|
<div style={{margin: "auto", minWidth: 500, maxWidth: 500, position: "relative", }}>
|
||||||
</Button>
|
<Button
|
||||||
{/*isStepOptional(activeStep) && (
|
disabled={activeStep === 0}
|
||||||
<Button
|
onClick={handleBack}
|
||||||
variant="contained"
|
variant={"outlined"}
|
||||||
color="primary"
|
style={{marginLeft: 10, height: 64, width: 100, position: "absolute", top: activeStep === 1 ? -594 : -577, left: activeStep === 1 ? 105+clickdiff : -145+clickdiff, }}
|
||||||
onClick={handleSkip}
|
>
|
||||||
>
|
Back
|
||||||
Skip
|
</Button>
|
||||||
</Button>
|
<Button
|
||||||
)*/}
|
variant={"outlined"}
|
||||||
<Button
|
color="primary"
|
||||||
variant={activeStep === 1 ? finishedApps.length === 4 ? "contained" : "outlined" : "contained"}
|
onClick={handleNext}
|
||||||
color="primary"
|
style={{marginLeft: 10, height: 64, width: 100, position: "absolute", top: activeStep === 1 ? -594 : -577, left: activeStep === 1 ? 758+clickdiff : 510+clickdiff, }}
|
||||||
onClick={handleNext}
|
disabled={activeStep === 0 ? orgName.length === 0 || name.length === 0 : false}
|
||||||
style={{marginLeft: 10, }}
|
>
|
||||||
disabled={activeStep === 0 ? orgName.length === 0 || name.length === 0 : false}
|
{activeStep === steps.length - 1 ? "Finish" : "Next"}
|
||||||
>
|
</Button>
|
||||||
{activeStep === steps.length - 1 ? "Finish" : "Next"}
|
</div>
|
||||||
</Button>
|
:
|
||||||
</div>
|
<div style={{margin: "auto", minWidth: 500, maxWidth: 500, marginLeft: activeStep === 1 ? 250 : "auto", marginTop: activeStep === 0 ? 25 : 0, }}>
|
||||||
</div>
|
<Button disabled={activeStep === 0} onClick={handleBack}>
|
||||||
)}
|
Back
|
||||||
|
</Button>
|
||||||
|
{/*isStepOptional(activeStep) && (
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
onClick={handleSkip}
|
||||||
|
>
|
||||||
|
Skip
|
||||||
|
</Button>
|
||||||
|
)*/}
|
||||||
|
<Button
|
||||||
|
variant={activeStep === 1 ? finishedApps.length >= 4 ? "contained" : "outlined" : "outlined"}
|
||||||
|
color="primary"
|
||||||
|
onClick={handleNext}
|
||||||
|
style={{marginLeft: 10, }}
|
||||||
|
disabled={activeStep === 0 ? orgName.length === 0 || name.length === 0 : false}
|
||||||
|
>
|
||||||
|
{activeStep === steps.length - 1 ? "Finish" : "Next"}
|
||||||
|
</Button>
|
||||||
|
{activeStep === 0 ?
|
||||||
|
<Button
|
||||||
|
variant={"outlined"}
|
||||||
|
color="secondary"
|
||||||
|
onClick={() => {
|
||||||
|
console.log("Skip!")
|
||||||
|
|
||||||
|
if (isCloud) {
|
||||||
|
ReactGA.event({
|
||||||
|
category: "welcome",
|
||||||
|
action: "click_page_one_skip",
|
||||||
|
label: "",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
setActiveStep(1)
|
||||||
|
navigate(`/welcome?tab=2`)
|
||||||
|
}}
|
||||||
|
style={{marginLeft: 240, }}
|
||||||
|
disabled={activeStep !== 0}
|
||||||
|
>
|
||||||
|
Skip
|
||||||
|
</Button>
|
||||||
|
: null}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { useInterval } from "react-powerhooks";
|
import { useInterval } from "react-powerhooks";
|
||||||
import DetectionFramework from "../components/DetectionFramework.jsx";
|
import AppFramework from "../components/AppFramework.jsx";
|
||||||
import { makeStyles, useTheme } from "@material-ui/core/styles";
|
import { makeStyles, useTheme } from "@material-ui/core/styles";
|
||||||
// nodejs library that concatenates classes
|
// nodejs library that concatenates classes
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
@@ -834,7 +834,7 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLogged
|
|||||||
borderRadius: theme.palette.borderRadius,
|
borderRadius: theme.palette.borderRadius,
|
||||||
border: "1px solid rgba(255,255,255,0.3)",
|
border: "1px solid rgba(255,255,255,0.3)",
|
||||||
}}>
|
}}>
|
||||||
<DetectionFramework
|
<AppFramework
|
||||||
inputUsecase={inputUsecase}
|
inputUsecase={inputUsecase}
|
||||||
frameworkData={frameworkData}
|
frameworkData={frameworkData}
|
||||||
selectedOption={"Draw"}
|
selectedOption={"Draw"}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { useInterval } from "react-powerhooks";
|
import { useInterval } from "react-powerhooks";
|
||||||
import DetectionFramework from "../components/DetectionFramework.jsx";
|
|
||||||
import { makeStyles, useTheme } from "@material-ui/core/styles";
|
import { makeStyles, useTheme } from "@material-ui/core/styles";
|
||||||
// nodejs library that concatenates classes
|
// nodejs library that concatenates classes
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import ReactDOM from "react-dom"
|
import ReactDOM from "react-dom"
|
||||||
|
|
||||||
import DetectionFramework from "../components/DetectionFramework.jsx";
|
import AppFramework from "../components/AppFramework.jsx";
|
||||||
import { useAlert } from "react-alert";
|
import { useAlert } from "react-alert";
|
||||||
import { Link, useParams } from "react-router-dom";
|
import { Link, useParams } from "react-router-dom";
|
||||||
import theme from '../theme';
|
import theme from '../theme';
|
||||||
@@ -69,7 +69,7 @@ const Framework = (props) => {
|
|||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
{frameworkLoaded === true && isLoaded ?
|
{frameworkLoaded === true && isLoaded ?
|
||||||
<DetectionFramework
|
<AppFramework
|
||||||
frameworkData={frameworkData}
|
frameworkData={frameworkData}
|
||||||
selectedOption={"Draw"}
|
selectedOption={"Draw"}
|
||||||
showOptions={false}
|
showOptions={false}
|
||||||
|
|||||||
+298
-35
@@ -1,20 +1,45 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import WelcomeForm from "../components/WelcomeForm.jsx";
|
import ReactGA from 'react-ga';
|
||||||
import DetectionFramework from "../components/DetectionFramework.jsx";
|
import WelcomeForm2 from "../components/WelcomeForm2.jsx";
|
||||||
|
import Stepper from "@material-ui/core/Stepper";
|
||||||
|
import Step from "@material-ui/core/Step";
|
||||||
|
import StepLabel from "@material-ui/core/StepLabel";
|
||||||
|
import AppFramework from "../components/AppFramework.jsx";
|
||||||
import {
|
import {
|
||||||
Grid,
|
Grid,
|
||||||
|
Container,
|
||||||
Fade,
|
Fade,
|
||||||
Typography,
|
Typography,
|
||||||
|
Paper,
|
||||||
|
Button,
|
||||||
|
Card,
|
||||||
|
CardContent,
|
||||||
|
CardActionArea,
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import theme from '../theme';
|
import theme from '../theme';
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate, Link } from "react-router-dom";
|
||||||
|
|
||||||
// Should be different if logged in :|
|
|
||||||
const Welcome = (props) => {
|
const Welcome = (props) => {
|
||||||
const { globalUrl, surfaceColor, newColor, mini, inputColor, userdata, isLoggedIn, isLoaded } = props;
|
const { globalUrl, surfaceColor, newColor, mini, inputColor, userdata, isLoggedIn, isLoaded } = props;
|
||||||
|
const [skipped, setSkipped] = React.useState(new Set());
|
||||||
const [inputUsecase, setInputUsecase] = useState({});
|
const [inputUsecase, setInputUsecase] = useState({});
|
||||||
const [frameworkData, setFrameworkData] = useState(undefined);
|
const [frameworkData, setFrameworkData] = useState(undefined);
|
||||||
const [discoveryWrapper, setDiscoveryWrapper] = useState(undefined);
|
const [discoveryWrapper, setDiscoveryWrapper] = useState(undefined);
|
||||||
|
const [activeStep, setActiveStep] = React.useState(0);
|
||||||
|
const [apps, setApps] = React.useState([]);
|
||||||
|
const [defaultSearch, setDefaultSearch] = React.useState("")
|
||||||
|
const [selectionOpen, setSelectionOpen] = React.useState(false)
|
||||||
|
const [showWelcome, setShowWelcome] = React.useState(false)
|
||||||
|
|
||||||
|
const isCloud =
|
||||||
|
window.location.host === "localhost:3002" ||
|
||||||
|
window.location.host === "shuffler.io";
|
||||||
|
|
||||||
|
const [steps, setSteps] = useState([
|
||||||
|
"Help us get to know you",
|
||||||
|
"Find your Apps",
|
||||||
|
"Discover Usecases",
|
||||||
|
])
|
||||||
|
|
||||||
let navigate = useNavigate();
|
let navigate = useNavigate();
|
||||||
|
|
||||||
@@ -51,46 +76,284 @@ const Welcome = (props) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getApps = () => {
|
||||||
|
fetch(globalUrl + "/api/v1/apps", {
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
Accept: "application/json",
|
||||||
|
},
|
||||||
|
credentials: "include",
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
if (response.status !== 200) {
|
||||||
|
console.log("Status not 200 for apps :O!");
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then((responseJson) => {
|
||||||
|
setApps(responseJson);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.log("App loading error: "+error.toString());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const usecaseButtons = [{
|
||||||
|
"name": "Phishing",
|
||||||
|
"usecase": "Email management",
|
||||||
|
"color": "#C51152",
|
||||||
|
}, {
|
||||||
|
"name": "Enrichment",
|
||||||
|
"usecase": "2. Enrich",
|
||||||
|
"color": "#F4C20D",
|
||||||
|
}, {
|
||||||
|
"name": "Detection",
|
||||||
|
"usecase": "3. Detect",
|
||||||
|
"color": "#3CBA54",
|
||||||
|
}, {
|
||||||
|
"name": "Response",
|
||||||
|
"usecase": "4. Respond",
|
||||||
|
"color": "#4885ED",
|
||||||
|
}]
|
||||||
|
|
||||||
|
const handleSetSearch = (input, orgupdate) => {
|
||||||
|
console.log("INPUT & ORGUPDATE: ", input, orgupdate, defaultSearch)
|
||||||
|
if (input !== defaultSearch) {
|
||||||
|
setDefaultSearch(input)
|
||||||
|
setSelectionOpen(false)
|
||||||
|
setTimeout(function(){
|
||||||
|
setSelectionOpen(true)
|
||||||
|
}, 150);
|
||||||
|
|
||||||
|
//if (userdata !== undefined && userdata.active_org !== undefined && userdata.active_org.id !== undefined) {
|
||||||
|
// sendOrgUpdate("", "", userdata.active_org.id, orgupdate)
|
||||||
|
//}
|
||||||
|
} else {
|
||||||
|
setDefaultSearch("")
|
||||||
|
setSelectionOpen(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getFramework()
|
getFramework()
|
||||||
}, []);
|
getApps()
|
||||||
|
|
||||||
|
if (
|
||||||
|
window.location.search !== undefined &&
|
||||||
|
window.location.search !== null
|
||||||
|
) {
|
||||||
|
const urlSearchParams = new URLSearchParams(window.location.search);
|
||||||
|
const params = Object.fromEntries(urlSearchParams.entries());
|
||||||
|
const foundTab = params["tab"];
|
||||||
|
if (foundTab !== null && foundTab !== undefined && !isNaN(foundTab)) {
|
||||||
|
console.log("FOUND TAB: ", foundTab)
|
||||||
|
setShowWelcome(true)
|
||||||
|
if (foundTab === 3 || foundTab === "3") {
|
||||||
|
console.log("SET SEARCH!!")
|
||||||
|
|
||||||
|
handleSetSearch(usecaseButtons[0].name, usecaseButtons[0].usecase)
|
||||||
|
}
|
||||||
|
|
||||||
|
setActiveStep(foundTab-1)
|
||||||
|
} else {
|
||||||
|
navigate(`/welcome?tab=1`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const isStepSkipped = step => {
|
||||||
|
return skipped.has(step)
|
||||||
|
}
|
||||||
|
|
||||||
|
const paperObject = {
|
||||||
|
flex: 1,
|
||||||
|
padding: 0,
|
||||||
|
textAlign: "center",
|
||||||
|
maxWidth: 300,
|
||||||
|
minWidth: 300,
|
||||||
|
backgroundColor: theme.palette.surfaceColor,
|
||||||
|
color: "white",
|
||||||
|
}
|
||||||
|
|
||||||
|
const actionObject = {
|
||||||
|
padding: "50px 35px 50px 35px",
|
||||||
|
}
|
||||||
|
|
||||||
|
const imageStyle = {
|
||||||
|
width: 150,
|
||||||
|
height: 150,
|
||||||
|
margin: "auto",
|
||||||
|
marginTop: 30,
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Grid container spacing={2} style={{ padding: 70, maxWidth: 1366, minWidth: 1366, margin: "auto", }}>
|
<div style={{width: 1000, margin: "auto", backgroundColor: theme.palette.platformColor, paddingBottom: 150, minHeight: 1500,}}>
|
||||||
<Grid item xs={6}>
|
{/*
|
||||||
<div>
|
<div style={{position: "fixed", bottom: 110, right: 110, display: "flex", }}>
|
||||||
<WelcomeForm
|
<img src="/images/Arrow.png" style={{width: 250, height: "100%",}} />
|
||||||
userdata={userdata}
|
</div>
|
||||||
globalUrl={globalUrl}
|
*/}
|
||||||
discoveryWrapper={discoveryWrapper}
|
{showWelcome === true ?
|
||||||
setDiscoveryWrapper={setDiscoveryWrapper}
|
<div>
|
||||||
/>
|
<div style={{minWidth: 500, maxWidth: 500, margin: "auto",}}>
|
||||||
|
<Stepper
|
||||||
|
activeStep={activeStep}
|
||||||
|
color="primary"
|
||||||
|
style={{
|
||||||
|
backgroundColor: theme.palette.platformColor,
|
||||||
|
borderRadius: theme.palette.borderRadius,
|
||||||
|
padding: 12,
|
||||||
|
border: "1px solid rgba(255,255,255,0.3)",
|
||||||
|
maxWidth: 500,
|
||||||
|
color: "white",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{steps.map((label, index) => {
|
||||||
|
const stepProps = {}
|
||||||
|
const labelProps = {}
|
||||||
|
//if (isStepOptional(index)) {
|
||||||
|
// labelProps.optional = "optional"
|
||||||
|
//}
|
||||||
|
|
||||||
|
if (isStepSkipped(index)) {
|
||||||
|
stepProps.completed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Step key={label} {...stepProps} style={{maxWidth: 160, color: "white", }}>
|
||||||
|
<StepLabel {...labelProps} style={{marginLeft: 10, color: "white",}}>
|
||||||
|
{label}
|
||||||
|
</StepLabel>
|
||||||
|
</Step>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</Stepper>
|
||||||
</div>
|
</div>
|
||||||
</Grid>
|
<Grid container spacing={2} style={{ padding: 0, maxWidth: 1000, minWidth: 1000, margin: "auto", }}>
|
||||||
{frameworkData === undefined || window.location.href.includes("tab=3") ? null :
|
<Grid item xs={window.location.href.includes("tab=2") ? 6 : 12}>
|
||||||
<div style={{marginTop: 25}}>
|
<div>
|
||||||
<Typography variant="h6" style={{textAlign: "center", marginBottom: 25, }}>
|
{/*
|
||||||
App Framework
|
<WelcomeForm
|
||||||
</Typography>
|
userdata={userdata}
|
||||||
<Fade>
|
|
||||||
<DetectionFramework
|
|
||||||
inputUsecase={inputUsecase}
|
|
||||||
frameworkData={frameworkData}
|
|
||||||
selectedOption={"Draw"}
|
|
||||||
showOptions={false}
|
|
||||||
isLoaded={true}
|
|
||||||
isLoggedIn={true}
|
|
||||||
globalUrl={globalUrl}
|
globalUrl={globalUrl}
|
||||||
size={0.8}
|
|
||||||
color={theme.palette.platformColor}
|
|
||||||
discoveryWrapper={discoveryWrapper}
|
discoveryWrapper={discoveryWrapper}
|
||||||
setDiscoveryWrapper={setDiscoveryWrapper}
|
setDiscoveryWrapper={setDiscoveryWrapper}
|
||||||
/>
|
/>
|
||||||
</Fade>
|
*/}
|
||||||
|
<WelcomeForm2
|
||||||
|
userdata={userdata}
|
||||||
|
globalUrl={globalUrl}
|
||||||
|
discoveryWrapper={discoveryWrapper}
|
||||||
|
setDiscoveryWrapper={setDiscoveryWrapper}
|
||||||
|
appFramework={frameworkData}
|
||||||
|
getFramework={getFramework}
|
||||||
|
steps={steps}
|
||||||
|
skipped={skipped}
|
||||||
|
setSkipped={setSkipped}
|
||||||
|
activeStep={activeStep}
|
||||||
|
setActiveStep={setActiveStep}
|
||||||
|
getApps={getApps}
|
||||||
|
apps={apps}
|
||||||
|
handleSetSearch={handleSetSearch}
|
||||||
|
usecaseButtons={usecaseButtons}
|
||||||
|
defaultSearch={defaultSearch}
|
||||||
|
setDefaultSearch={setDefaultSearch}
|
||||||
|
selectionOpen={selectionOpen}
|
||||||
|
setSelectionOpen={setSelectionOpen}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</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.platformColor}
|
||||||
|
discoveryWrapper={discoveryWrapper}
|
||||||
|
setDiscoveryWrapper={setDiscoveryWrapper}
|
||||||
|
apps={apps}
|
||||||
|
/>
|
||||||
|
</Fade>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</Grid>
|
||||||
|
</div>
|
||||||
|
:
|
||||||
|
<Fade in={true}>
|
||||||
|
<div style={{maxWidth: 700, margin: "auto", marginTop: 50, }}>
|
||||||
|
<Typography variant="h4" style={{color: "white", textAlign: "center"}}>
|
||||||
|
Welcome to Shuffle
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body1" style={{textAlign: "center", marginBottom: 50, }}>
|
||||||
|
Who do you identify with the most?
|
||||||
|
</Typography>
|
||||||
|
<div style={{display: "flex", marginTop: 70, width: 700, margin: "auto",}}>
|
||||||
|
<Card style={paperObject} onClick={() => {
|
||||||
|
if (isCloud) {
|
||||||
|
ReactGA.event({
|
||||||
|
category: "welcome",
|
||||||
|
action: "click_welcome_continue",
|
||||||
|
label: "",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
setShowWelcome(true)
|
||||||
|
}}>
|
||||||
|
<CardActionArea style={actionObject}>
|
||||||
|
<Typography variant="h4" style={{color: "#49A928"}}>
|
||||||
|
New to Shuffle
|
||||||
|
</Typography>
|
||||||
|
<img src="/images/welcome_cog.png" style={imageStyle} />
|
||||||
|
<Typography variant="body1" style={{marginTop: 30, color: "rgba(255,255,255,0.8)"}}>
|
||||||
|
Follow our short introduction and learn some tips and tricks
|
||||||
|
</Typography>
|
||||||
|
</CardActionArea>
|
||||||
|
</Card>
|
||||||
|
<div style={{marginLeft: 25, marginRight: 25, }}>
|
||||||
|
<Typography style={{marginTop: 200, }}>
|
||||||
|
OR
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
<Card style={paperObject} onClick={() => {
|
||||||
|
if (isCloud) {
|
||||||
|
ReactGA.event({
|
||||||
|
category: "welcome",
|
||||||
|
action: "click_getting_started",
|
||||||
|
label: "",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
navigate("/getting-started?message=Skipped intro")
|
||||||
|
}}>
|
||||||
|
<CardActionArea style={actionObject}>
|
||||||
|
<Typography variant="h4" style={{color: "#f86a3e"}}>
|
||||||
|
Experienced
|
||||||
|
</Typography>
|
||||||
|
<img src="/images/social/shuffle_logo_round.png" style={imageStyle} />
|
||||||
|
<Typography variant="body1" style={{marginTop: 30, color: "rgba(255,255,255,0.8)"}}>
|
||||||
|
You know Shuffle well. Head to the product right away!
|
||||||
|
</Typography>
|
||||||
|
</CardActionArea>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
</Fade>
|
||||||
</Grid>
|
}
|
||||||
)
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Welcome;
|
export default Welcome;
|
||||||
|
|||||||
Reference in New Issue
Block a user