Merge branch '1.3.0' of github.com:Shuffle/Shuffle into 1.3.0
This commit is contained in:
@@ -19,7 +19,7 @@ require (
|
||||
github.com/gorilla/mux v1.8.0
|
||||
github.com/h2non/filetype v1.1.3
|
||||
github.com/satori/go.uuid v1.2.0
|
||||
github.com/shuffle/shuffle-shared v0.4.19
|
||||
github.com/shuffle/shuffle-shared v0.4.35
|
||||
golang.org/x/crypto v0.9.0
|
||||
google.golang.org/api v0.125.0
|
||||
google.golang.org/appengine v1.6.7
|
||||
|
||||
+14
-3
@@ -5841,6 +5841,8 @@ func handleAppZipUpload(resp http.ResponseWriter, request *http.Request) {
|
||||
resp.Write([]byte("OK"))
|
||||
}
|
||||
|
||||
|
||||
|
||||
func initHandlers() {
|
||||
var err error
|
||||
ctx := context.Background()
|
||||
@@ -5890,6 +5892,7 @@ func initHandlers() {
|
||||
|
||||
r := mux.NewRouter()
|
||||
r.HandleFunc("/api/v1/_ah/health", shuffle.HealthCheckHandler)
|
||||
r.HandleFunc("/api/v1/health", shuffle.RunOpsHealthCheck).Methods("GET", "OPTIONS")
|
||||
|
||||
// Make user related locations
|
||||
// Fix user changes with org
|
||||
@@ -5998,7 +6001,7 @@ func initHandlers() {
|
||||
// New for recommendations in Shuffle
|
||||
r.HandleFunc("/api/v1/recommendations/get_actions", shuffle.HandleActionRecommendation).Methods("POST", "OPTIONS")
|
||||
r.HandleFunc("/api/v1/recommendations/modify", shuffle.HandleRecommendationAction).Methods("POST", "OPTIONS")
|
||||
r.HandleFunc("/api/v1/workflows/{key}/revisions", shuffle.GetWorkflowRevisions).Methods("GET", "OPTIONS")
|
||||
// r.HandleFunc("/api/v1/workflows/{key}/revisions", shuffle.GetWorkflowRevisions).Methods("GET", "OPTIONS")
|
||||
|
||||
// Triggers
|
||||
r.HandleFunc("/api/v1/hooks/new", shuffle.HandleNewHook).Methods("POST", "OPTIONS")
|
||||
@@ -6054,9 +6057,16 @@ func initHandlers() {
|
||||
r.HandleFunc("/api/v1/orgs/{orgId}/list_cache", shuffle.HandleListCacheKeys).Methods("GET", "OPTIONS")
|
||||
r.HandleFunc("/api/v1/orgs/{orgId}/get_cache", shuffle.HandleGetCacheKey).Methods("POST", "OPTIONS")
|
||||
r.HandleFunc("/api/v1/orgs/{orgId}/set_cache", shuffle.HandleSetCacheKey).Methods("POST", "OPTIONS")
|
||||
r.HandleFunc("/api/v1/orgs/{orgId}/cache/{cache_key}", shuffle.HandleDeleteCacheKey).Methods("DELETE", "OPTIONS")
|
||||
|
||||
r.HandleFunc("/api/v1/orgs/{orgId}/stats", shuffle.HandleGetStatistics).Methods("GET", "OPTIONS")
|
||||
r.HandleFunc("/api/v1/orgs/{orgId}/revisions", shuffle.GetWorkflowRevisions).Methods("GET", "OPTIONS")
|
||||
|
||||
r.HandleFunc("/api/v1/orgs/{orgId}/cache", shuffle.HandleListCacheKeys).Methods("GET", "OPTIONS")
|
||||
r.HandleFunc("/api/v1/orgs/{orgId}/cache", shuffle.HandleSetCacheKey).Methods("POST", "OPTIONS")
|
||||
r.HandleFunc("/api/v1/orgs/{orgId}/cache/{cache_key}", shuffle.HandleDeleteCacheKey).Methods("DELETE", "OPTIONS")
|
||||
r.HandleFunc("/api/v1/orgs/{orgId}/datastore", shuffle.HandleListCacheKeys).Methods("GET", "OPTIONS")
|
||||
r.HandleFunc("/api/v1/orgs/{orgId}/datastore", shuffle.HandleSetCacheKey).Methods("POST", "OPTIONS")
|
||||
r.HandleFunc("/api/v1/orgs/{orgId}/datastore/{cache_key}", shuffle.HandleDeleteCacheKey).Methods("DELETE", "OPTIONS")
|
||||
|
||||
|
||||
// Docker orborus specific - downloads an image
|
||||
r.HandleFunc("/api/v1/get_docker_image", getDockerImage).Methods("POST", "OPTIONS")
|
||||
@@ -6098,6 +6108,7 @@ func initHandlers() {
|
||||
// Had to move away from mux, which means Method is fucked up right now.
|
||||
func main() {
|
||||
initHandlers()
|
||||
go shuffle.InitOpsWorkflow()
|
||||
hostname, err := os.Hostname()
|
||||
if err != nil {
|
||||
hostname = "MISSING"
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
version: '3'
|
||||
services:
|
||||
frontend:
|
||||
image: ghcr.io/shuffle/shuffle-frontend:nightly
|
||||
image: ghcr.io/shuffle/shuffle-frontend:latest
|
||||
container_name: shuffle-frontend
|
||||
hostname: shuffle-frontend
|
||||
ports:
|
||||
|
||||
@@ -13,8 +13,9 @@
|
||||
"@mui/material": "^5.14.0",
|
||||
"@mui/styles": "^5.14.0",
|
||||
"@mui/x-data-grid": "^5.17.11",
|
||||
"@uiw/codemirror-themes": "^4.21.7",
|
||||
"@uiw/react-codemirror": "^4.21.7",
|
||||
"@mui/x-date-pickers": "^6.11.1",
|
||||
"@uiw/codemirror-themes": "^4.21.9",
|
||||
"@uiw/react-codemirror": "^4.21.9",
|
||||
"@use-it/interval": "^1.0.0",
|
||||
"algoliasearch": "^4.13.1",
|
||||
"calculate-size": "^1.1.1",
|
||||
@@ -24,6 +25,7 @@
|
||||
"cytoscape-edgehandles": "^3.6.0",
|
||||
"cytoscape-node-html-label": "^1.1.5",
|
||||
"d3": "^7.1.1",
|
||||
"dayjs": "^1.11.9",
|
||||
"dotenv": "^6.1.0",
|
||||
"downshift": "^3.3.5",
|
||||
"github-markdown-css": "^3.0.1",
|
||||
@@ -36,11 +38,11 @@
|
||||
"jss-nested": "^6.0.1",
|
||||
"jss-props-sort": "^6.0.0",
|
||||
"jss-vendor-prefixer": "^8.0.1",
|
||||
"material-ui-chip-input": "^2.0.0-beta.2",
|
||||
"md5-file": "^4.0.0",
|
||||
"mdbreact": "^4.21.1",
|
||||
"mime": "^3.0.0",
|
||||
"moment": "^2.29.1",
|
||||
"mui-chips-input": "^2.1.3",
|
||||
"mui-nested-menu": "^3.2.1",
|
||||
"process": "^0.11.10",
|
||||
"react": "^18.2.0",
|
||||
@@ -67,6 +69,7 @@
|
||||
"react-router": "^6.14.1",
|
||||
"react-router-dom": "^6.14.1",
|
||||
"react-scripts": "^5.0.1",
|
||||
"react-toastify": "^9.1.3",
|
||||
"reaviz": "^14.9.4",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"search-insights": "^2.2.1",
|
||||
@@ -108,6 +111,6 @@
|
||||
"react-16": "npm:react@16.13.1",
|
||||
"react-dom-16": "npm:react-dom@16.13.1",
|
||||
"react-error-overlay": "6.0.9",
|
||||
"webpack": "^4.46.0"
|
||||
"webpack": "^5.88.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,7 +167,7 @@ const App = (message, props) => {
|
||||
const includedData =
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: "#1F2023",
|
||||
backgroundColor: theme.palette.backgroundColor,
|
||||
color: "rgba(255, 255, 255, 0.65)",
|
||||
minHeight: "100vh",
|
||||
}}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
|
||||
import theme from '../theme.jsx';
|
||||
import CytoscapeComponent from 'react-cytoscapejs';
|
||||
import frameworkStyle from '../frameworkStyle.jsx';
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import theme from '../theme.jsx';
|
||||
import { useAlert } from "react-alert";
|
||||
|
||||
import AppSearch from '../components/Appsearch.jsx';
|
||||
import PaperComponent from "../components/PaperComponent.jsx"
|
||||
@@ -18,14 +17,15 @@ import {
|
||||
Divider,
|
||||
IconButton,
|
||||
Badge,
|
||||
CircularProgress,
|
||||
CircularProgress,
|
||||
Tooltip,
|
||||
Dialog,
|
||||
Chip,
|
||||
Avatar,
|
||||
Button
|
||||
Button,
|
||||
} from "@mui/material";
|
||||
|
||||
|
||||
import {
|
||||
Close as CloseIcon,
|
||||
Delete as DeleteIcon,
|
||||
@@ -33,10 +33,10 @@ import {
|
||||
|
||||
import * as edgehandles from "cytoscape-edgehandles";
|
||||
import * as cytoscape from "cytoscape";
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
cytoscape.use(edgehandles);
|
||||
|
||||
|
||||
const svgSize = "40px"
|
||||
const parsedDatatypeImages = {
|
||||
"SIEM": encodeURI(`data:image/svg+xml;utf-8,<svg fill="rgb(248,90,62)" width="${svgSize}" height="${svgSize}" viewBox="0 0 ${svgSize} ${svgSize}" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M6.93767 0C8.71083 0 10.4114 0.704386 11.6652 1.9582C12.919 3.21202 13.6234 4.91255 13.6234 6.68571C13.6234 8.34171 13.0165 9.864 12.0188 11.0366L12.2965 11.3143H13.1091L18.252 16.4571L16.7091 18L11.5662 12.8571V12.0446L11.2885 11.7669C10.116 12.7646 8.59367 13.3714 6.93767 13.3714C5.16451 13.3714 3.46397 12.667 2.21015 11.4132C0.956339 10.1594 0.251953 8.45888 0.251953 6.68571C0.251953 4.91255 0.956339 3.21202 2.21015 1.9582C3.46397 0.704386 5.16451 0 6.93767 0ZM6.93767 2.05714C4.36624 2.05714 2.3091 4.11429 2.3091 6.68571C2.3091 9.25714 4.36624 11.3143 6.93767 11.3143C9.5091 11.3143 11.5662 9.25714 11.5662 6.68571C11.5662 4.11429 9.5091 2.05714 6.93767 2.05714Z" /></svg>`),
|
||||
@@ -520,7 +520,7 @@ const AppFramework = (props) => {
|
||||
|
||||
const scale = size === undefined ? 1 : size > 5 ? 3 : size
|
||||
|
||||
const alert = useAlert()
|
||||
//const alert = useAlert()
|
||||
|
||||
|
||||
const handleLoadNextSuggestion = (frameworkData) => {
|
||||
@@ -783,16 +783,16 @@ const AppFramework = (props) => {
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success === false) {
|
||||
if (responseJson.reason !== undefined) {
|
||||
alert.error("Failed updating: " + responseJson.reason)
|
||||
toast("Failed updating: " + responseJson.reason)
|
||||
} else {
|
||||
alert.error("Failed to update framework for your org.")
|
||||
toast("Failed to update framework for your org.")
|
||||
}
|
||||
} else {
|
||||
alert.info("Updated usecase.")
|
||||
toast("Updated usecase.")
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
//setFrameworkLoaded(true)
|
||||
})
|
||||
}
|
||||
@@ -815,13 +815,13 @@ const AppFramework = (props) => {
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success === false) {
|
||||
alert.error("Failed to activate the app")
|
||||
toast("Failed to activate the app")
|
||||
} else {
|
||||
//alert.success("App activated for your organization! Refresh the page to use the app.")
|
||||
//toast("App activated for your organization! Refresh the page to use the app.")
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
//alert.error(error.toString())
|
||||
//toast(error.toString())
|
||||
console.log("Activate app error: ", error.toString())
|
||||
});
|
||||
}
|
||||
@@ -851,9 +851,9 @@ const AppFramework = (props) => {
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success === false) {
|
||||
if (responseJson.reason !== undefined) {
|
||||
alert.error("Failed updating: " + responseJson.reason)
|
||||
toast("Failed updating: " + responseJson.reason)
|
||||
} else {
|
||||
alert.error("Failed to update framework for your org.")
|
||||
toast("Failed to update framework for your org.")
|
||||
|
||||
}
|
||||
}
|
||||
@@ -862,7 +862,7 @@ const AppFramework = (props) => {
|
||||
//setFrameworkData(responseJson)
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
//setFrameworkLoaded(true)
|
||||
})
|
||||
}
|
||||
@@ -1856,6 +1856,7 @@ const AppFramework = (props) => {
|
||||
//autounselectify={true}
|
||||
var usecasediff = -100
|
||||
const bgColor = color === undefined || color === null || color.length === 0 ? theme.palette.surfaceColor : color
|
||||
console.log("Background: ", bgColor)
|
||||
|
||||
return (
|
||||
<div style={{margin: "auto", backgroundColor: bgColor, position: "relative", }}>
|
||||
@@ -1992,11 +1993,11 @@ const AppFramework = (props) => {
|
||||
|
||||
{
|
||||
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: -50, left: 50, }}>
|
||||
<Paper style={{width: 300, maxHeight: 400, overflow: "hidden", zIndex: 12500, padding: 25, paddingRight: 25, backgroundColor: theme.palette.surfaceColor, border: "1px solid rgba(255,255,255,0.2)", position: "absolute", top: -50, left: 50, }}>
|
||||
{paperTitle.length > 0 ?
|
||||
<span>
|
||||
<Typography variant="h6" style={{textAlign: "center"}}>
|
||||
{paperTitle}
|
||||
{paperTitle.replace("_", " ", -1)}
|
||||
</Typography>
|
||||
<Divider style={{marginTop: 5, marginBottom: 5 }} />
|
||||
</span>
|
||||
@@ -2126,7 +2127,7 @@ const AppFramework = (props) => {
|
||||
?
|
||||
<span>
|
||||
<Typography variant="body2" color="textSecondary" style={{marginTop: 10}}>
|
||||
Click an app below to select it
|
||||
Search to find your app
|
||||
</Typography>
|
||||
</span>
|
||||
:
|
||||
@@ -2161,7 +2162,7 @@ const AppFramework = (props) => {
|
||||
elements={elements}
|
||||
minZoom={0.35}
|
||||
maxZoom={2.00}
|
||||
style={{width: 560*scale, height: 560*scale, backgroundColor: "transparent", margin: "auto",}}
|
||||
style={{width: 560*scale, height: 560*scale, backgroundColor: theme.palette.backgroundColor, margin: "auto",}}
|
||||
stylesheet={frameworkStyle}
|
||||
boxSelectionEnabled={false}
|
||||
panningEnabled={false}
|
||||
|
||||
@@ -74,7 +74,7 @@ const AppGrid = props => {
|
||||
.then(response => {
|
||||
if (response.success === true) {
|
||||
setFormMessage(response.reason)
|
||||
//alert.info("Thanks for submitting!")
|
||||
//toast("Thanks for submitting!")
|
||||
} else {
|
||||
setFormMessage(errorMessage)
|
||||
}
|
||||
|
||||
@@ -1,23 +1,34 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import theme from '../theme.jsx';
|
||||
import ReactGA from 'react-ga4';
|
||||
import theme from '../theme.jsx';
|
||||
import {Link} from 'react-router-dom';
|
||||
import { useAlert } from "react-alert";
|
||||
import { Search as SearchIcon, CloudQueue as CloudQueueIcon, Code as CodeIcon } from '@mui/icons-material';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
//import algoliasearch from 'algoliasearch/lite';
|
||||
import algoliasearch from 'algoliasearch';
|
||||
import { InstantSearch, connectSearchBox, connectHits } from 'react-instantsearch-dom';
|
||||
import { Grid, Paper, TextField, ButtonBase, InputAdornment, Typography, Button, Tooltip} from '@mui/material';
|
||||
import {
|
||||
Grid,
|
||||
Paper,
|
||||
TextField,
|
||||
ButtonBase,
|
||||
InputAdornment,
|
||||
Typography,
|
||||
Button,
|
||||
Tooltip
|
||||
} from '@mui/material';
|
||||
|
||||
import aa from 'search-insights'
|
||||
const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240")
|
||||
const Appsearch = props => {
|
||||
const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs, newSelectedApp, setNewSelectedApp, defaultSearch, showSearch, ConfiguredHits, userdata, cy, isCreatorPage, actionImageList, setActionImageList} = props
|
||||
|
||||
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
|
||||
const alert = useAlert();
|
||||
//const alert = useAlert();
|
||||
const rowHandler = maxRows === undefined || maxRows === null ? 50 : maxRows
|
||||
const xs = parsedXs === undefined || parsedXs === null ? 12 : parsedXs
|
||||
//const theme = useTheme();
|
||||
//const [apps, setApps] = React.useState([]);
|
||||
//const [filteredApps, setFilteredApps] = React.useState([]);
|
||||
const [formMail, setFormMail] = React.useState("");
|
||||
@@ -57,55 +68,18 @@ const Appsearch = props => {
|
||||
if (response.status !== 200) {
|
||||
console.log("Status not 200 for set creator :O!");
|
||||
}
|
||||
alert.success("Sucessfully updated specialzed app.")
|
||||
toast("Sucessfully updated specialzed app.")
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (!responseJson.success && responseJson.reason !== undefined) {
|
||||
alert.error("Failed updating user: " + responseJson.reason);
|
||||
toast("Failed updating user: " + responseJson.reason);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
});
|
||||
};
|
||||
const submitContact = (email, message) => {
|
||||
const data = {
|
||||
"firstname": "",
|
||||
"lastname": "",
|
||||
"title": "",
|
||||
"companyname": "",
|
||||
"email": email,
|
||||
"phone": "",
|
||||
"message": message,
|
||||
}
|
||||
|
||||
const errorMessage = "Something went wrong. Please contact frikky@shuffler.io directly."
|
||||
|
||||
fetch(globalUrl+"/api/v1/contact", {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(response => {
|
||||
if (response.success === true) {
|
||||
setFormMessage(response.reason)
|
||||
//alert.info("Thanks for submitting!")
|
||||
} else {
|
||||
setFormMessage(errorMessage)
|
||||
}
|
||||
|
||||
setFormMail("")
|
||||
setMessage("")
|
||||
})
|
||||
.catch(error => {
|
||||
setFormMessage(errorMessage)
|
||||
console.log(error)
|
||||
});
|
||||
}
|
||||
|
||||
// value={currentRefinement}
|
||||
const SearchBox = ({currentRefinement, refine, isSearchStalled} ) => {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
|
||||
import theme from '../theme.jsx';
|
||||
import { useAlert } from "react-alert";
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
import {
|
||||
Tooltip,
|
||||
IconButton,
|
||||
@@ -34,7 +35,7 @@ const AuthenticationItem = (props) => {
|
||||
const [selectedAuthenticationModalOpen, setSelectedAuthenticationModalOpen] = React.useState(false);
|
||||
const [authenticationFields, setAuthenticationFields] = React.useState([]);
|
||||
|
||||
const alert = useAlert();
|
||||
//const alert = useAlert();
|
||||
var bgColor = "#27292d";
|
||||
if (index % 2 === 0) {
|
||||
bgColor = "#1f2023";
|
||||
@@ -63,7 +64,7 @@ const AuthenticationItem = (props) => {
|
||||
}
|
||||
|
||||
const deleteAuthentication = (data) => {
|
||||
alert.info("Deleting auth " + data.label);
|
||||
toast("Deleting auth " + data.label);
|
||||
|
||||
// Just use this one?
|
||||
const url = globalUrl + "/api/v1/apps/authentication/" + data.id;
|
||||
@@ -79,13 +80,13 @@ const AuthenticationItem = (props) => {
|
||||
response.json().then((responseJson) => {
|
||||
console.log("RESP: ", responseJson);
|
||||
if (responseJson["success"] === false) {
|
||||
alert.error("Failed deleting auth");
|
||||
toast("Failed deleting auth");
|
||||
} else {
|
||||
// Need to wait because query in ES is too fast
|
||||
setTimeout(() => {
|
||||
getAppAuthentication();
|
||||
}, 1000);
|
||||
//alert.success("Successfully deleted authentication!")
|
||||
//toast("Successfully deleted authentication!")
|
||||
}
|
||||
})
|
||||
)
|
||||
@@ -115,9 +116,9 @@ const AuthenticationItem = (props) => {
|
||||
.then((response) =>
|
||||
response.json().then((responseJson) => {
|
||||
if (responseJson["success"] === false) {
|
||||
alert.error("Failed overwriting appauth in workflows");
|
||||
toast("Failed overwriting appauth in workflows");
|
||||
} else {
|
||||
alert.success("Successfully updated auth everywhere!");
|
||||
toast("Successfully updated auth everywhere!");
|
||||
//setSelectedUserModalOpen(false);
|
||||
setTimeout(() => {
|
||||
getAppAuthentication();
|
||||
@@ -126,7 +127,7 @@ const AuthenticationItem = (props) => {
|
||||
})
|
||||
)
|
||||
.catch((error) => {
|
||||
alert.error("Err: " + error.toString());
|
||||
toast("Err: " + error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import theme from '../theme.jsx';
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
import {
|
||||
Button,
|
||||
@@ -54,7 +54,7 @@ const AuthenticationData = (props) => {
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (!responseJson.success) {
|
||||
alert.error("Failed to set app auth: " + responseJson.reason);
|
||||
toast("Failed to set app auth: " + responseJson.reason);
|
||||
} else {
|
||||
if (getAppAuthentication !== undefined) {
|
||||
getAppAuthentication()
|
||||
@@ -65,11 +65,11 @@ const AuthenticationData = (props) => {
|
||||
}
|
||||
|
||||
// Needs a refresh with the new authentication..
|
||||
//alert.success("Successfully saved new app auth")
|
||||
//toast("Successfully saved new app auth")
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
//alert.error(error.toString());
|
||||
//toast(error.toString());
|
||||
console.log("New auth error: ", error.toString());
|
||||
});
|
||||
}
|
||||
@@ -146,7 +146,7 @@ const AuthenticationData = (props) => {
|
||||
selectedApp.authentication.parameters[key].name
|
||||
] = "false";
|
||||
} else {
|
||||
alert.info(
|
||||
toast(
|
||||
"Field " +
|
||||
selectedApp.authentication.parameters[key].name +
|
||||
" can't be empty"
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { useState, useEffect } from "react";
|
||||
|
||||
import theme from '../theme.jsx';
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { useAlert } from "react-alert";
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
import {
|
||||
Divider,
|
||||
@@ -44,7 +44,7 @@ const AuthenticationData = (props) => {
|
||||
authFieldsOnly,
|
||||
} = props
|
||||
|
||||
const alert = useAlert()
|
||||
//const alert = useAlert()
|
||||
let navigate = useNavigate();
|
||||
const [submitSuccessful, setSubmitSuccessful] = useState(false)
|
||||
const [authenticationOption, setAuthenticationOptions] = React.useState({
|
||||
@@ -99,9 +99,9 @@ const AuthenticationData = (props) => {
|
||||
.then((responseJson) => {
|
||||
if (!responseJson.success) {
|
||||
if (responseJson.reason === undefined) {
|
||||
alert.error("Failed to set app auth. Are you logged in?")
|
||||
toast("Failed to set app auth. Are you logged in?")
|
||||
} else {
|
||||
alert.error("Failed to set app auth: " + responseJson.reason);
|
||||
toast("Failed to set app auth: " + responseJson.reason);
|
||||
}
|
||||
} else {
|
||||
setSubmitSuccessful(true)
|
||||
@@ -115,7 +115,7 @@ const AuthenticationData = (props) => {
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
//alert.error(error.toString());
|
||||
//toast(error.toString());
|
||||
console.log("New auth error: ", error.toString());
|
||||
});
|
||||
};
|
||||
@@ -175,7 +175,7 @@ const AuthenticationData = (props) => {
|
||||
selectedApp.authentication.parameters[paramkey].name
|
||||
] = "false";
|
||||
} else {
|
||||
alert.info(
|
||||
toast(
|
||||
"Field " +
|
||||
selectedApp.authentication.parameters[paramkey].name +
|
||||
" can't be empty"
|
||||
|
||||
@@ -11,11 +11,11 @@ import {
|
||||
Card,
|
||||
} from "@mui/material";
|
||||
|
||||
import { useAlert } from "react-alert";
|
||||
//import { useAlert
|
||||
|
||||
const Branding = (props) => {
|
||||
const { globalUrl, userdata, serverside, billingInfo, stripeKey, selectedOrganization, handleGetOrg, } = props;
|
||||
const alert = useAlert();
|
||||
//const alert = useAlert();
|
||||
const [publishingInfo, setPublishingInfo] = useState("");
|
||||
|
||||
// Should enable / disable org branding
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import theme from "../theme.jsx";
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
import {
|
||||
Tooltip,
|
||||
Divider,
|
||||
@@ -16,7 +18,7 @@ import {
|
||||
DialogTitle,
|
||||
DialogActions,
|
||||
} from "@mui/material";
|
||||
import { useAlert } from "react-alert";
|
||||
//import { useAlert
|
||||
|
||||
import {
|
||||
Edit as EditIcon,
|
||||
@@ -72,7 +74,7 @@ const CacheView = (props) => {
|
||||
const [dataValue, setDataValue] = React.useState({});
|
||||
const [editCache, setEditCache] = React.useState(false);
|
||||
const [show, setShow] = useState({});
|
||||
const alert = useAlert();
|
||||
//const alert = useAlert();
|
||||
useEffect(() => {
|
||||
listOrgCache(orgId);
|
||||
console.log("orgid", orgId);
|
||||
@@ -105,7 +107,7 @@ const CacheView = (props) => {
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
@@ -148,7 +150,7 @@ const CacheView = (props) => {
|
||||
|
||||
|
||||
const deleteCache = (orgId, key) => {
|
||||
alert.info("Attempting to delete Cache");
|
||||
toast("Attempting to delete Cache");
|
||||
fetch(globalUrl + `/api/v1/orgs/${orgId}/cache/${key}`, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
@@ -158,16 +160,16 @@ const CacheView = (props) => {
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status === 200) {
|
||||
alert.success("Successfully deleted Cache");
|
||||
toast("Successfully deleted Cache");
|
||||
setTimeout(() => {
|
||||
listOrgCache(orgId);
|
||||
}, 1000);
|
||||
} else {
|
||||
alert.error("Failed deleting Cache. Does it still exist?");
|
||||
toast("Failed deleting Cache. Does it still exist?");
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
@@ -197,12 +199,12 @@ const CacheView = (props) => {
|
||||
})
|
||||
.then((responseJson) => {
|
||||
setAddCache(responseJson);
|
||||
alert.success("Cache Edited Successfully!");
|
||||
toast("Cache Edited Successfully!");
|
||||
listOrgCache(orgId);
|
||||
setModalOpen(false);
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
@@ -231,12 +233,12 @@ const CacheView = (props) => {
|
||||
})
|
||||
.then((responseJson) => {
|
||||
setAddCache(responseJson);
|
||||
alert.success("New Cache Added Successfully!");
|
||||
toast("New Cache Added Successfully!");
|
||||
listOrgCache(orgId);
|
||||
setModalOpen(false);
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { useInterval } from "react-powerhooks";
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
import {
|
||||
InputAdornment,
|
||||
@@ -112,9 +113,9 @@ const ConfigureWorkflow = (props) => {
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status === 200) {
|
||||
//alert.success("Successfully GOT app "+appId)
|
||||
//toast("Successfully GOT app "+appId)
|
||||
} else {
|
||||
alert.error("Failed getting app");
|
||||
toast("Failed getting app");
|
||||
}
|
||||
|
||||
return response.json();
|
||||
@@ -128,7 +129,7 @@ const ConfigureWorkflow = (props) => {
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
@@ -566,7 +567,7 @@ const ConfigureWorkflow = (props) => {
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
//window.location.pathname = "/search"
|
||||
//alert.error("Failed to find this app. Is it public?")
|
||||
//toast("Failed to find this app. Is it public?")
|
||||
}
|
||||
|
||||
return response.json();
|
||||
@@ -574,16 +575,16 @@ const ConfigureWorkflow = (props) => {
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success === false) {
|
||||
if (responseJson.reason !== undefined) {
|
||||
alert.error("Failed to activate the app: "+responseJson.reason);
|
||||
toast("Failed to activate the app: "+responseJson.reason);
|
||||
} else {
|
||||
alert.error("Failed to activate the app");
|
||||
toast("Failed to activate the app");
|
||||
}
|
||||
} else {
|
||||
alert.success("App activated for your organization!");
|
||||
toast("App activated for your organization!");
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
@@ -693,7 +694,7 @@ const ConfigureWorkflow = (props) => {
|
||||
|
||||
if (workflow.actions !== null) {
|
||||
//console.log(workflow.actions)
|
||||
alert.info("Setting action to version "+action.update_version)
|
||||
toast("Setting action to version "+action.update_version)
|
||||
for (let [key,keyval] in Object.entries(workflow.actions)) {
|
||||
if (workflow.actions[key].app_name === action.app_name && workflow.actions[key].app_version === action.app_version) {
|
||||
workflow.actions[key].app_version = action.update_version
|
||||
@@ -851,7 +852,7 @@ const ConfigureWorkflow = (props) => {
|
||||
console.log("NAVIGATOR: ", navigator);
|
||||
const clipboard = navigator.clipboard;
|
||||
if (clipboard === undefined) {
|
||||
alert.error("Can only copy over HTTPS (port 3443)");
|
||||
toast("Can only copy over HTTPS (port 3443)");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -864,7 +865,7 @@ const ConfigureWorkflow = (props) => {
|
||||
|
||||
/* Copy the text inside the text field */
|
||||
document.execCommand("copy");
|
||||
alert.success("Copied Webhook URL");
|
||||
toast("Copied Webhook URL");
|
||||
}
|
||||
}}>
|
||||
<Typography variant="body2" color="textSecondary">{webhook.description}</Typography>
|
||||
|
||||
@@ -81,7 +81,7 @@ const CreatorGrid = props => {
|
||||
.then(response => {
|
||||
if (response.success === true) {
|
||||
setFormMessage(response.reason)
|
||||
//alert.info("Thanks for submitting!")
|
||||
//toast("Thanks for submitting!")
|
||||
} else {
|
||||
setFormMessage(errorMessage)
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ const DocsGrid = props => {
|
||||
.then(response => {
|
||||
if (response.success === true) {
|
||||
setFormMessage(response.reason)
|
||||
//alert.info("Thanks for submitting!")
|
||||
//toast("Thanks for submitting!")
|
||||
} else {
|
||||
setFormMessage(errorMessage)
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import React, { useEffect, useContext } from "react";
|
||||
import theme from '../theme.jsx';
|
||||
import { isMobile } from "react-device-detect"
|
||||
import ChipInput from "material-ui-chip-input";
|
||||
import { MuiChipsInput } from "mui-chips-input";
|
||||
import UsecaseSearch from "../components/UsecaseSearch.jsx"
|
||||
import WorkflowGrid from "../components/WorkflowGrid.jsx"
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
import {
|
||||
Badge,
|
||||
@@ -25,6 +27,7 @@ import {
|
||||
Typography,
|
||||
Zoom,
|
||||
CircularProgress,
|
||||
Drawer,
|
||||
Dialog,
|
||||
DialogTitle,
|
||||
DialogActions,
|
||||
@@ -39,6 +42,13 @@ import {
|
||||
|
||||
} from "@mui/material";
|
||||
|
||||
import {
|
||||
DatePicker,
|
||||
LocalizationProvider,
|
||||
} from '@mui/x-date-pickers'
|
||||
|
||||
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'
|
||||
|
||||
import {
|
||||
ExpandLess as ExpandLessIcon,
|
||||
ExpandMore as ExpandMoreIcon,
|
||||
@@ -49,16 +59,19 @@ import {
|
||||
const EditWorkflow = (props) => {
|
||||
const { globalUrl, workflow, setWorkflow, modalOpen, setModalOpen, showUpload, usecases, setNewWorkflow, appFramework, isEditing, userdata, } = props
|
||||
|
||||
const [_, setUpdate] = React.useState(""); // Used for rendering, don't remove
|
||||
|
||||
const [submitLoading, setSubmitLoading] = React.useState(false);
|
||||
const [showMoreClicked, setShowMoreClicked] = React.useState(false);
|
||||
const [innerWorkflow, setInnerWorkflow] = React.useState(workflow)
|
||||
const [_, setUpdate] = React.useState(""); // Used for rendering, don't remove
|
||||
const [innerWorkflow, setInnerWorkflow] = React.useState(workflow)
|
||||
|
||||
const [newWorkflowTags, setNewWorkflowTags] = React.useState(workflow.tags !== undefined && workflow.tags !== null ? JSON.parse(JSON.stringify(workflow.tags)) : [])
|
||||
const [description, setDescription] = React.useState(workflow.description !== undefined ? workflow.description : "")
|
||||
|
||||
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 : "")
|
||||
|
||||
const [dueDate, setDueDate] = React.useState(workflow.due_date !== undefined && workflow.due_date !== null && workflow.due_date !== 0 ? dayjs(workflow.due_date*1000) : dayjs().subtract(1, 'day'))
|
||||
|
||||
// Gets the generated workflow
|
||||
const getGeneratedWorkflow = (workflow_id) => {
|
||||
@@ -114,7 +127,7 @@ const EditWorkflow = (props) => {
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
//alert.error(error.toString());
|
||||
//toast(error.toString());
|
||||
console.log("Get workflow error: ", error.toString());
|
||||
})
|
||||
}
|
||||
@@ -136,67 +149,67 @@ const EditWorkflow = (props) => {
|
||||
var total_count = 0
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
<Drawer
|
||||
open={modalOpen}
|
||||
onClose={() => {
|
||||
setModalOpen(false);
|
||||
}}
|
||||
PaperProps={{
|
||||
style: {
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
color: "white",
|
||||
minWidth: isMobile ? "90%" : 550,
|
||||
maxWidth: isMobile ? "90%" : 550,
|
||||
minHeight: 400,
|
||||
minWidth: isMobile ? "90%" : 650,
|
||||
maxWidth: isMobile ? "90%" : 650,
|
||||
minHeight: 400,
|
||||
paddingTop: 25,
|
||||
//minWidth: isMobile ? "90%" : newWorkflow === true ? 1000 : 550,
|
||||
//maxWidth: isMobile ? "90%" : newWorkflow === true ? 1000 : 550,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<DialogTitle style={{padding: 30, paddingBottom: 0, zIndex: 1000,}}>
|
||||
<div style={{display: "flex"}}>
|
||||
<div style={{display: "flex"}}>
|
||||
<div style={{flex: 1, color: "rgba(255,255,255,0.9)" }}>
|
||||
<div style={{display: "flex"}}>
|
||||
<Typography variant="h6" style={{flex: 9, }}>
|
||||
{newWorkflow ? "New" : "Editing"} workflow
|
||||
</Typography>
|
||||
{newWorkflow === true ? null :
|
||||
<div style={{ marginLeft: 5, flex: 1 }}>
|
||||
<Tooltip title="Open Workflow Form for 'normal' users">
|
||||
<a
|
||||
rel="noopener noreferrer"
|
||||
href={`/workflows/${workflow.id}/run`}
|
||||
target="_blank"
|
||||
style={{
|
||||
textDecoration: "none",
|
||||
color: "#f85a3e",
|
||||
marginLeft: 5,
|
||||
marginTop: 10,
|
||||
}}
|
||||
>
|
||||
<OpenInNewIcon />
|
||||
</a>
|
||||
</Tooltip>
|
||||
</div>
|
||||
}
|
||||
<div style={{display: "flex"}}>
|
||||
<Typography variant="h6" style={{flex: 9, }}>
|
||||
{newWorkflow ? "New" : "Editing"} workflow
|
||||
</Typography>
|
||||
{newWorkflow === true ? null :
|
||||
<div style={{ marginLeft: 5, flex: 1 }}>
|
||||
<Tooltip title="Open Workflow Form for 'normal' users">
|
||||
<a
|
||||
rel="noopener noreferrer"
|
||||
href={`/workflows/${workflow.id}/run`}
|
||||
target="_blank"
|
||||
style={{
|
||||
textDecoration: "none",
|
||||
color: "#f85a3e",
|
||||
marginLeft: 5,
|
||||
marginTop: 10,
|
||||
}}
|
||||
>
|
||||
<OpenInNewIcon />
|
||||
</a>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<Typography variant="body2" color="textSecondary" style={{maxWidth: 440,}}>
|
||||
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>
|
||||
</Typography>
|
||||
{showUpload === true ?
|
||||
<div style={{ float: "right" }}>
|
||||
<Tooltip color="primary" title={"Import manually"} placement="top">
|
||||
<Button
|
||||
color="primary"
|
||||
style={{}}
|
||||
variant="text"
|
||||
onClick={() => upload.click()}
|
||||
>
|
||||
<PublishIcon />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
: null}
|
||||
}
|
||||
</div>
|
||||
<Typography variant="body2" color="textSecondary" style={{maxWidth: 440,}}>
|
||||
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>
|
||||
</Typography>
|
||||
{showUpload === true ?
|
||||
<div style={{ float: "right" }}>
|
||||
<Tooltip color="primary" title={"Import manually"} placement="top">
|
||||
<Button
|
||||
color="primary"
|
||||
style={{}}
|
||||
variant="text"
|
||||
onClick={() => upload.click()}
|
||||
>
|
||||
<PublishIcon />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
: null}
|
||||
</div>
|
||||
{/*newWorkflow === true ?
|
||||
<div style={{flex: 1, marginLeft: 45, }}>
|
||||
@@ -211,12 +224,12 @@ const EditWorkflow = (props) => {
|
||||
</div>
|
||||
</DialogTitle>
|
||||
<FormControl>
|
||||
<DialogContent style={{paddingTop: 10, display: "flex", minHeight: 350, zIndex: 1001, }}>
|
||||
<div style={{minWidth: newWorkflow ? 450 : 500, maxWidth: newWorkflow ? 450 : 500, }}>
|
||||
<DialogContent style={{paddingTop: 10, display: "flex", minHeight: 300, zIndex: 1001, }}>
|
||||
<div style={{minWidth: newWorkflow ? 500 : 550, maxWidth: newWorkflow ? 450 : 500, }}>
|
||||
<TextField
|
||||
onBlur={(event) => {
|
||||
setName(event.target.value)
|
||||
}}
|
||||
onChange={(event) => {
|
||||
setName(event.target.value)
|
||||
}}
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
@@ -231,27 +244,29 @@ const EditWorkflow = (props) => {
|
||||
autoFocus
|
||||
fullWidth
|
||||
/>
|
||||
<TextField
|
||||
onBlur={(event) => {
|
||||
setDescription(event.target.value)
|
||||
}}
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
maxRows={4}
|
||||
color="primary"
|
||||
defaultValue={innerWorkflow.description}
|
||||
placeholder="Description"
|
||||
multiline
|
||||
label="Description"
|
||||
margin="dense"
|
||||
fullWidth
|
||||
/>
|
||||
<div style={{display: "flex", }}>
|
||||
<TextField
|
||||
onBlur={(event) => {
|
||||
setDescription(event.target.value)
|
||||
}}
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
maxRows={4}
|
||||
color="primary"
|
||||
defaultValue={innerWorkflow.description}
|
||||
placeholder="Description"
|
||||
multiline
|
||||
label="Description"
|
||||
margin="dense"
|
||||
fullWidth
|
||||
/>
|
||||
</div>
|
||||
<div style={{display: "flex", marginTop: 10, }}>
|
||||
<ChipInput
|
||||
style={{ flex: 1, maxHeight: 40, marginTop: 12, overflow: "auto", }}
|
||||
<MuiChipsInput
|
||||
style={{ flex: 1, maxHeight: 40, }}
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
@@ -261,13 +276,20 @@ const EditWorkflow = (props) => {
|
||||
color="primary"
|
||||
fullWidth
|
||||
value={newWorkflowTags}
|
||||
onChange={(chip) => {
|
||||
console.log("Chip: ", chip)
|
||||
//newWorkflowTags.push(chip);
|
||||
setNewWorkflowTags(chip);
|
||||
}}
|
||||
onAdd={(chip) => {
|
||||
newWorkflowTags.push(chip);
|
||||
setNewWorkflowTags(newWorkflowTags);
|
||||
}}
|
||||
onDelete={(chip, index) => {
|
||||
console.log("Deleting: ", chip, index)
|
||||
newWorkflowTags.splice(index, 1);
|
||||
setNewWorkflowTags(newWorkflowTags);
|
||||
setUpdate(Math.random());
|
||||
}}
|
||||
/>
|
||||
{usecases !== null && usecases !== undefined && usecases.length > 0 ?
|
||||
@@ -328,26 +350,41 @@ const EditWorkflow = (props) => {
|
||||
|
||||
{showMoreClicked === true ?
|
||||
<span style={{marginTop: 25, }}>
|
||||
<div style={{display: "flex"}}>
|
||||
<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" />
|
||||
|
||||
<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)
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
<LocalizationProvider dateAdapter={AdapterDayjs}>
|
||||
<DatePicker
|
||||
sx={{
|
||||
marginTop: 3,
|
||||
marginLeft: 3,
|
||||
}}
|
||||
>
|
||||
<FormControlLabel value="test" control={<Radio />} label="Test" />
|
||||
<FormControlLabel value="production" control={<Radio />} label="Production" />
|
||||
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
value={dueDate}
|
||||
label="Due Date"
|
||||
format="YYYY-MM-DD"
|
||||
onChange={(newValue) => {
|
||||
setDueDate(newValue)
|
||||
}}
|
||||
/>
|
||||
</LocalizationProvider>
|
||||
</div>
|
||||
<div />
|
||||
|
||||
<FormControl style={{marginTop: 15, }}>
|
||||
@@ -430,38 +467,28 @@ const EditWorkflow = (props) => {
|
||||
</span>
|
||||
: 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>
|
||||
{/*newWorkflow === true ?
|
||||
<div style={{marginLeft: 50, maxWidth: 400, minWidth: 400, position: "relative",}}>
|
||||
<UsecaseSearch
|
||||
globalUrl={globalUrl}
|
||||
appFramework={appFramework}
|
||||
defaultSearch={undefined}
|
||||
apps={undefined}
|
||||
setFoundWorkflowId={setFoundWorkflowId}
|
||||
userdata={userdata}
|
||||
/>
|
||||
</div>
|
||||
: null*/}
|
||||
<IconButton
|
||||
style={{ color: "white", margin: "auto", marginTop: 10, textAlign: "center", width: 50,}}
|
||||
onClick={() => {
|
||||
setShowMoreClicked(!showMoreClicked);
|
||||
}}
|
||||
>
|
||||
{showMoreClicked ? <ExpandLessIcon /> : <ExpandMoreIcon/>}
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
|
||||
<DialogActions style={{paddingRight: 20, }}>
|
||||
<Button
|
||||
style={{}}
|
||||
onClick={() => {
|
||||
if (setNewWorkflow !== undefined) {
|
||||
setWorkflow({})
|
||||
}
|
||||
if (setNewWorkflow !== undefined) {
|
||||
setWorkflow({})
|
||||
}
|
||||
|
||||
setModalOpen(false)
|
||||
setModalOpen(false)
|
||||
}}
|
||||
color="primary"
|
||||
>
|
||||
@@ -470,46 +497,73 @@ const EditWorkflow = (props) => {
|
||||
<Button
|
||||
variant="contained"
|
||||
style={{}}
|
||||
disabled={name.length === 0}
|
||||
disabled={name.length === 0 || submitLoading === true}
|
||||
onClick={() => {
|
||||
innerWorkflow.name = name
|
||||
innerWorkflow.description = description
|
||||
if (newWorkflowTags.length > 0) {
|
||||
innerWorkflow.tags = newWorkflowTags
|
||||
}
|
||||
setSubmitLoading(true)
|
||||
|
||||
if (selectedUsecases.length > 0) {
|
||||
innerWorkflow.usecase_ids = selectedUsecases
|
||||
}
|
||||
innerWorkflow.name = name
|
||||
innerWorkflow.description = description
|
||||
if (newWorkflowTags.length > 0) {
|
||||
innerWorkflow.tags = newWorkflowTags
|
||||
}
|
||||
|
||||
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)
|
||||
console.log("editing workflow: ", innerWorkflow)
|
||||
}
|
||||
|
||||
setModalOpen(false)
|
||||
if (dueDate > 0) {
|
||||
innerWorkflow.due_date = new Date(`${dueDate["$y"]}-${dueDate["$M"]+1}-${dueDate["$D"]}`).getTime()/1000
|
||||
}
|
||||
|
||||
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)
|
||||
console.log("editing workflow: ", innerWorkflow)
|
||||
}
|
||||
|
||||
setSubmitLoading(true)
|
||||
|
||||
// If new workflow, don't close it
|
||||
if (isEditing) {
|
||||
setModalOpen(false)
|
||||
}
|
||||
}}
|
||||
color="primary"
|
||||
>
|
||||
{submitLoading ? <CircularProgress color="secondary" /> : "Submit"}
|
||||
</Button>
|
||||
</DialogActions>
|
||||
|
||||
{newWorkflow === true && name.length > 5 ?
|
||||
<div style={{marginLeft: 30, }}>
|
||||
<WorkflowGrid
|
||||
maxRows={1}
|
||||
globalUrl={globalUrl}
|
||||
showSuggestions={false}
|
||||
isMobile={isMobile}
|
||||
userdata={userdata}
|
||||
inputsearch={name+description+newWorkflowTags.join(" ")}
|
||||
|
||||
parsedXs={6}
|
||||
alternativeView={false}
|
||||
onlyResults={true}
|
||||
/>
|
||||
</div>
|
||||
: null}
|
||||
</FormControl>
|
||||
</Dialog>
|
||||
</Drawer>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
import {
|
||||
IconButton,
|
||||
@@ -29,7 +30,7 @@ import {
|
||||
Add as AddIcon,
|
||||
} from "@mui/icons-material";
|
||||
|
||||
import { useAlert } from "react-alert";
|
||||
//import { useAlert
|
||||
import Dropzone from "../components/Dropzone.jsx";
|
||||
import CodeEditor from "../components/ShuffleCodeEditor.jsx";
|
||||
import theme from "../theme.jsx";
|
||||
@@ -45,7 +46,7 @@ const Files = (props) => {
|
||||
const [openEditor, setOpenEditor] = React.useState(false);
|
||||
const [renderTextBox, setRenderTextBox] = React.useState(false);
|
||||
|
||||
const alert = useAlert();
|
||||
//const alert = useAlert();
|
||||
const allowedFileTypes = ["txt", "py", "yaml", "yml","json", "html", "js", "csv", "log"]
|
||||
var upload = "";
|
||||
|
||||
@@ -113,7 +114,7 @@ const Files = (props) => {
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
@@ -139,12 +140,12 @@ const Files = (props) => {
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success) {
|
||||
alert.info("Successfully deleted file " + file.name);
|
||||
toast("Successfully deleted file " + file.name);
|
||||
} else if (
|
||||
responseJson.reason !== undefined &&
|
||||
responseJson.reason !== null
|
||||
) {
|
||||
alert.error("Failed to delete file: " + responseJson.reason);
|
||||
toast("Failed to delete file: " + responseJson.reason);
|
||||
}
|
||||
setTimeout(() => {
|
||||
getFiles();
|
||||
@@ -153,7 +154,7 @@ const Files = (props) => {
|
||||
console.log(responseJson);
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
@@ -178,7 +179,7 @@ const Files = (props) => {
|
||||
// console.log("respdata type ->", typeof(respdata));
|
||||
|
||||
if (respdata.length === 0) {
|
||||
alert.error("Failed getting file. Is it deleted?");
|
||||
toast("Failed getting file. Is it deleted?");
|
||||
return;
|
||||
}
|
||||
return respdata
|
||||
@@ -189,7 +190,7 @@ const Files = (props) => {
|
||||
//console.log("filecontent state ",fileContent);
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
@@ -210,7 +211,7 @@ const Files = (props) => {
|
||||
})
|
||||
.then((respdata) => {
|
||||
if (respdata.length === 0) {
|
||||
alert.error("Failed getting file. Is it deleted?");
|
||||
toast("Failed getting file. Is it deleted?");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -249,7 +250,7 @@ const Files = (props) => {
|
||||
//setSchedules(responseJson)
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
@@ -291,11 +292,11 @@ const Files = (props) => {
|
||||
if (responseJson.success === true) {
|
||||
handleFileUpload(responseJson.id, file);
|
||||
} else {
|
||||
alert.error("Failed to upload file ", filename);
|
||||
toast("Failed to upload file ", filename);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error("Failed to upload file ", filename);
|
||||
toast("Failed to upload file ", filename);
|
||||
console.log(error.toString());
|
||||
});
|
||||
};
|
||||
@@ -312,7 +313,7 @@ const Files = (props) => {
|
||||
.then((response) => {
|
||||
if (response.status !== 200 && response.status !== 201) {
|
||||
console.log("Status not 200 for apps :O!");
|
||||
alert.error("File was created, but failed to upload.");
|
||||
toast("File was created, but failed to upload.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -323,7 +324,7 @@ const Files = (props) => {
|
||||
//setFiles(responseJson)
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
@@ -364,7 +365,7 @@ const Files = (props) => {
|
||||
const files = isDropzone ? e.dataTransfer.files : e.target.files;
|
||||
|
||||
//const reader = new FileReader();
|
||||
//alert.info("Starting fileupload")
|
||||
//toast("Starting fileupload")
|
||||
uploadFiles(files);
|
||||
};
|
||||
|
||||
@@ -681,7 +682,7 @@ const Files = (props) => {
|
||||
<ListItemText
|
||||
primary=<span style={{ display:"inline"}}>
|
||||
<Tooltip
|
||||
title={`Edit File (${allowedFileTypes.join(", ")})`}
|
||||
title={`Edit File (${allowedFileTypes.join(", ")}). Max size 2MB`}
|
||||
style={{}}
|
||||
aria-label={"Edit"}
|
||||
>
|
||||
@@ -742,7 +743,7 @@ const Files = (props) => {
|
||||
) {
|
||||
const clipboard = navigator.clipboard;
|
||||
if (clipboard === undefined) {
|
||||
alert.error(
|
||||
toast(
|
||||
"Can only copy over HTTPS (port 3443)"
|
||||
);
|
||||
return;
|
||||
@@ -758,7 +759,7 @@ const Files = (props) => {
|
||||
/* Copy the text inside the text field */
|
||||
document.execCommand("copy");
|
||||
|
||||
alert.info(file.id + " copied to clipboard");
|
||||
toast(file.id + " copied to clipboard");
|
||||
}
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
import React from "react";
|
||||
|
||||
//import List from '@material-ui/core/List';
|
||||
//import ListItem from '@material-ui/core/ListItem';
|
||||
|
||||
//borderTop: "1px solid #385F71"
|
||||
const FooterStyle = {
|
||||
right: "0",
|
||||
left: "0",
|
||||
bottom: "0",
|
||||
height: "130px",
|
||||
backgroundColor: "rgba(15, 14, 31, 1)",
|
||||
};
|
||||
|
||||
const FooterInfo = {
|
||||
maxWidth: "1150px",
|
||||
minWidth: "768px",
|
||||
textAlign: "center",
|
||||
margin: "auto",
|
||||
};
|
||||
|
||||
const hrefStyle = {
|
||||
color: "#bdbdbd",
|
||||
textDecoration: "none",
|
||||
};
|
||||
|
||||
const Footer = (props) => {
|
||||
return (
|
||||
<div style={FooterStyle}>
|
||||
<div style={FooterInfo}>
|
||||
<Box />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Box = (props) => {
|
||||
return (
|
||||
<div style={{ display: "flex" }}>
|
||||
<div style={{ flex: "1" }}>
|
||||
<a style={hrefStyle} href="/about">
|
||||
<h1>About</h1>
|
||||
</a>
|
||||
</div>
|
||||
<div style={{ flex: "1" }}>
|
||||
<a style={hrefStyle} href="/privacy-policy">
|
||||
<h1>Privacy Policy</h1>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Footer;
|
||||
@@ -1,8 +1,9 @@
|
||||
import React, {useState} from 'react';
|
||||
import {BrowserView, MobileView} from "react-device-detect";
|
||||
import { toast } from 'react-toastify';
|
||||
import theme from '../theme.jsx';
|
||||
import {BrowserView, MobileView} from "react-device-detect";
|
||||
|
||||
import {Link} from 'react-router-dom';
|
||||
import { useNavigate, Link } from "react-router-dom";
|
||||
import ReactGA from 'react-ga4';
|
||||
|
||||
import {
|
||||
@@ -21,7 +22,7 @@ import {
|
||||
IconButton,
|
||||
Divider,
|
||||
LinearProgress,
|
||||
} from '@mui/material';
|
||||
} from '@mui/material'
|
||||
|
||||
import {
|
||||
MeetingRoom as MeetingRoomIcon,
|
||||
@@ -29,16 +30,20 @@ import {
|
||||
Settings as SettingsIcon,
|
||||
Notifications as NotificationsIcon,
|
||||
Home as HomeIcon,
|
||||
Polymer as PolymerIcon,
|
||||
Apps as AppsIcon,
|
||||
Description as DescriptionIcon,
|
||||
EmojiObjects as EmojiObjectsIcon,
|
||||
Business as BusinessIcon,
|
||||
Analytics as AnalyticsIcon,
|
||||
Lightbulb as LightbulbIcon,
|
||||
|
||||
Polyline as PolylineIcon,
|
||||
} from '@mui/icons-material';
|
||||
|
||||
import { useAlert } from "react-alert";
|
||||
import {
|
||||
Analytics as AnalyticsIcon,
|
||||
Lightbulb as LightbulbIcon,
|
||||
} from "@mui/icons-material";
|
||||
|
||||
//import { useAlert
|
||||
|
||||
import SearchField from '../components/Searchfield.jsx'
|
||||
const hoverColor = "#f85a3e"
|
||||
@@ -46,7 +51,8 @@ const hoverOutColor = "#e8eaf6"
|
||||
|
||||
const Header = props => {
|
||||
const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, homePage, userdata, serverside, } = props;
|
||||
const alert = useAlert()
|
||||
//const theme = useTheme();
|
||||
//const alert = useAlert()
|
||||
|
||||
|
||||
const [HomeHoverColor, setHomeHoverColor] = useState(hoverOutColor);
|
||||
@@ -57,6 +63,7 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho
|
||||
const [anchorEl, setAnchorEl] = React.useState(null);
|
||||
const [anchorElAvatar, setAnchorElAvatar] = React.useState(null);
|
||||
const [subAnchorEl, setSubAnchorEl] = React.useState(null);
|
||||
let navigate = useNavigate();
|
||||
|
||||
|
||||
const handleClick = (event) => {
|
||||
@@ -95,7 +102,7 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho
|
||||
setNotifications([])
|
||||
handleClose()
|
||||
} else {
|
||||
alert.error("Failed dismissing notifications. Please try again later.")
|
||||
toast("Failed dismissing notifications. Please try again later.")
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
@@ -125,7 +132,7 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho
|
||||
console.log("NEW NOTIFICATIONS: ", newNotifications)
|
||||
setNotifications(newNotifications)
|
||||
} else {
|
||||
alert.error("Failed dismissing notification. Please try again later.")
|
||||
toast("Failed dismissing notification. Please try again later.")
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
@@ -400,9 +407,9 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho
|
||||
setTimeout(() => {
|
||||
window.location.reload()
|
||||
}, 2000)
|
||||
alert.success("Successfully changed active organization - refreshing!")
|
||||
toast("Successfully changed active organization - refreshing!")
|
||||
} else {
|
||||
alert.error("Failed changing org: ", responseJson.reason)
|
||||
toast("Failed changing org: ", responseJson.reason)
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
@@ -514,6 +521,7 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho
|
||||
}
|
||||
|
||||
// Handle top bar or something
|
||||
const defaultTop = 7
|
||||
const loginTextBrowser = !isLoggedIn ?
|
||||
<div style={{display: "flex", minWidth: 1250, maxWidth: 1250, margin: "auto", textAlign: "center",}}>
|
||||
<div style={{display: "flex", flex: 1, }}>
|
||||
@@ -664,7 +672,7 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho
|
||||
</div>
|
||||
</div>
|
||||
:
|
||||
<div style={{display: "flex", backgroundColor: "#1f2023",}}>
|
||||
<div style={{display: "flex", }}>
|
||||
<div style={{minWidth: 1250, maxWidth: 1250, display: "flex", margin: "auto", }}>
|
||||
<div style={{flex: 1, flexDirection: "row"}}>
|
||||
<List style={{height: 56, marginTop: "auto", marginBottom: "auto", display: "flex", flexDirect: "row", alignItems: "baseline", maxWidth: 340, }} component="nav">
|
||||
@@ -686,9 +694,9 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho
|
||||
<Link to="/workflows" style={hrefStyle}>
|
||||
<div onMouseOver={handleSoarHover} onMouseOut={handleSoarHoverOut} style={{color: SoarHoverColor, cursor: "pointer", display: "flex"}}>
|
||||
{/*
|
||||
<PolymerIcon style={{marginRight: "5px"}} />
|
||||
<PolylineIcon style={{marginRight: "5px"}} />
|
||||
*/}
|
||||
<Typography style={{marginTop: 5, marginRight: 8, }}>Workflows</Typography>
|
||||
<Typography style={{marginTop: defaultTop, marginRight: 8, }}>Workflows</Typography>
|
||||
</div>
|
||||
</Link>
|
||||
</ListItem>
|
||||
@@ -698,17 +706,24 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho
|
||||
{/*
|
||||
<AppsIcon style={{marginRight: "5px"}} />
|
||||
*/}
|
||||
<Typography style={{marginTop: 5, marginRight: 5, }}>Apps</Typography>
|
||||
<Typography style={{marginTop: defaultTop, marginRight: 5, }}>Apps</Typography>
|
||||
</div>
|
||||
</Link>
|
||||
</ListItem>
|
||||
{/*
|
||||
<ListItem style={{textAlign: "center"}}>
|
||||
<Link to="/dashboard" style={hrefStyle}>
|
||||
<div onMouseOver={handleDocsHover} onMouseOut={handleDocsHoverOut} style={{color: DocsHoverColor, cursor: "pointer"}}>Dashboard</div>
|
||||
</Link>
|
||||
</ListItem>
|
||||
*/}
|
||||
<ListItem style={listItemStyle}>
|
||||
<Link to="/docs" style={hrefStyle}>
|
||||
<div onMouseOver={handleDocsHover} onMouseOut={handleDocsHoverOut} style={{color: DocsHoverColor, cursor: "pointer", display: "flex"}}>
|
||||
{/*
|
||||
<DescriptionIcon style={{marginRight: "5px"}} />
|
||||
*/}
|
||||
<Typography style={{marginTop: 5,}}>Docs</Typography>
|
||||
<Typography style={{marginTop: defaultTop,}}>Docs</Typography>
|
||||
</div>
|
||||
</Link>
|
||||
</ListItem>
|
||||
@@ -782,14 +797,13 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho
|
||||
</ListItem>
|
||||
: null*/}
|
||||
|
||||
|
||||
|
||||
{userdata === undefined || userdata.orgs === undefined || userdata.orgs === null || userdata.orgs.length <= 1 ?
|
||||
null
|
||||
:
|
||||
<span style={{paddingTop: 12, }}>
|
||||
<Select
|
||||
disableunderline
|
||||
|
||||
disableUnderline
|
||||
SelectDisplayProps={{
|
||||
style: {
|
||||
maxWidth: 50,
|
||||
@@ -883,13 +897,32 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho
|
||||
</span>
|
||||
}
|
||||
|
||||
{/* Show on cloud, if not suborg and if not customer/pov/internal */}
|
||||
{isCloud && (userdata.org_status === undefined || userdata.org_status === null || userdata.org_status.length === 0) ?
|
||||
<ListItem style={{textAlign: "center", marginLeft: 0, marginRight: 7, marginTop: 3, }}>
|
||||
<Link to ="/pricing?tab=cloud&highlight=true" style={hrefStyle}>
|
||||
<Button variant="contained" color="primary" style={{textTransform: "none"}} onClick={() => {
|
||||
ReactGA.event({
|
||||
category: "header",
|
||||
action: "pricing_upgrade_click",
|
||||
label: "",
|
||||
})
|
||||
}}>
|
||||
Upgrade
|
||||
</Button>
|
||||
</Link>
|
||||
</ListItem>
|
||||
: null}
|
||||
|
||||
{userdata === undefined || userdata.app_execution_limit === undefined || userdata.app_execution_usage === undefined || userdata.app_execution_usage < 1000 ?
|
||||
null
|
||||
:
|
||||
<Tooltip title={`Amount of executions left: ${userdata.app_execution_usage} / ${userdata.app_execution_limit}. When the limit is reached, you can still use Shuffle normally, but your Workflow triggers may stop working. Reach out to support@shuffler.io to extend this limit.`}>
|
||||
<div style={{maxHeight: 30, minHeight: 30, padding: 8, textAlign: "center", cursor: "pointer", borderRadius: theme.palette.borderRadius, marginRight: 10, marginTop: 12, backgroundColor: theme.palette.surfaceColor, minWidth: 60, maxWidth: 60, }} onClick={() => {
|
||||
<div style={{maxHeight: 30, minHeight: 30, padding: 8, textAlign: "center", cursor: "pointer", borderRadius: theme.palette.borderRadius, marginRight: 10, marginTop: 12, backgroundColor: theme.palette.surfaceColor, minWidth: 60, maxWidth: 60, border: userdata.app_execution_usage/userdata.app_execution_limit >= 0.9 ? "#f86a3e" : null, }} onClick={() => {
|
||||
console.log(userdata.appe_execution_usage/userdata.app_execution_limit)
|
||||
if (window.drift !== undefined) {
|
||||
window.drift.api.startInteraction({ interactionId: 326905 })
|
||||
navigate("/pricing")
|
||||
} else {
|
||||
console.log("Couldn't find drift in window.drift and not .drift-open-chat with querySelector: ", window.drift)
|
||||
}
|
||||
@@ -999,16 +1032,16 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho
|
||||
const loadedCheck =
|
||||
<div style={{minHeight: 68}}>
|
||||
<BrowserView>
|
||||
{loginTextBrowser}
|
||||
{loginTextBrowser}
|
||||
</BrowserView>
|
||||
<MobileView>
|
||||
{loginTextMobile}
|
||||
{loginTextMobile}
|
||||
</MobileView>
|
||||
</div>
|
||||
// <div style={{backgroundImage: "linear-gradient(-90deg,#342f78 0,#29255e 50%,#1b1947 100%"}}>
|
||||
return (
|
||||
<div style={{backgroundColor: props.color === "undefined" ? "inherit" : props.color}}>
|
||||
{loadedCheck}
|
||||
<div style={{backgroundColor: theme.palette.backgroundColor, }}>
|
||||
{loadedCheck}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,176 +0,0 @@
|
||||
/* eslint-disable react/no-multi-comp */
|
||||
import React, { useState } from "react";
|
||||
|
||||
import DialogTitle from "@material-ui/core/DialogTitle";
|
||||
import Dialog from "@material-ui/core/Dialog";
|
||||
import TextField from "@material-ui/core/TextField";
|
||||
import Button from "@material-ui/core/Button";
|
||||
|
||||
const LoginDialog = (props) => {
|
||||
const {
|
||||
classes,
|
||||
onClose,
|
||||
open,
|
||||
globalUrl,
|
||||
isLoggedIn,
|
||||
setIsLoggedIn,
|
||||
...other
|
||||
} = props;
|
||||
|
||||
const [username, setUsername] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
//const [selectedValue, setSelectedValue] = useState(false);
|
||||
|
||||
// Used to swap from login to register. True = login, false = register
|
||||
const [loginCheck, setLoginCheck] = useState(true);
|
||||
|
||||
// Error messages etc
|
||||
const [loginInfo, setLoginInfo] = useState("");
|
||||
|
||||
const handleValidateForm = () => {
|
||||
return username.length > 1 && password.length > 8;
|
||||
};
|
||||
|
||||
const onSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
// Just use this one?
|
||||
var data =
|
||||
'{"username": "' + username + '", "password": "' + password + '"}';
|
||||
var baseurl = globalUrl;
|
||||
if (loginCheck) {
|
||||
var url = baseurl + "/login";
|
||||
fetch(url, {
|
||||
method: "POST",
|
||||
body: data,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
})
|
||||
.then((response) =>
|
||||
response.json().then((responseJson) => {
|
||||
console.log(responseJson);
|
||||
//console.log(e)
|
||||
if (responseJson["success"] === false) {
|
||||
setLoginInfo(responseJson["reason"]);
|
||||
} else {
|
||||
setLoginInfo("Successful login :)");
|
||||
onClose();
|
||||
setIsLoggedIn(true);
|
||||
}
|
||||
})
|
||||
)
|
||||
.catch((error) => {
|
||||
setLoginInfo("Error in userdata");
|
||||
});
|
||||
} else {
|
||||
url = baseurl + "/register";
|
||||
fetch(url, {
|
||||
method: "POST",
|
||||
body: data,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
})
|
||||
.then((response) =>
|
||||
response.json().then((responseJson) => {
|
||||
if (responseJson["success"] === false) {
|
||||
setLoginInfo(responseJson["reason"]);
|
||||
} else {
|
||||
setLoginInfo("Successful register :)");
|
||||
onClose();
|
||||
setIsLoggedIn(true);
|
||||
}
|
||||
})
|
||||
)
|
||||
.catch((error) => {
|
||||
setLoginInfo("Error in userdata");
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const onChangeUser = (e) => {
|
||||
setUsername(e.target.value);
|
||||
};
|
||||
|
||||
const onChangePass = (e) => {
|
||||
setPassword(e.target.value);
|
||||
};
|
||||
|
||||
const onClickRegister = () => {
|
||||
setLoginCheck(!loginCheck);
|
||||
};
|
||||
|
||||
//var loginChange = loginCheck ? (<div><p onClick={setLoginCheck(false)}>Want to register? Click here.</p></div>) : (<div><p onClick={setLoginCheck(true)}>Go back to login? Click here.</p></div>);
|
||||
var formtitle = loginCheck ? <div>Login</div> : <div>Register</div>;
|
||||
var formButton = loginCheck ? (
|
||||
<div>Click to Register</div>
|
||||
) : (
|
||||
<div>Click to Login</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<Dialog modal open={open} onClose={onClose} {...other}>
|
||||
<DialogTitle>{formtitle}</DialogTitle>
|
||||
<form onSubmit={onSubmit} style={{ margin: "15px 15px 15px 15px" }}>
|
||||
Username
|
||||
<div>
|
||||
<TextField
|
||||
required
|
||||
id="standard-required"
|
||||
autoComplete="username"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
onChange={onChangeUser}
|
||||
/>
|
||||
</div>
|
||||
Password
|
||||
<div>
|
||||
<TextField
|
||||
id="outlined-password-input"
|
||||
type="password"
|
||||
autoComplete="current-password"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
onChange={onChangePass}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ display: "flex", marginTop: "15px" }}>
|
||||
<Button
|
||||
color="secondary"
|
||||
variant="contained"
|
||||
type="submit"
|
||||
style={{ flex: "1", marginRight: "5px" }}
|
||||
disabled={!handleValidateForm()}
|
||||
>
|
||||
SUBMIT
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
color="primary"
|
||||
variant="contained"
|
||||
type="button"
|
||||
style={{ flex: "1" }}
|
||||
onClick={onClose}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
{loginInfo}
|
||||
</form>
|
||||
<div style={{ display: "flex" }}>
|
||||
<Button
|
||||
color="secondary"
|
||||
variant="contained"
|
||||
onClick={onClickRegister}
|
||||
type="button"
|
||||
style={{ flex: "1" }}
|
||||
>
|
||||
{formButton}
|
||||
</Button>
|
||||
</div>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default LoginDialog;
|
||||
@@ -1,213 +0,0 @@
|
||||
import React, { useState, useRef, useImperativeHandle } from "react";
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import Menu, { MenuProps } from "@material-ui/core/Menu";
|
||||
import MenuItem, { MenuItemProps } from "@material-ui/core/MenuItem";
|
||||
import ArrowRight from "@material-ui/icons/ArrowRight";
|
||||
import clsx from "clsx";
|
||||
|
||||
//<MenuItemProps, 'button'>
|
||||
|
||||
//export interface NestedMenuItemProps {
|
||||
// /**
|
||||
// * Open state of parent `<Menu />`, used to close decendent menus when the
|
||||
// * root menu is closed.
|
||||
// */
|
||||
// parentMenuOpen: boolean;
|
||||
// /**
|
||||
// * Component for the container element.
|
||||
// * @default 'div'
|
||||
// */
|
||||
// component: React.ElementType;
|
||||
// /**
|
||||
// * Effectively becomes the `children` prop passed to the `<MenuItem/>`
|
||||
// * element.
|
||||
// */
|
||||
// label: React.ReactNode;
|
||||
// /**
|
||||
// * @default <ArrowRight />
|
||||
// */
|
||||
// rightIcon: React.ReactNode;
|
||||
// /**
|
||||
// * Props passed to container element.
|
||||
// */
|
||||
// ContainerProps: React.HTMLAttributes;
|
||||
// //<HTMLElement> &React.RefAttributes<HTMLElement | null>
|
||||
// /**
|
||||
// * Props passed to sub `<Menu/>` element
|
||||
// */
|
||||
// MenuProps: Omit<MenuProps, 'children'>;
|
||||
// /**
|
||||
// * @see https://material-ui.com/api/list-item/
|
||||
// */
|
||||
// button: true;
|
||||
//}
|
||||
|
||||
const TRANSPARENT = "rgba(0,0,0,0)";
|
||||
const useMenuItemStyles = makeStyles((theme) => ({
|
||||
root: (props: any) => ({
|
||||
backgroundColor: props.open ? theme.palette.action.hover : TRANSPARENT,
|
||||
}),
|
||||
}));
|
||||
|
||||
/**
|
||||
* Use as a drop-in replacement for `<MenuItem>` when you need to add cascading
|
||||
* menu elements as children to this component.
|
||||
*/
|
||||
//const NestedMenuItem = React.forwardRef<NestedMenuItemProps>(
|
||||
const NestedMenuItem = (props, ref) => {
|
||||
console.log(props, ref);
|
||||
//function NestedMenuItem(props, ref) {
|
||||
const {
|
||||
parentMenuOpen,
|
||||
component = "div",
|
||||
label,
|
||||
rightIcon = <ArrowRight />,
|
||||
children,
|
||||
className,
|
||||
tabIndex: tabIndexProp,
|
||||
MenuProps = {},
|
||||
ContainerProps: ContainerPropsProp = {},
|
||||
...MenuItemProps
|
||||
} = props;
|
||||
|
||||
const [isSubMenuOpen, setIsSubMenuOpen] = useState(false);
|
||||
|
||||
const { ref: containerRefProp, ...ContainerProps } = ContainerPropsProp;
|
||||
|
||||
const menuItemRef = useRef < HTMLLIElement > null;
|
||||
useImperativeHandle(ref, () => menuItemRef.current);
|
||||
const containerRef = useRef < HTMLDivElement > null;
|
||||
useImperativeHandle(containerRefProp, () => containerRef.current);
|
||||
const menuContainerRef = useRef < HTMLDivElement > null;
|
||||
|
||||
console.log(
|
||||
"PAST THIS: ",
|
||||
containerRefProp,
|
||||
menuItemRef,
|
||||
containerRef,
|
||||
menuContainerRef,
|
||||
ContainerProps
|
||||
);
|
||||
|
||||
const handleMouseEnter = (event: React.MouseEvent<HTMLElement>) => {
|
||||
setIsSubMenuOpen(true);
|
||||
|
||||
if (ContainerProps?.onMouseEnter) {
|
||||
ContainerProps.onMouseEnter(event);
|
||||
}
|
||||
};
|
||||
const handleMouseLeave = (event: React.MouseEvent<HTMLElement>) => {
|
||||
setIsSubMenuOpen(false);
|
||||
|
||||
if (ContainerProps?.onMouseLeave) {
|
||||
ContainerProps.onMouseLeave(event);
|
||||
}
|
||||
};
|
||||
|
||||
// Check if any immediate children are active
|
||||
const isSubmenuFocused = () => {
|
||||
const active = containerRef.current?.ownerDocument?.activeElement;
|
||||
for (const child of menuContainerRef.current?.children ?? []) {
|
||||
if (child === active) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const handleFocus = (event: React.FocusEvent<HTMLElement>) => {
|
||||
if (event.target === containerRef.current) {
|
||||
setIsSubMenuOpen(true);
|
||||
}
|
||||
|
||||
if (ContainerProps?.onFocus) {
|
||||
ContainerProps.onFocus(event);
|
||||
}
|
||||
};
|
||||
|
||||
const handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {
|
||||
if (event.key === "Escape") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isSubmenuFocused()) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
const active = containerRef.current?.ownerDocument?.activeElement;
|
||||
|
||||
if (event.key === "ArrowLeft" && isSubmenuFocused()) {
|
||||
containerRef.current?.focus();
|
||||
}
|
||||
|
||||
if (
|
||||
event.key === "ArrowRight" &&
|
||||
event.target === containerRef.current &&
|
||||
event.target === active
|
||||
) {
|
||||
console.log("MENU: ", menuContainerRef);
|
||||
const firstChild = menuContainerRef.current.children[0];
|
||||
console.log("FIRST: ", firstChild);
|
||||
firstChild.focus();
|
||||
}
|
||||
};
|
||||
|
||||
const open = isSubMenuOpen && parentMenuOpen;
|
||||
const menuItemClasses = useMenuItemStyles({ open });
|
||||
|
||||
// Root element must have a `tabIndex` attribute for keyboard navigation
|
||||
let tabIndex;
|
||||
if (!props.disabled) {
|
||||
tabIndex = tabIndexProp !== undefined ? tabIndexProp : -1;
|
||||
}
|
||||
|
||||
console.log("PAST 2! ", tabIndex);
|
||||
|
||||
return (
|
||||
<div
|
||||
{...ContainerProps}
|
||||
ref={containerRef}
|
||||
onFocus={handleFocus}
|
||||
tabIndex={tabIndex}
|
||||
onMouseEnter={handleMouseEnter}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
onKeyDown={handleKeyDown}
|
||||
>
|
||||
<MenuItem
|
||||
{...MenuItemProps}
|
||||
className={clsx(menuItemClasses.root, className)}
|
||||
ref={menuItemRef}
|
||||
>
|
||||
{label}
|
||||
{rightIcon}
|
||||
</MenuItem>
|
||||
<Menu
|
||||
// Set pointer events to 'none' to prevent the invisible Popover div
|
||||
// from capturing events for clicks and hovers
|
||||
style={{ pointerEvents: "none" }}
|
||||
anchorEl={menuItemRef.current}
|
||||
anchorOrigin={{
|
||||
vertical: "top",
|
||||
horizontal: "right",
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: "top",
|
||||
horizontal: "left",
|
||||
}}
|
||||
open={open}
|
||||
autoFocus={false}
|
||||
disableAutoFocus
|
||||
disableEnforceFocus
|
||||
onClose={() => {
|
||||
setIsSubMenuOpen(false);
|
||||
}}
|
||||
>
|
||||
<div ref={menuContainerRef} style={{ pointerEvents: "auto" }}>
|
||||
{children}
|
||||
</div>
|
||||
</Menu>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default NestedMenuItem;
|
||||
@@ -1,202 +0,0 @@
|
||||
import React, {useState, useRef, useImperativeHandle} from 'react'
|
||||
import {makeStyles} from '@material-ui/core/styles'
|
||||
import Menu, {MenuProps} from '@material-ui/core/Menu'
|
||||
import MenuItem, {MenuItemProps} from '@material-ui/core/MenuItem'
|
||||
import ArrowRight from '@material-ui/icons/ArrowRight'
|
||||
import clsx from 'clsx'
|
||||
|
||||
export interface NestedMenuItemProps extends Omit<MenuItemProps, 'button'> {
|
||||
/**
|
||||
* Open state of parent `<Menu />`, used to close decendent menus when the
|
||||
* root menu is closed.
|
||||
*/
|
||||
parentMenuOpen: boolean
|
||||
/**
|
||||
* Component for the container element.
|
||||
* @default 'div'
|
||||
*/
|
||||
component?: React.ElementType
|
||||
/**
|
||||
* Effectively becomes the `children` prop passed to the `<MenuItem/>`
|
||||
* element.
|
||||
*/
|
||||
label?: React.ReactNode
|
||||
/**
|
||||
* @default <ArrowRight />
|
||||
*/
|
||||
rightIcon?: React.ReactNode
|
||||
/**
|
||||
* Props passed to container element.
|
||||
*/
|
||||
ContainerProps?: React.HTMLAttributes<HTMLElement> &
|
||||
React.RefAttributes<HTMLElement | null>
|
||||
/**
|
||||
* Props passed to sub `<Menu/>` element
|
||||
*/
|
||||
MenuProps?: Omit<MenuProps, 'children'>
|
||||
/**
|
||||
* @see https://material-ui.com/api/list-item/
|
||||
*/
|
||||
button?: true | undefined
|
||||
}
|
||||
|
||||
const TRANSPARENT = 'rgba(0,0,0,0)'
|
||||
const useMenuItemStyles = makeStyles((theme) => ({
|
||||
root: (props: any) => ({
|
||||
backgroundColor: props.open ? theme.palette.action.hover : TRANSPARENT
|
||||
})
|
||||
}))
|
||||
|
||||
/**
|
||||
* Use as a drop-in replacement for `<MenuItem>` when you need to add cascading
|
||||
* menu elements as children to this component.
|
||||
*/
|
||||
const NestedMenuItem = React.forwardRef<
|
||||
HTMLLIElement | null,
|
||||
NestedMenuItemProps
|
||||
>(function NestedMenuItem(props, ref) {
|
||||
const {
|
||||
parentMenuOpen,
|
||||
component = 'div',
|
||||
label,
|
||||
rightIcon = <ArrowRight />,
|
||||
children,
|
||||
className,
|
||||
tabIndex: tabIndexProp,
|
||||
MenuProps = {},
|
||||
ContainerProps: ContainerPropsProp = {},
|
||||
...MenuItemProps
|
||||
} = props
|
||||
|
||||
const {ref: containerRefProp, ...ContainerProps} = ContainerPropsProp
|
||||
|
||||
const menuItemRef = useRef<HTMLLIElement>(null)
|
||||
useImperativeHandle(ref, () => menuItemRef.current)
|
||||
|
||||
const containerRef = useRef<HTMLDivElement>(null)
|
||||
useImperativeHandle(containerRefProp, () => containerRef.current)
|
||||
|
||||
const menuContainerRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
const [isSubMenuOpen, setIsSubMenuOpen] = useState(false)
|
||||
|
||||
const handleMouseEnter = (event: React.MouseEvent<HTMLElement>) => {
|
||||
setIsSubMenuOpen(true)
|
||||
|
||||
if (ContainerProps?.onMouseEnter) {
|
||||
ContainerProps.onMouseEnter(event)
|
||||
}
|
||||
}
|
||||
const handleMouseLeave = (event: React.MouseEvent<HTMLElement>) => {
|
||||
setIsSubMenuOpen(false)
|
||||
|
||||
if (ContainerProps?.onMouseLeave) {
|
||||
ContainerProps.onMouseLeave(event)
|
||||
}
|
||||
}
|
||||
|
||||
// Check if any immediate children are active
|
||||
const isSubmenuFocused = () => {
|
||||
const active = containerRef.current?.ownerDocument?.activeElement
|
||||
for (const child of menuContainerRef.current?.children ?? []) {
|
||||
if (child === active) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
const handleFocus = (event: React.FocusEvent<HTMLElement>) => {
|
||||
if (event.target === containerRef.current) {
|
||||
setIsSubMenuOpen(true)
|
||||
}
|
||||
|
||||
if (ContainerProps?.onFocus) {
|
||||
ContainerProps.onFocus(event)
|
||||
}
|
||||
}
|
||||
|
||||
const handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {
|
||||
if (event.key === 'Escape') {
|
||||
return
|
||||
}
|
||||
|
||||
if (isSubmenuFocused()) {
|
||||
event.stopPropagation()
|
||||
}
|
||||
|
||||
const active = containerRef.current?.ownerDocument?.activeElement
|
||||
|
||||
if (event.key === 'ArrowLeft' && isSubmenuFocused()) {
|
||||
containerRef.current?.focus()
|
||||
}
|
||||
|
||||
if (
|
||||
event.key === 'ArrowRight' &&
|
||||
event.target === containerRef.current &&
|
||||
event.target === active
|
||||
) {
|
||||
const firstChild = menuContainerRef.current?.children[0] as
|
||||
| HTMLElement
|
||||
| undefined
|
||||
firstChild?.focus()
|
||||
}
|
||||
}
|
||||
|
||||
const open = isSubMenuOpen && parentMenuOpen
|
||||
const menuItemClasses = useMenuItemStyles({open})
|
||||
|
||||
// Root element must have a `tabIndex` attribute for keyboard navigation
|
||||
let tabIndex
|
||||
if (!props.disabled) {
|
||||
tabIndex = tabIndexProp !== undefined ? tabIndexProp : -1
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
{...ContainerProps}
|
||||
ref={containerRef}
|
||||
onFocus={handleFocus}
|
||||
tabIndex={tabIndex}
|
||||
onMouseEnter={handleMouseEnter}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
onKeyDown={handleKeyDown}
|
||||
>
|
||||
<MenuItem
|
||||
{...MenuItemProps}
|
||||
className={clsx(menuItemClasses.root, className)}
|
||||
ref={menuItemRef}
|
||||
>
|
||||
{label}
|
||||
{rightIcon}
|
||||
</MenuItem>
|
||||
<Menu
|
||||
// Set pointer events to 'none' to prevent the invisible Popover div
|
||||
// from capturing events for clicks and hovers
|
||||
style={{pointerEvents: 'none'}}
|
||||
anchorEl={menuItemRef.current}
|
||||
anchorOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'right'
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'left'
|
||||
}}
|
||||
open={open}
|
||||
autoFocus={false}
|
||||
disableAutoFocus
|
||||
disableEnforceFocus
|
||||
onClose={() => {
|
||||
setIsSubMenuOpen(false)
|
||||
}}
|
||||
>
|
||||
<div ref={menuContainerRef} style={{pointerEvents: 'auto'}}>
|
||||
{children}
|
||||
</div>
|
||||
</Menu>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
export default NestedMenuItem
|
||||
@@ -1,101 +1,106 @@
|
||||
import React, {useState} from 'react';
|
||||
import theme from '../theme.jsx';
|
||||
import {isMobile} from "react-device-detect";
|
||||
import ReactGA from 'react-ga4';
|
||||
|
||||
import {TextField, Typography, Button} from '@material-ui/core';
|
||||
|
||||
const Newsletter = (props) => {
|
||||
const { globalUrl, } = props;
|
||||
|
||||
const [email, setEmail] = useState("");
|
||||
const [msg, setMsg] = useState("");
|
||||
const [buttonActive, setButtonActive] = useState(true);
|
||||
const buttonStyle = {minWidth: 300, borderRadius: 30, height: 60, width: 140, margin: isMobile ? "15px auto 15px auto" : "20px 20px 20px 10px", fontSize: 18,}
|
||||
|
||||
const newsletterSignup = (inemail) => {
|
||||
if (inemail.length < 4) {
|
||||
setMsg("Invalid email")
|
||||
setButtonActive(true)
|
||||
return
|
||||
}
|
||||
|
||||
setButtonActive(false)
|
||||
const data = {"email": inemail}
|
||||
const url = globalUrl+'/api/v1/functions/newsletter_signup'
|
||||
fetch(url, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(data),
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=utf-8',
|
||||
},
|
||||
})
|
||||
.then(response =>
|
||||
response.json().then(responseJson => {
|
||||
setButtonActive(true)
|
||||
setMsg(responseJson["reason"])
|
||||
if (responseJson["success"] === false) {
|
||||
} else {
|
||||
setEmail("")
|
||||
}
|
||||
}),
|
||||
)
|
||||
.catch(error => {
|
||||
setMsg("Something went wrong: ", error.toString())
|
||||
setButtonActive(true)
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{margin: "auto", color: "white", textAlign: "center",}}>
|
||||
<Typography variant="h4" style={{marginTop: 35,}}>
|
||||
Security Automation Newsletter
|
||||
</Typography>
|
||||
<Typography variant="h6" style={{color: "#7d7f82", marginTop: 20, }}>
|
||||
Defensive security is 99% noise. Join us to sift through it.
|
||||
</Typography>
|
||||
<div style={{}}>
|
||||
<TextField
|
||||
style={{minWidth: isMobile ? "90%" : 450, backgroundColor: theme.palette.inputColor, marginTop: 20, borderRadius: 10, }}
|
||||
InputProps={{
|
||||
style:{
|
||||
borderRadius: 10,
|
||||
height: 60,
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
color="primary"
|
||||
value={email}
|
||||
onChange={(e) => {
|
||||
setEmail(e.target.value)
|
||||
}}
|
||||
placeholder="Your email"
|
||||
id="standard-required"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
style={buttonStyle}
|
||||
disabled={!buttonActive}
|
||||
onClick={() => {
|
||||
newsletterSignup(email)
|
||||
ReactGA.event({
|
||||
category: "newsletter",
|
||||
action: `signup_click`,
|
||||
label: "",
|
||||
})
|
||||
}}
|
||||
>
|
||||
Sign up
|
||||
</Button>
|
||||
<div/>
|
||||
{msg}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
export default Newsletter;
|
||||
import React, {useState} from 'react';
|
||||
import { useTheme } from '@mui/styles';
|
||||
import {isMobile} from "react-device-detect";
|
||||
import ReactGA from 'react-ga4';
|
||||
|
||||
import {
|
||||
TextField,
|
||||
Typography,
|
||||
Button
|
||||
} from '@mui/material';
|
||||
|
||||
const Newsletter = (props) => {
|
||||
const { globalUrl, } = props;
|
||||
|
||||
const theme = useTheme();
|
||||
const [email, setEmail] = useState("");
|
||||
const [msg, setMsg] = useState("");
|
||||
const [buttonActive, setButtonActive] = useState(true);
|
||||
const buttonStyle = {minWidth: 300, borderRadius: 30, height: 60, width: 140, margin: isMobile ? "15px auto 15px auto" : "20px 20px 20px 10px", fontSize: 18,}
|
||||
|
||||
const newsletterSignup = (inemail) => {
|
||||
if (inemail.length < 4) {
|
||||
setMsg("Invalid email")
|
||||
setButtonActive(true)
|
||||
return
|
||||
}
|
||||
|
||||
setButtonActive(false)
|
||||
const data = {"email": inemail}
|
||||
const url = globalUrl+'/api/v1/functions/newsletter_signup'
|
||||
fetch(url, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(data),
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=utf-8',
|
||||
},
|
||||
})
|
||||
.then(response =>
|
||||
response.json().then(responseJson => {
|
||||
setButtonActive(true)
|
||||
setMsg(responseJson["reason"])
|
||||
if (responseJson["success"] === false) {
|
||||
} else {
|
||||
setEmail("")
|
||||
}
|
||||
}),
|
||||
)
|
||||
.catch(error => {
|
||||
setMsg("Something went wrong: ", error.toString())
|
||||
setButtonActive(true)
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{margin: "auto", color: "white", textAlign: "center",}}>
|
||||
<Typography variant="h4" style={{marginTop: 35,}}>
|
||||
Security Automation Newsletter
|
||||
</Typography>
|
||||
<Typography variant="h6" style={{color: "#7d7f82", marginTop: 20, }}>
|
||||
Defensive security is 99% noise. Join us to sift through it.
|
||||
</Typography>
|
||||
<div style={{}}>
|
||||
<TextField
|
||||
style={{minWidth: isMobile ? "90%" : 450, backgroundColor: theme.palette.inputColor, marginTop: 20, borderRadius: 10, }}
|
||||
InputProps={{
|
||||
style:{
|
||||
borderRadius: 10,
|
||||
height: 60,
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
color="primary"
|
||||
value={email}
|
||||
onChange={(e) => {
|
||||
setEmail(e.target.value)
|
||||
}}
|
||||
placeholder="Your email"
|
||||
id="standard-required"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
style={buttonStyle}
|
||||
disabled={!buttonActive}
|
||||
onClick={() => {
|
||||
newsletterSignup(email)
|
||||
ReactGA.event({
|
||||
category: "newsletter",
|
||||
action: `signup_click`,
|
||||
label: "",
|
||||
})
|
||||
}}
|
||||
>
|
||||
Sign up
|
||||
</Button>
|
||||
<div/>
|
||||
{msg}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
export default Newsletter;
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import React, { useRef, useState, useEffect, useLayoutEffect } from "react";
|
||||
import { toast } from 'react-toastify';
|
||||
import { useParams, useNavigate, Link } from "react-router-dom";
|
||||
import theme from '../theme.jsx';
|
||||
import { useAlert } from "react-alert";
|
||||
//import { useAlert
|
||||
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import {
|
||||
@@ -36,8 +37,9 @@ import {
|
||||
Breadcrumbs,
|
||||
CircularProgress,
|
||||
Switch,
|
||||
Collapse,
|
||||
Fade,
|
||||
} from "@mui/material";
|
||||
|
||||
import {
|
||||
LockOpen as LockOpenIcon,
|
||||
SupervisorAccount as SupervisorAccountIcon,
|
||||
@@ -70,6 +72,7 @@ const registeredApps = [
|
||||
"todoist",
|
||||
"microsoft_sentinel",
|
||||
"microsoft_365_defender",
|
||||
"google_chat",
|
||||
"google_sheets",
|
||||
"google_drive",
|
||||
"google_disk",
|
||||
@@ -97,7 +100,7 @@ const AuthenticationOauth2 = (props) => {
|
||||
} = props;
|
||||
|
||||
let navigate = useNavigate();
|
||||
const alert = useAlert()
|
||||
//const alert = useAlert()
|
||||
|
||||
//const [update, setUpdate] = React.useState("|")
|
||||
const [defaultConfigSet, setDefaultConfigSet] = React.useState(
|
||||
@@ -260,6 +263,16 @@ const AuthenticationOauth2 = (props) => {
|
||||
admin_consent,
|
||||
"consent",
|
||||
)
|
||||
} else if (selectedApp.name.toLowerCase().includes("google_chat") || selectedApp.name.toLowerCase().includes("google_hangout")) {
|
||||
handleOauth2Request(
|
||||
"253565968129-6pij4g6ojim4gpum0h9m9u3bc357qsq7.apps.googleusercontent.com",
|
||||
"",
|
||||
"https://www.googleapis.com",
|
||||
["https://www.googleapis.com/auth/chat.messages",],
|
||||
admin_consent,
|
||||
"consent",
|
||||
)
|
||||
|
||||
} else if (selectedApp.name.toLowerCase().includes("jira_service_desk") || selectedApp.name.toLowerCase().includes("jira") || selectedApp.name.toLowerCase().includes("jira_service_management")) {
|
||||
handleOauth2Request(
|
||||
"AI02egeCQh1Zskm1QAJaaR6dzjR97V2F",
|
||||
@@ -410,7 +423,7 @@ const AuthenticationOauth2 = (props) => {
|
||||
//}
|
||||
//while(open === true)
|
||||
} catch (e) {
|
||||
alert.error(
|
||||
toast(
|
||||
"Failed authentication - probably bad credentials. Try again"
|
||||
);
|
||||
setButtonClicked(false);
|
||||
@@ -439,7 +452,7 @@ const AuthenticationOauth2 = (props) => {
|
||||
console.log("NEW AUTH: ", authenticationOption);
|
||||
if (authenticationOption.label.length === 0) {
|
||||
authenticationOption.label = `Auth for ${selectedApp.name}`;
|
||||
//alert.info("Label can't be empty")
|
||||
//toast("Label can't be empty")
|
||||
//return
|
||||
}
|
||||
|
||||
@@ -467,7 +480,7 @@ const AuthenticationOauth2 = (props) => {
|
||||
selectedApp.authentication.parameters[key].name
|
||||
] = "false";
|
||||
} else {
|
||||
alert.info(
|
||||
toast(
|
||||
"Field " + selectedApp.authentication.parameters[key].name.replace("_basic", "", -1).replace("_", " ", -1) + " can't be empty"
|
||||
|
||||
);
|
||||
|
||||
@@ -1,22 +1,21 @@
|
||||
import React, { useEffect } from "react";
|
||||
import theme from '../theme.jsx';
|
||||
import theme from "../theme.jsx";
|
||||
import { makeStyles } from "@mui/styles";
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
import {
|
||||
Tooltip,
|
||||
Grid,
|
||||
Button,
|
||||
Grid,
|
||||
Button,
|
||||
TextField,
|
||||
Typography,
|
||||
Typography,
|
||||
IconButton,
|
||||
} from "@mui/material";
|
||||
|
||||
import { useAlert } from "react-alert";
|
||||
|
||||
import {
|
||||
ExpandLess as ExpandLessIcon,
|
||||
ExpandMore as ExpandMoreIcon,
|
||||
Save as SaveIcon,
|
||||
ExpandMore as ExpandMoreIcon,
|
||||
Save as SaveIcon,
|
||||
} from "@mui/icons-material";
|
||||
|
||||
const useStyles = makeStyles({
|
||||
@@ -39,7 +38,7 @@ const OrgHeader = (props) => {
|
||||
handleEditOrg,
|
||||
} = props;
|
||||
|
||||
const alert = useAlert();
|
||||
//const alert = useAlert();
|
||||
const classes = useStyles();
|
||||
|
||||
var upload = "";
|
||||
@@ -101,10 +100,9 @@ const OrgHeader = (props) => {
|
||||
//console.log("USER: ", userdata)
|
||||
const orgSaveButton = (
|
||||
<Tooltip title="Save any unsaved data" placement="bottom">
|
||||
<div>
|
||||
<Button
|
||||
style={{ width: 150, height: 55, flex: 1 }}
|
||||
variant="contained"
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
disabled={
|
||||
userdata === undefined || userdata === null || userdata.admin !== "true"
|
||||
@@ -123,7 +121,6 @@ const OrgHeader = (props) => {
|
||||
>
|
||||
<SaveIcon />
|
||||
</Button>
|
||||
</div>
|
||||
</Tooltip>
|
||||
);
|
||||
|
||||
@@ -212,13 +209,13 @@ const OrgHeader = (props) => {
|
||||
const invalid = ["#", ":", "."];
|
||||
for (var key in invalid) {
|
||||
if (e.target.value.includes(invalid[key])) {
|
||||
alert.error("Can't use " + invalid[key] + " in name");
|
||||
toast("Can't use " + invalid[key] + " in name");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (e.target.value.length > 100) {
|
||||
alert.error("Choose a shorter name.");
|
||||
toast("Choose a shorter name.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React, { useState, useEffect, useLayoutEffect } from "react";
|
||||
import { toast } from 'react-toastify';
|
||||
import { makeStyles, createStyles } from "@mui/styles";
|
||||
import theme from '../theme.jsx';
|
||||
|
||||
@@ -6,9 +7,8 @@ import theme from '../theme.jsx';
|
||||
import { validateJson, GetIconInfo } from "../views/Workflows.jsx";
|
||||
import { GetParsedPaths } from "../views/Apps.jsx";
|
||||
import { sortByKey } from "../views/AngularWorkflow.jsx";
|
||||
//import NestedMenuItem from "material-ui-nested-menu-item-v5";
|
||||
import { NestedMenuItem } from "mui-nested-menu";
|
||||
import { useAlert } from "react-alert";
|
||||
//import { useAlert
|
||||
|
||||
import {
|
||||
ButtonGroup,
|
||||
@@ -86,13 +86,12 @@ import {
|
||||
Circle as CircleIcon,
|
||||
SquareFoot as SquareFootIcon,
|
||||
} from '@mui/icons-material';
|
||||
//} from "@material-ui/icons";
|
||||
|
||||
|
||||
//import CodeMirror from "@uiw/react-codemirror";
|
||||
//import "codemirror/keymap/sublime";
|
||||
//import "codemirror/theme/gruvbox-dark.css";
|
||||
import ShuffleCodeEditor from "../components/ShuffleCodeEditor.jsx";
|
||||
//import ShuffleCodeEditor from "../components/ShuffleCodeEditor.jsx";
|
||||
|
||||
const useStyles = makeStyles({
|
||||
notchedOutline: {
|
||||
@@ -113,7 +112,6 @@ const useStyles = makeStyles({
|
||||
},
|
||||
inputRoot: {
|
||||
color: "white",
|
||||
// This matches the specificity of the default styles at https://github.com/mui-org/material-ui/blob/v4.11.3/packages/material-ui-lab/src/Autocomplete/Autocomplete.js#L90
|
||||
"&:hover .MuiOutlinedInput-notchedOutline": {
|
||||
borderColor: "#f86a3e",
|
||||
},
|
||||
@@ -158,28 +156,30 @@ const ParsedAction = (props) => {
|
||||
authenticationType,
|
||||
appAuthentication,
|
||||
getAppAuthentication,
|
||||
actionDelayChange,
|
||||
getParents,
|
||||
isCloud,
|
||||
lastSaved,
|
||||
setLastSaved,
|
||||
setShowVideo,
|
||||
toolsAppId,
|
||||
aiSubmit,
|
||||
//expansionModalOpen,
|
||||
//setExpansionModalOpen,
|
||||
actionDelayChange,
|
||||
getParents,
|
||||
isCloud,
|
||||
lastSaved,
|
||||
setLastSaved,
|
||||
setShowVideo,
|
||||
toolsAppId,
|
||||
aiSubmit,
|
||||
|
||||
expansionModalOpen,
|
||||
setExpansionModalOpen,
|
||||
|
||||
setEditorData,
|
||||
setcodedata,
|
||||
} = props;
|
||||
|
||||
const classes = useStyles();
|
||||
const alert = useAlert()
|
||||
|
||||
const [expansionModalOpen, setExpansionModalOpen] = React.useState(false);
|
||||
//const alert = useAlert()
|
||||
|
||||
const [hideBody, setHideBody] = React.useState(true);
|
||||
const [activateHidingBodyButton, setActivateHidingBodyButton] = React.useState(false);
|
||||
|
||||
const [codedata, setcodedata] = React.useState("");
|
||||
const [fieldCount, setFieldCount] = React.useState(0);
|
||||
const [hiddenDescription, setHiddenDescription] = React.useState(true);
|
||||
const [fieldCount, setFieldCount] = React.useState(0);
|
||||
const [hiddenDescription, setHiddenDescription] = React.useState(true);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
@@ -228,9 +228,9 @@ const ParsedAction = (props) => {
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status === 200) {
|
||||
//alert.success("Successfully GOT app "+appId)
|
||||
//toast("Successfully GOT app "+appId)
|
||||
} else {
|
||||
alert.error("Failed getting app");
|
||||
toast("Failed getting app");
|
||||
}
|
||||
|
||||
return response.json();
|
||||
@@ -291,7 +291,7 @@ const ParsedAction = (props) => {
|
||||
//foundparams.push(param.name)
|
||||
}
|
||||
} else {
|
||||
alert.error("Couldn't find action " + selectedAction.name);
|
||||
toast("Couldn't find action " + selectedAction.name);
|
||||
}
|
||||
|
||||
selectedAction.errors = [];
|
||||
@@ -307,7 +307,7 @@ const ParsedAction = (props) => {
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1146,7 +1146,7 @@ const ParsedAction = (props) => {
|
||||
// setNewSelectedAction({ target: { value: newValue.name } });
|
||||
//}
|
||||
}}
|
||||
renderOption={(data) => {
|
||||
renderOption={(props, data, state) => {
|
||||
var newActionname = data.app_name;
|
||||
if (
|
||||
data.label !== undefined &&
|
||||
@@ -1518,29 +1518,6 @@ const ParsedAction = (props) => {
|
||||
|
||||
const clickedFieldId = "rightside_field_" + count;
|
||||
|
||||
const shufflecode = fieldCount !== count ? null :
|
||||
(
|
||||
<ShuffleCodeEditor
|
||||
isCloud={isCloud}
|
||||
toolsAppId={toolsAppId}
|
||||
fieldCount = {fieldCount}
|
||||
setFieldCount = {setFieldCount}
|
||||
actionlist = {actionlist}
|
||||
changeActionParameterCodeMirror = {changeActionParameterCodeMirror}
|
||||
codedata={codedata}
|
||||
setcodedata={setcodedata}
|
||||
expansionModalOpen={expansionModalOpen}
|
||||
setExpansionModalOpen={setExpansionModalOpen}
|
||||
globalUrl={globalUrl}
|
||||
|
||||
workflowExecutions={workflowExecutions}
|
||||
getParents={getParents}
|
||||
selectedAction={selectedAction}
|
||||
parameterName={data.name}
|
||||
aiSubmit={aiSubmit}
|
||||
/>
|
||||
)
|
||||
|
||||
//<TextareaAutosize
|
||||
// <CodeMirror
|
||||
//fullWidth
|
||||
@@ -1590,38 +1567,47 @@ const ParsedAction = (props) => {
|
||||
disableUnderline: true,
|
||||
endAdornment: hideExtraTypes ? null : (
|
||||
<InputAdornment position="end">
|
||||
<ButtonGroup orientation={multiline ? "vertical" : "horizontal"}>
|
||||
<Tooltip title="Expand window" placement="top">
|
||||
<AspectRatioIcon
|
||||
style={{ cursor: "pointer", margin: multiline ? 5 : 0 ,}}
|
||||
onClick={(event) => {
|
||||
event.preventDefault()
|
||||
setFieldCount(count)
|
||||
setcodedata(data.value)
|
||||
setExpansionModalOpen(true)
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
<Tooltip title="Autocomplete text" placement="top">
|
||||
<AddCircleOutlineIcon
|
||||
style={{ cursor: "pointer", margin: multiline ? 5 : 0, }}
|
||||
onClick={(event) => {
|
||||
event.preventDefault()
|
||||
<ButtonGroup orientation={multiline ? "vertical" : "horizontal"}>
|
||||
<Tooltip title="Expand window" placement="top">
|
||||
<AspectRatioIcon
|
||||
style={{ cursor: "pointer", margin: multiline ? 5 : 0 ,}}
|
||||
onClick={(event) => {
|
||||
event.preventDefault()
|
||||
setFieldCount(count)
|
||||
setExpansionModalOpen(true)
|
||||
|
||||
// Get cursor position
|
||||
// This makes it so we can put it in the right location?
|
||||
setMenuPosition({
|
||||
top: event.pageY + 10,
|
||||
left: event.pageX + 10,
|
||||
});
|
||||
setShowDropdownNumber(count);
|
||||
setShowDropdown(true);
|
||||
setShowAutocomplete(true);
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
</ButtonGroup>
|
||||
</InputAdornment>
|
||||
//setcodedata(data.value)
|
||||
|
||||
setEditorData({
|
||||
"name": data.name,
|
||||
"value": data.value,
|
||||
"field_number": count,
|
||||
"actionlist": actionlist,
|
||||
"field_id": clickedFieldId,
|
||||
})
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
<Tooltip title="Autocomplete text" placement="top">
|
||||
<AddCircleOutlineIcon
|
||||
style={{ cursor: "pointer", margin: multiline ? 5 : 0, }}
|
||||
onClick={(event) => {
|
||||
event.preventDefault()
|
||||
|
||||
// Get cursor position
|
||||
// This makes it so we can put it in the right location?
|
||||
setMenuPosition({
|
||||
top: event.pageY + 10,
|
||||
left: event.pageX + 10,
|
||||
});
|
||||
setShowDropdownNumber(count);
|
||||
setShowDropdown(true);
|
||||
setShowAutocomplete(true);
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
</ButtonGroup>
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
multiline={data.name.startsWith("${") && data.name.endsWith("}") ? true : multiline}
|
||||
@@ -1651,11 +1637,11 @@ const ParsedAction = (props) => {
|
||||
*/
|
||||
|
||||
//console.log("Clicked field: ", clickedFieldId)
|
||||
if (setScrollConfig !== undefined && scrollConfig !== null && scrollConfig !== undefined && scrollConfig.selected !== clickedFieldId) {
|
||||
scrollConfig.selected = clickedFieldId
|
||||
setScrollConfig(scrollConfig)
|
||||
//console.log("Change field id!")
|
||||
}
|
||||
if (setScrollConfig !== undefined && scrollConfig !== null && scrollConfig !== undefined && scrollConfig.selected !== clickedFieldId) {
|
||||
scrollConfig.selected = clickedFieldId
|
||||
setScrollConfig(scrollConfig)
|
||||
//console.log("Change field id!")
|
||||
}
|
||||
}}
|
||||
id={clickedFieldId}
|
||||
rows={data.name.startsWith("${") && data.name.endsWith("}") ? 2 : rows}
|
||||
@@ -1696,10 +1682,10 @@ const ParsedAction = (props) => {
|
||||
null : null
|
||||
}
|
||||
onBlur={(event) => {
|
||||
baseHelperText = calculateHelpertext(event.target.value)
|
||||
if (setLastSaved !== undefined) {
|
||||
setLastSaved(false)
|
||||
}
|
||||
baseHelperText = calculateHelpertext(event.target.value)
|
||||
if (setLastSaved !== undefined) {
|
||||
setLastSaved(false)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
);
|
||||
@@ -2003,12 +1989,11 @@ const ParsedAction = (props) => {
|
||||
|
||||
datafield = (
|
||||
<Select
|
||||
MenuProps={{
|
||||
disableScrollLock: true,
|
||||
}}
|
||||
MenuProps={{
|
||||
disableScrollLock: true,
|
||||
}}
|
||||
SelectDisplayProps={{
|
||||
style: {
|
||||
marginLeft: 10,
|
||||
},
|
||||
}}
|
||||
value={selectedActionParameters[count].value}
|
||||
@@ -2568,7 +2553,7 @@ const ParsedAction = (props) => {
|
||||
*/}
|
||||
</div>
|
||||
{datafield}
|
||||
{shufflecode}
|
||||
{/*shufflecode*/}
|
||||
{showDropdown &&
|
||||
showDropdownNumber === count &&
|
||||
data.variant === "STATIC_VALUE" &&
|
||||
@@ -2587,7 +2572,6 @@ const ParsedAction = (props) => {
|
||||
labelId="action-autocompleter"
|
||||
SelectDisplayProps={{
|
||||
style: {
|
||||
marginLeft: 10,
|
||||
},
|
||||
}}
|
||||
onClose={() => {
|
||||
@@ -2680,6 +2664,62 @@ const ParsedAction = (props) => {
|
||||
return null;
|
||||
};
|
||||
|
||||
|
||||
const ActionSelectOption = (actionprops) => {
|
||||
const { data, newActionname, newActiondescription, useIcon, extraDescription, } = actionprops;
|
||||
const [hover, setHover] = React.useState(false);
|
||||
|
||||
console.log("Extra desc: ", extraDescription)
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
color="secondary"
|
||||
title={newActiondescription}
|
||||
placement="left"
|
||||
>
|
||||
<div style={{
|
||||
cursor: "pointer",
|
||||
padding: 8,
|
||||
paddingLeft: 14,
|
||||
paddingBottom: 4,
|
||||
backgroundColor: hover ? theme.palette.surfaceColor : theme.palette.inputColor,
|
||||
}} onMouseEnter={() => setHover(true)} onMouseLeave={() => setHover(false)}
|
||||
onClick={() => {
|
||||
//setSelectedAction(actionprops)
|
||||
//setShowActionList(false)
|
||||
//setUpdate(Math.random())
|
||||
//
|
||||
if (data !== undefined && data !== null) {
|
||||
setNewSelectedAction({
|
||||
target: {
|
||||
value: data.name
|
||||
}
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div style={{ display: "flex", marginBottom: 0,}}>
|
||||
<span
|
||||
style={{
|
||||
marginRight: 10,
|
||||
marginTop: "auto",
|
||||
marginBottom: 0,
|
||||
}}
|
||||
>
|
||||
{useIcon}
|
||||
</span>
|
||||
<span style={{marginBottom: 0, marginTop: 3, }}>{newActionname}</span>
|
||||
</div>
|
||||
{extraDescription.length > 0 ?
|
||||
<Typography variant="body2" color="textSecondary" style={{marginTop: 0, overflow: "hidden", whiteSpace: "nowrap", display: "block",}}>
|
||||
{extraDescription}
|
||||
</Typography>
|
||||
: null}
|
||||
</div>
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
|
||||
//const CustomPopper = function (props) {
|
||||
// const classes = useStyles()
|
||||
// return <Popper {...props} className={classes.root} placement="bottom" />
|
||||
@@ -2906,7 +2946,6 @@ const ParsedAction = (props) => {
|
||||
}}
|
||||
SelectDisplayProps={{
|
||||
style: {
|
||||
marginLeft: 10,
|
||||
},
|
||||
}}
|
||||
>
|
||||
@@ -2941,6 +2980,7 @@ const ParsedAction = (props) => {
|
||||
<div style={{flex: 5}}>
|
||||
<Typography style={{color: "rgba(255,255,255,0.7)"}}>Name</Typography>
|
||||
<TextField
|
||||
|
||||
style={theme.palette.textFieldStyle}
|
||||
InputProps={{
|
||||
style: theme.palette.innerTextfieldStyle,
|
||||
@@ -3227,8 +3267,7 @@ const ParsedAction = (props) => {
|
||||
}
|
||||
SelectDisplayProps={{
|
||||
style: {
|
||||
marginLeft: 10,
|
||||
maxWidth: 250,
|
||||
maxWidth: 250,
|
||||
},
|
||||
}}
|
||||
fullWidth
|
||||
@@ -3329,7 +3368,6 @@ const ParsedAction = (props) => {
|
||||
}
|
||||
SelectDisplayProps={{
|
||||
style: {
|
||||
marginLeft: 10,
|
||||
},
|
||||
}}
|
||||
fullWidth
|
||||
@@ -3375,9 +3413,9 @@ const ParsedAction = (props) => {
|
||||
<div style={{ marginTop: "20px" }}>
|
||||
<Typography>Execution variable (optional)</Typography>
|
||||
<Select
|
||||
MenuProps={{
|
||||
disableScrollLock: true,
|
||||
}}
|
||||
MenuProps={{
|
||||
disableScrollLock: true,
|
||||
}}
|
||||
value={
|
||||
selectedAction.execution_variable !== undefined
|
||||
&& selectedAction.execution_variable !== null
|
||||
@@ -3389,7 +3427,6 @@ const ParsedAction = (props) => {
|
||||
}
|
||||
SelectDisplayProps={{
|
||||
style: {
|
||||
marginLeft: 10,
|
||||
},
|
||||
}}
|
||||
fullWidth
|
||||
@@ -3458,20 +3495,20 @@ const ParsedAction = (props) => {
|
||||
autoHighlight
|
||||
value={selectedAction}
|
||||
classes={{ inputRoot: classes.inputRoot }}
|
||||
groupBy={(option) => {
|
||||
// Most popular
|
||||
// Is categorized
|
||||
// Uncategorized
|
||||
return option.category_label !== undefined && option.category_label !== null && option.category_label.length > 0 ? "Most used" : "All Actions";
|
||||
}}
|
||||
renderGroup={(params) => {
|
||||
return (
|
||||
<li key={params.key}>
|
||||
<Typography variant="body1" style={{textAlign: "center", marginLeft: 10, marginTop: 25, marginBottom: 10, }}>{params.group}</Typography>
|
||||
<Typography variant="body2">{params.children}</Typography>
|
||||
</li>
|
||||
)
|
||||
}}
|
||||
groupBy={(option) => {
|
||||
// Most popular
|
||||
// Is categorized
|
||||
// Uncategorized
|
||||
return option.category_label !== undefined && option.category_label !== null && option.category_label.length > 0 ? "Most used" : "All Actions";
|
||||
}}
|
||||
renderGroup={(params) => {
|
||||
return (
|
||||
<li key={params.key}>
|
||||
<Typography variant="body1" style={{textAlign: "center", marginLeft: 10, marginTop: 25, marginBottom: 10, }}>{params.group}</Typography>
|
||||
<Typography variant="body2">{params.children}</Typography>
|
||||
</li>
|
||||
)
|
||||
}}
|
||||
options={selectedApp.actions === undefined || selectedApp.actions === null ? [] : selectedApp.actions.filter((a) => a.category_label !== undefined && a.category_label !== null && a.category_label.length > 0).concat(sortByKey(selectedApp.actions, "label"))}
|
||||
ListboxProps={{
|
||||
style: {
|
||||
@@ -3491,8 +3528,6 @@ const ParsedAction = (props) => {
|
||||
return null;
|
||||
}
|
||||
|
||||
console.log("OPTION: ", option)
|
||||
|
||||
const newname = (
|
||||
option.name.charAt(0).toUpperCase() + option.name.substring(1)
|
||||
).replaceAll("_", " ");
|
||||
@@ -3509,10 +3544,10 @@ const ParsedAction = (props) => {
|
||||
// Workaround with event lol
|
||||
if (newValue !== undefined && newValue !== null) {
|
||||
setNewSelectedAction({
|
||||
target: {
|
||||
value: newValue.name
|
||||
}
|
||||
});
|
||||
target: {
|
||||
value: newValue.name
|
||||
}
|
||||
});
|
||||
}
|
||||
}}
|
||||
renderOption={(props, data, state) => {
|
||||
@@ -3560,100 +3595,87 @@ const ParsedAction = (props) => {
|
||||
method = "CONNECT"
|
||||
}
|
||||
|
||||
// FIXME: Should it require a base URL?
|
||||
if (method.length > 0 && data.description !== undefined && data.description !== null && data.description.includes("http")) {
|
||||
var extraUrl = ""
|
||||
const descSplit = data.description.split("\n")
|
||||
// Last line of descSplit
|
||||
if (descSplit.length > 0) {
|
||||
extraUrl = descSplit[descSplit.length-1]
|
||||
}
|
||||
// FIXME: Should it require a base URL?
|
||||
if (method.length > 0 && data.description !== undefined && data.description !== null && data.description.includes("http")) {
|
||||
var extraUrl = ""
|
||||
const descSplit = data.description.split("\n")
|
||||
// Last line of descSplit
|
||||
if (descSplit.length > 0) {
|
||||
extraUrl = descSplit[descSplit.length-1]
|
||||
}
|
||||
|
||||
//for (let [line,lineval] in Object.entries(descSplit)) {
|
||||
// if (descSplit[line].includes("http") && descSplit[line].includes("://")) {
|
||||
// const urlsplit = descSplit[line].split("/")
|
||||
// try {
|
||||
// extraUrl = "/"+urlsplit.slice(3, urlsplit.length).join("/")
|
||||
// } catch (e) {
|
||||
// //console.log("Failed - running with -1")
|
||||
// extraUrl = "/"+urlsplit.slice(3, urlsplit.length-1).join("/")
|
||||
// }
|
||||
//for (let [line,lineval] in Object.entries(descSplit)) {
|
||||
// if (descSplit[line].includes("http") && descSplit[line].includes("://")) {
|
||||
// const urlsplit = descSplit[line].split("/")
|
||||
// try {
|
||||
// extraUrl = "/"+urlsplit.slice(3, urlsplit.length).join("/")
|
||||
// } catch (e) {
|
||||
// //console.log("Failed - running with -1")
|
||||
// extraUrl = "/"+urlsplit.slice(3, urlsplit.length-1).join("/")
|
||||
// }
|
||||
|
||||
|
||||
// //console.log("NO BASEURL TOO!! Why missing last one in certain scenarios (sevco)?", extraUrl, urlsplit, descSplit[line])
|
||||
// //break
|
||||
// }
|
||||
//}
|
||||
// //console.log("NO BASEURL TOO!! Why missing last one in certain scenarios (sevco)?", extraUrl, urlsplit, descSplit[line])
|
||||
// //break
|
||||
// }
|
||||
//}
|
||||
|
||||
if (extraUrl.length > 0) {
|
||||
if (extraUrl.includes(" ")) {
|
||||
extraUrl = extraUrl.split(" ")[0]
|
||||
}
|
||||
if (extraUrl.length > 0) {
|
||||
if (extraUrl.includes(" ")) {
|
||||
extraUrl = extraUrl.split(" ")[0]
|
||||
}
|
||||
|
||||
if (extraUrl.includes("#")) {
|
||||
extraUrl = extraUrl.split("#")[0]
|
||||
}
|
||||
if (extraUrl.includes("#")) {
|
||||
extraUrl = extraUrl.split("#")[0]
|
||||
}
|
||||
|
||||
extraDescription = `${method} ${extraUrl}`
|
||||
} else {
|
||||
//console.log("No url found. Check again :)")
|
||||
}
|
||||
}
|
||||
extraDescription = `${method} ${extraUrl}`
|
||||
} else {
|
||||
//console.log("No url found. Check again :)")
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
color="secondary"
|
||||
title={newActiondescription}
|
||||
placement="left"
|
||||
>
|
||||
<div>
|
||||
<div style={{ display: "flex", marginBottom: 0,}}>
|
||||
<span
|
||||
style={{
|
||||
marginRight: 10,
|
||||
marginTop: "auto",
|
||||
marginBottom: 0,
|
||||
}}
|
||||
>
|
||||
{useIcon}
|
||||
</span>
|
||||
<span style={{marginBottom: 0, marginTop: 3, }}>{newActionname}</span>
|
||||
</div>
|
||||
{extraDescription.length > 0 ?
|
||||
<Typography variant="body2" color="textSecondary" style={{marginTop: 0, overflow: "hidden", whiteSpace: "nowrap", display: "block",}}>
|
||||
{extraDescription}
|
||||
</Typography>
|
||||
: null}
|
||||
</div>
|
||||
</Tooltip>
|
||||
<ActionSelectOption
|
||||
data={data}
|
||||
newActiondescription={newActiondescription}
|
||||
useIcon={useIcon}
|
||||
newActionname={newActionname}
|
||||
extraDescription={extraDescription}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
renderInput={(params) => {
|
||||
if (params.inputProps !== undefined && params.inputProps !== null && params.inputProps.value !== undefined && params.inputProps.value !== null) {
|
||||
const prefixes = ["Post", "Put", "Patch"]
|
||||
for (let [key,keyval] in Object.entries(prefixes)) {
|
||||
if (params.inputProps.value.startsWith(prefixes[key])) {
|
||||
params.inputProps.value = params.inputProps.value.replace(prefixes[key]+" ", "", -1)
|
||||
if (params.inputProps.value.length > 1) {
|
||||
params.inputProps.value = params.inputProps.value.charAt(0).toUpperCase()+params.inputProps.value.substring(1)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
if (params.inputProps !== undefined && params.inputProps !== null && params.inputProps.value !== undefined && params.inputProps.value !== null) {
|
||||
const prefixes = ["Post", "Put", "Patch"]
|
||||
for (let [key,keyval] in Object.entries(prefixes)) {
|
||||
if (params.inputProps.value.startsWith(prefixes[key])) {
|
||||
params.inputProps.value = params.inputProps.value.replace(prefixes[key]+" ", "", -1)
|
||||
if (params.inputProps.value.length > 1) {
|
||||
params.inputProps.value = params.inputProps.value.charAt(0).toUpperCase()+params.inputProps.value.substring(1)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<TextField
|
||||
color="primary"
|
||||
variant="body1"
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
}}
|
||||
{...params}
|
||||
label="Find Actions"
|
||||
variant="outlined"
|
||||
/>
|
||||
<TextField
|
||||
data-lpignore="true"
|
||||
autocomplete="off"
|
||||
dataLPIgnore="true"
|
||||
|
||||
color="primary"
|
||||
id="checkbox-search"
|
||||
variant="body1"
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
}}
|
||||
{...params}
|
||||
label="Find Actions"
|
||||
variant="outlined"
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -12,10 +12,10 @@ import {
|
||||
} from "@mui/material";
|
||||
|
||||
import Priority from "../components/Priority.jsx";
|
||||
import { useAlert } from "react-alert";
|
||||
//import { useAlert
|
||||
|
||||
const Priorities = (props) => {
|
||||
const { globalUrl, userdata, serverside, billingInfo, stripeKey, checkLogin, } = props;
|
||||
const { globalUrl, userdata, serverside, billingInfo, stripeKey, checkLogin, setAdminTab, setCurTab, } = props;
|
||||
const [showDismissed, setShowDismissed] = React.useState(false);
|
||||
const [showRead, setShowRead] = React.useState(false);
|
||||
|
||||
@@ -60,6 +60,8 @@ const Priorities = (props) => {
|
||||
globalUrl={globalUrl}
|
||||
priority={priority}
|
||||
checkLogin={checkLogin}
|
||||
setAdminTab={setAdminTab}
|
||||
setCurTab={setCurTab}
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
import ReactGA from 'react-ga4';
|
||||
import theme from "../theme.jsx";
|
||||
import { useNavigate, Link } from "react-router-dom";
|
||||
import {
|
||||
@@ -16,12 +18,13 @@ import {
|
||||
AutoFixHigh as AutoFixHighIcon,
|
||||
ArrowForward as ArrowForwardIcon,
|
||||
} from '@mui/icons-material';
|
||||
import { useAlert } from "react-alert";
|
||||
//import { useAlert
|
||||
|
||||
const Priority = (props) => {
|
||||
const { globalUrl, userdata, serverside, priority, checkLogin, } = props;
|
||||
const { globalUrl, userdata, serverside, priority, checkLogin, setAdminTab, setCurTab, } = props;
|
||||
|
||||
|
||||
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
|
||||
let navigate = useNavigate();
|
||||
const changeRecommendation = (recommendation, action) => {
|
||||
const data = {
|
||||
@@ -54,20 +57,20 @@ const Priority = (props) => {
|
||||
}
|
||||
} else {
|
||||
if (responseJson.success === false && responseJson.reason !== undefined) {
|
||||
alert.error("Failed change recommendation: ", responseJson.reason)
|
||||
toast("Failed change recommendation: ", responseJson.reason)
|
||||
} else {
|
||||
alert.error("Failed change recommendation");
|
||||
toast("Failed change recommendation");
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.info("Failed dismissing alert. Please contact support@shuffler.io if this persists.");
|
||||
toast("Failed dismissing alert. Please contact support@shuffler.io if this persists.");
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<div style={{border: priority.active === false ? "1px solid #000000" : priority.severity === 1 ? "1px solid #f85a3e" : "1px solid rgba(255,255,255,0.3)", borderRadius: theme.palette.borderRadius, marginTop: 10, marginBottom: 10, padding: 15, textAlign: "center", height: 70, textAlign: "left", backgroundColor: theme.palette.surfaceColor, display: "flex", }}>
|
||||
<div style={{border: priority.active === false ? "1px solid #000000" : priority.severity === 1 ? "1px solid #f85a3e" : "1px solid rgba(255,255,255,0.3)", borderRadius: theme.palette.borderRadius, marginTop: 10, marginBottom: 10, padding: 15, textAlign: "center", height: 95, textAlign: "left", backgroundColor: theme.palette.surfaceColor, display: "flex", }}>
|
||||
<div style={{flex: 2, overflow: "hidden",}}>
|
||||
<span style={{display: "flex", }}>
|
||||
{priority.type === "usecase" || priority.type == "apps" ? <AutoFixHighIcon style={{height: 19, width: 19, marginLeft: 3, marginRight: 10, }}/> : null}
|
||||
@@ -100,17 +103,30 @@ const Priority = (props) => {
|
||||
}
|
||||
</div>
|
||||
<div style={{flex: 1, display: "flex", marginLeft: 30, }}>
|
||||
<Button style={{height: 50, borderRadius: 25, marginTop: 8, width: 175, marginRight: 10, color: priority.active === false ? "white" : "black", backgroundColor: priority.active === false ? theme.palette.inputColor : "white", }} variant="contained" color="secondary" onClick={() => {
|
||||
/*
|
||||
ReactGA.event({
|
||||
category: "",
|
||||
action: `partner_${partner.name}_click`,
|
||||
label: "",
|
||||
})
|
||||
*/
|
||||
<Button style={{height: 50, borderRadius: 25, marginTop: 8, width: 175, marginRight: 10, color: priority.active === false ? "white" : "black", backgroundColor: priority.active === false ? theme.palette.inputColor : "rgba(255,255,255,0.8)", }} variant="contained" color="secondary" onClick={() => {
|
||||
|
||||
if (isCloud) {
|
||||
ReactGA.event({
|
||||
category: "recommendation",
|
||||
action: `click_${priority.name}`,
|
||||
label: "",
|
||||
})
|
||||
}
|
||||
|
||||
navigate(priority.url)
|
||||
|
||||
if (setAdminTab !== undefined && setCurTab !== undefined) {
|
||||
if (priority.description.toLowerCase().includes("notification workflow")) {
|
||||
setCurTab(0)
|
||||
setAdminTab(0)
|
||||
}
|
||||
|
||||
if (priority.description.toLowerCase().includes("hybrid shuffle")) {
|
||||
setCurTab(6)
|
||||
}
|
||||
}
|
||||
}}>
|
||||
explore
|
||||
Explore
|
||||
</Button>
|
||||
{priority.active === true ?
|
||||
<Button style={{borderRadius: 25, width: 100, height: 50, marginTop: 8, }} variant="text" color="secondary" onClick={() => {
|
||||
|
||||
@@ -1,177 +0,0 @@
|
||||
import React, { useState } from "react";
|
||||
|
||||
import DialogTitle from "@material-ui/core/DialogTitle";
|
||||
import Dialog from "@material-ui/core/Dialog";
|
||||
import TextField from "@material-ui/core/TextField";
|
||||
import Button from "@material-ui/core/Button";
|
||||
import Divider from "@material-ui/core/Divider";
|
||||
|
||||
const SettingsDialog = (props) => {
|
||||
const {
|
||||
classes,
|
||||
onClose,
|
||||
settingsOpen,
|
||||
settingsData,
|
||||
globalUrl,
|
||||
isLoggedIn,
|
||||
setIsLoggedIn,
|
||||
...other
|
||||
} = props;
|
||||
|
||||
const [password1, setPassword1] = useState("");
|
||||
const [password2, setPassword2] = useState("");
|
||||
const [password3, setPassword3] = useState("");
|
||||
|
||||
const handleValidateForm = () => {
|
||||
var passlength = 10;
|
||||
if (
|
||||
password1 === password2 &&
|
||||
password1.length >= passlength &&
|
||||
password3.length >= passlength
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
const onChangePass1 = (e) => {
|
||||
setPassword1(e.target.value);
|
||||
};
|
||||
|
||||
const onChangePass2 = (e) => {
|
||||
setPassword2(e.target.value);
|
||||
};
|
||||
|
||||
const onChangePass3 = (e) => {
|
||||
setPassword3(e.target.value);
|
||||
};
|
||||
|
||||
const onSubmitPassReset = () => {
|
||||
console.log("Should change password");
|
||||
// Rofl, this can't possibly be typesafe
|
||||
var data =
|
||||
'{"password1": "' +
|
||||
password1 +
|
||||
'", "password2": "' +
|
||||
password2 +
|
||||
'", "password3": "' +
|
||||
password3 +
|
||||
'"}';
|
||||
|
||||
fetch(globalUrl + "/passwordreset", {
|
||||
body: data,
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then((responseJson) => {
|
||||
console.log(responseJson);
|
||||
if (responseJson.status === true) {
|
||||
console.log("SUCCESS");
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
});
|
||||
};
|
||||
|
||||
//PaperProps={{style: {minWidth: "500px"}}
|
||||
return (
|
||||
<Dialog open={settingsOpen} onClose={() => onClose()} {...other}>
|
||||
<DialogTitle>Settings</DialogTitle>
|
||||
<Divider />
|
||||
<div style={{ marginLeft: "15px", marginRight: "15px" }}>
|
||||
<h3>Username</h3>
|
||||
{settingsData.username}
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
marginLeft: "15px",
|
||||
marginRight: "15px",
|
||||
marginBottom: "15px",
|
||||
}}
|
||||
>
|
||||
<h3>ApiKey</h3>
|
||||
<TextField
|
||||
id="outlined-read-only-input"
|
||||
defaultValue={settingsData.apikey}
|
||||
value={settingsData.apikey}
|
||||
style={{ width: 320 }}
|
||||
InputProps={{
|
||||
readOnly: true,
|
||||
}}
|
||||
variant="outlined"
|
||||
/>
|
||||
</div>
|
||||
<Divider />
|
||||
<form style={{ margin: "15px 15px 15px 15px" }}>
|
||||
<h3>Change password</h3>
|
||||
<div>
|
||||
<TextField
|
||||
id="standard-password-input"
|
||||
label="Current password"
|
||||
type="password"
|
||||
name="password"
|
||||
style={{ width: 320 }}
|
||||
placeholder="********************************"
|
||||
autoComplete="current-password"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
onChange={onChangePass1}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<TextField
|
||||
label="Confirm current password"
|
||||
type="password"
|
||||
placeholder="********************************"
|
||||
name="password"
|
||||
style={{ width: 320 }}
|
||||
autoComplete="current-password"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
onChange={onChangePass2}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<TextField
|
||||
label="New password"
|
||||
type="password"
|
||||
name="password"
|
||||
placeholder="********************************"
|
||||
style={{ width: 320 }}
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
onChange={onChangePass3}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ display: "flex", marginTop: "10px" }}>
|
||||
<Button
|
||||
color="secondary"
|
||||
variant="contained"
|
||||
onClick={onSubmitPassReset}
|
||||
type="button"
|
||||
style={{ flex: "1", marginRight: "5px" }}
|
||||
disabled={!handleValidateForm()}
|
||||
>
|
||||
SUBMIT
|
||||
</Button>
|
||||
<Button
|
||||
color="primary"
|
||||
variant="contained"
|
||||
type="button"
|
||||
style={{ flex: "1" }}
|
||||
onClick={onClose}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default SettingsDialog;
|
||||
@@ -1,4 +1,5 @@
|
||||
import React, {useState, useEffect, useLayoutEffect} from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import {
|
||||
CircularProgress,
|
||||
IconButton,
|
||||
@@ -54,9 +55,10 @@ import {indentWithTab} from "@codemirror/commands"
|
||||
import { padding, textAlign } from '@mui/system';
|
||||
import data from '../frameworkStyle.jsx';
|
||||
import { useNavigate, Link, useParams } from "react-router-dom";
|
||||
import { tags as t } from '@lezer/highlight';
|
||||
import { createTheme } from '@uiw/codemirror-themes';
|
||||
|
||||
import { tags } from '@lezer/highlight';
|
||||
|
||||
|
||||
const liquidFilters = [
|
||||
{"name": "Size", "value": "size", "example": ""},
|
||||
@@ -81,44 +83,39 @@ const pythonFilters = [
|
||||
{"name": "Handle JSON", "value": `{% python %}\nimport json\njsondata = json.loads(r"""$nodename""")\n{% endpython %}`, "example": ``},
|
||||
]
|
||||
|
||||
//const shuffleTheme = createTheme({
|
||||
// theme: 'dark',
|
||||
// settings: {
|
||||
// background: '#282828',
|
||||
// foreground: '#282828',
|
||||
// caret: '#5d00ff',
|
||||
// selection: '#036dd626',
|
||||
// selectionMatch: '#036dd626',
|
||||
// lineHighlight: '#8a91991a',
|
||||
// gutterBackground: '#282828',
|
||||
// gutterForeground: '#8a919966',
|
||||
// fontSize: 18,
|
||||
// borderRadius: theme.palette.borderRadius,
|
||||
// border: `2px solid ${theme.palette.inputColor}`,
|
||||
// },
|
||||
// styles: [
|
||||
// { tag: tags.comment, color: '#787b8099' },
|
||||
// { tag: tags.variableName, color: '#0080ff' },
|
||||
// { tag: [tags.string, tags.special(tags.brace)], color: '#5c6166' },
|
||||
// { tag: tags.number, color: '#5c6166' },
|
||||
// { tag: tags.bool, color: '#5c6166' },
|
||||
// { tag: tags.null, color: '#5c6166' },
|
||||
// { tag: tags.keyword, color: '#5c6166' },
|
||||
// { tag: tags.operator, color: '#5c6166' },
|
||||
// { tag: tags.className, color: '#5c6166' },
|
||||
// { tag: tags.definition(tags.typeName), color: '#5c6166' },
|
||||
// { tag: tags.typeName, color: '#5c6166' },
|
||||
// { tag: tags.angleBracket, color: '#5c6166' },
|
||||
// { tag: tags.tagName, color: '#5c6166' },
|
||||
// { tag: tags.attributeName, color: '#5c6166' },
|
||||
// ],
|
||||
//});
|
||||
const shuffleTheme = createTheme({
|
||||
theme: 'dark',
|
||||
settings: {
|
||||
background: "rgba(40,40,40, 1)",
|
||||
foreground: '#75baff',
|
||||
caret: '#5d00ff',
|
||||
selection: '#036dd626',
|
||||
selectionMatch: '#036dd626',
|
||||
lineHighlight: '#8a91991a',
|
||||
gutterForeground: '#8a919966',
|
||||
},
|
||||
styles: [
|
||||
{ tag: t.comment, color: '#787b8099' },
|
||||
{ tag: t.variableName, color: '#0080ff' },
|
||||
{ tag: [t.string, t.special(t.brace)], color: '#5c6166' },
|
||||
{ tag: t.number, color: '#5c6166' },
|
||||
{ tag: t.bool, color: '#5c6166' },
|
||||
{ tag: t.null, color: '#5c6166' },
|
||||
{ tag: t.keyword, color: '#5c6166' },
|
||||
{ tag: t.operator, color: '#5c6166' },
|
||||
{ tag: t.className, color: '#5c6166' },
|
||||
{ tag: t.definition(t.typeName), color: '#5c6166' },
|
||||
{ tag: t.typeName, color: '#5c6166' },
|
||||
{ tag: t.angleBracket, color: '#5c6166' },
|
||||
{ tag: t.tagName, color: '#5c6166' },
|
||||
{ tag: t.attributeName, color: '#5c6166' },
|
||||
],
|
||||
});
|
||||
|
||||
const CodeEditor = (props) => {
|
||||
const {
|
||||
globalUrl,
|
||||
fieldCount,
|
||||
setFieldCount,
|
||||
actionlist,
|
||||
changeActionParameterCodeMirror,
|
||||
expansionModalOpen,
|
||||
@@ -132,6 +129,8 @@ const CodeEditor = (props) => {
|
||||
selectedAction ,
|
||||
workflowExecutions,
|
||||
getParents,
|
||||
|
||||
fieldname,
|
||||
} = props
|
||||
|
||||
const [localcodedata, setlocalcodedata] = React.useState(codedata === undefined || codedata === null || codedata.length === 0 ? "" : codedata);
|
||||
@@ -140,7 +139,7 @@ const CodeEditor = (props) => {
|
||||
const [validation, setValidation] = React.useState(false);
|
||||
const [expOutput, setExpOutput] = React.useState(" ");
|
||||
const [linewrap, setlinewrap] = React.useState(true);
|
||||
const [codeTheme, setcodeTheme] = React.useState("gruvbox-dark");
|
||||
//const [codeTheme, setcodeTheme] = React.useState("gruvbox-dark");
|
||||
const [editorPopupOpen, setEditorPopupOpen] = React.useState(false);
|
||||
|
||||
const [currentCharacter, setCurrentCharacter] = React.useState(-1);
|
||||
@@ -155,8 +154,8 @@ const CodeEditor = (props) => {
|
||||
const [mainVariables, setMainVariables] = React.useState([]);
|
||||
const [availableVariables, setAvailableVariables] = React.useState([]);
|
||||
|
||||
const [menuPosition, setMenuPosition] = useState(null);
|
||||
const [showAutocomplete, setShowAutocomplete] = React.useState(false);
|
||||
const [menuPosition, setMenuPosition] = useState(null);
|
||||
const [showAutocomplete, setShowAutocomplete] = React.useState(false);
|
||||
|
||||
const [isAiLoading, setIsAiLoading] = React.useState(false);
|
||||
|
||||
@@ -863,12 +862,12 @@ const CodeEditor = (props) => {
|
||||
var newResult = {}
|
||||
if (responseJson.success === true && responseJson.result !== null && responseJson.result !== undefined && responseJson.result.length > 0) {
|
||||
const result = responseJson.result.slice(0, 50)+"..."
|
||||
//alert.info("SUCCESS: "+result)
|
||||
//toast("SUCCESS: "+result)
|
||||
|
||||
const validate = validateJson(responseJson.result)
|
||||
newResult = validate
|
||||
} else if (responseJson.success === false && responseJson.reason !== undefined && responseJson.reason !== null) {
|
||||
alert.error(responseJson.reason)
|
||||
toast(responseJson.reason)
|
||||
newResult = {"valid": false, "result": responseJson.reason}
|
||||
} else if (responseJson.success === true) {
|
||||
newResult = {"valid": false, "result": "Couldn't finish execution. Please fill all the required fields, and retry the execution."}
|
||||
@@ -884,7 +883,7 @@ const CodeEditor = (props) => {
|
||||
setExecuting(false)
|
||||
})
|
||||
.catch(error => {
|
||||
//alert.error("Execution error: "+error.toString())
|
||||
//toast("Execution error: "+error.toString())
|
||||
console.log("error: ", error)
|
||||
setExecuting(false)
|
||||
})
|
||||
@@ -919,7 +918,6 @@ const CodeEditor = (props) => {
|
||||
maxHeight: isMobile ? "100%" : 720,
|
||||
border: theme.palette.defaultBorder,
|
||||
padding: isMobile ? "25px 10px 25px 10px" : 25,
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
},
|
||||
}}
|
||||
>
|
||||
@@ -1020,6 +1018,7 @@ const CodeEditor = (props) => {
|
||||
aria-controls={liquidOpen ? 'basic-menu' : undefined}
|
||||
aria-expanded={liquidOpen ? 'true' : undefined}
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
style={{
|
||||
textTransform: "none",
|
||||
width: 100,
|
||||
@@ -1055,6 +1054,7 @@ const CodeEditor = (props) => {
|
||||
aria-controls={mathOpen ? 'basic-menu' : undefined}
|
||||
aria-expanded={mathOpen ? 'true' : undefined}
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
style={{
|
||||
textTransform: "none",
|
||||
width: 100,
|
||||
@@ -1090,6 +1090,7 @@ const CodeEditor = (props) => {
|
||||
aria-controls={pythonOpen ? 'basic-menu' : undefined}
|
||||
aria-expanded={pythonOpen ? 'true' : undefined}
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
style={{
|
||||
textTransform: "none",
|
||||
width: 100,
|
||||
@@ -1125,6 +1126,7 @@ const CodeEditor = (props) => {
|
||||
aria-controls={!!menuPosition ? 'basic-menu' : undefined}
|
||||
aria-expanded={!!menuPosition ? 'true' : undefined}
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
style={{
|
||||
textTransform: "none",
|
||||
width: 130,
|
||||
@@ -1381,7 +1383,7 @@ const CodeEditor = (props) => {
|
||||
position: "relative",
|
||||
}}>
|
||||
<CodeMirror
|
||||
value = {localcodedata}
|
||||
value={localcodedata}
|
||||
height={isFileEditor ? 450 : 525}
|
||||
width={isFileEditor ? 650 : 600}
|
||||
style={{
|
||||
@@ -1389,6 +1391,8 @@ const CodeEditor = (props) => {
|
||||
wordBreak: "break-word",
|
||||
marginTop: 0,
|
||||
paddingTop: 0,
|
||||
backgroundColor: "rgba(40,40,40,1)",
|
||||
minHeight: 470,
|
||||
}}
|
||||
onCursorActivity = {(value) => {
|
||||
// console.log(value.getCursor())
|
||||
@@ -1398,27 +1402,22 @@ const CodeEditor = (props) => {
|
||||
findIndex(value.getCursor().line, value.getCursor().ch)
|
||||
highlight_variables(value)
|
||||
}}
|
||||
onChange={(value) => {
|
||||
//console.log("Value: '", value.getValue(), "'")
|
||||
onChange={(value, viewUpdate) => {
|
||||
console.log("Value: ", value, viewUpdate)
|
||||
setlocalcodedata(value)
|
||||
expectedOutput(value)
|
||||
|
||||
setlocalcodedata(value.getValue())
|
||||
expectedOutput(value.getValue())
|
||||
|
||||
if(value.display.input.prevInput.startsWith('$') || value.display.input.prevInput.endsWith('$')){
|
||||
setEditorPopupOpen(true)
|
||||
}
|
||||
|
||||
// console.log(findIndex(value.getValue()))
|
||||
// highlight_variables(value)
|
||||
//if(value.display.input.prevInput.startsWith('$') || value.display.input.prevInput.endsWith('$')){
|
||||
// setEditorPopupOpen(true)
|
||||
//}
|
||||
}}
|
||||
extensions={[indentWithTab]}
|
||||
extensions={[]}//indentWithTab]}
|
||||
theme={shuffleTheme}
|
||||
options={{
|
||||
styleSelectedText: true,
|
||||
theme: codeTheme,
|
||||
keyMap: 'sublime',
|
||||
mode: validation === true ? "json" : "python",
|
||||
lineWrapping: linewrap,
|
||||
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
@@ -1664,34 +1663,29 @@ const CodeEditor = (props) => {
|
||||
|
||||
|
||||
<div style={{display: 'flex',}}>
|
||||
<button
|
||||
<Button
|
||||
style={{
|
||||
color: "white",
|
||||
background: "#383b49",
|
||||
border: "none",
|
||||
height: 35,
|
||||
flex: 1,
|
||||
marginLeft: 5,
|
||||
marginTop: 5,
|
||||
cursor: "pointer"
|
||||
}}
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
onClick={() => {
|
||||
setExpansionModalOpen(false);
|
||||
}}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
style={{
|
||||
color: "white",
|
||||
background: "#f85a3e",
|
||||
border: "none",
|
||||
|
||||
height: 35,
|
||||
flex: 1,
|
||||
marginLeft: 10,
|
||||
marginTop: 5,
|
||||
cursor: "pointer"
|
||||
}}
|
||||
onClick={(event) => {
|
||||
// Take localcodedata through the Shuffle JSON parser just in case
|
||||
@@ -1709,15 +1703,26 @@ const CodeEditor = (props) => {
|
||||
runUpdateText(fixedcodedata);
|
||||
setcodedata(fixedcodedata);
|
||||
setExpansionModalOpen(false)
|
||||
} else {
|
||||
changeActionParameterCodeMirror(event, fieldCount, fixedcodedata)
|
||||
} else if (changeActionParameterCodeMirror !== undefined) {
|
||||
//changeActionParameterCodeMirror(event, fieldCount, fixedcodedata)
|
||||
changeActionParameterCodeMirror(event, fieldCount, fixedcodedata, actionlist)
|
||||
setExpansionModalOpen(false)
|
||||
setcodedata(fixedcodedata)
|
||||
}
|
||||
|
||||
// Check if fieldname is set, and try to find and inject the text
|
||||
if (fieldname !== undefined && fieldname !== null && fieldname.length > 0) {
|
||||
const foundfield = document.getElementById(fieldname)
|
||||
if (foundfield !== undefined && foundfield !== null) {
|
||||
foundfield.value = fixedcodedata
|
||||
}
|
||||
}
|
||||
|
||||
setExpansionModalOpen(false)
|
||||
}}
|
||||
>
|
||||
Submit
|
||||
</button>
|
||||
</Button>
|
||||
</div>
|
||||
</Dialog>)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { toast } from 'react-toastify';
|
||||
import theme from '../theme.jsx';
|
||||
import { useNavigate, Link } from "react-router-dom";
|
||||
import { useAlert } from "react-alert";
|
||||
//import { useAlert
|
||||
import ConfigureWorkflow from "../components/ConfigureWorkflow.jsx";
|
||||
import PaperComponent from "../components/PaperComponent.jsx"
|
||||
import AuthenticationOauth2 from "../components/Oauth2Auth.jsx";
|
||||
@@ -349,14 +350,14 @@ const UsecaseSearch = (props) => {
|
||||
const [firstRequest, setFirstRequest] = React.useState(true);
|
||||
|
||||
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
|
||||
const alert = useAlert()
|
||||
//const alert = useAlert()
|
||||
|
||||
useEffect(() => {
|
||||
// if (firstRequest !== true && workflow.id !== undefined && autotry === true && setUsecaseSearch !== undefined && authenticationModalOpen === false && configureWorkflowModalOpen === false) {
|
||||
//
|
||||
if (autotry === true && configureWorkflowModalOpen === false && workflow.id !== undefined && setUsecaseSearch !== undefined) {
|
||||
console.log("Close it?")
|
||||
alert.info("Workflow successfully added! Add more apps, and we will suggest more workflows")
|
||||
toast("Workflow successfully added! Add more apps, and we will suggest more workflows")
|
||||
|
||||
if (setCloseWindow !== undefined) {
|
||||
setCloseWindow(true)
|
||||
@@ -793,7 +794,7 @@ const UsecaseSearch = (props) => {
|
||||
console.log("Deleted workflow")
|
||||
})
|
||||
.catch((error) => {
|
||||
//alert.error(error.toString());
|
||||
//toast(error.toString());
|
||||
console.log("Delete workflow error: ", error.toString());
|
||||
})
|
||||
}
|
||||
@@ -822,7 +823,7 @@ const UsecaseSearch = (props) => {
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
//alert.error(error.toString());
|
||||
//toast(error.toString());
|
||||
console.log("Get workflows error: ", error.toString());
|
||||
})
|
||||
}
|
||||
@@ -894,9 +895,9 @@ const UsecaseSearch = (props) => {
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success === false) {
|
||||
if (responseJson.reason !== undefined) {
|
||||
alert.error("Error setting workflow: ", responseJson.reason)
|
||||
toast("Error setting workflow: ", responseJson.reason)
|
||||
} else {
|
||||
alert.error("Error setting workflow.")
|
||||
toast("Error setting workflow.")
|
||||
}
|
||||
|
||||
return
|
||||
@@ -905,7 +906,7 @@ const UsecaseSearch = (props) => {
|
||||
return responseJson;
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -930,7 +931,7 @@ const UsecaseSearch = (props) => {
|
||||
|
||||
if (responseJson.success === false) {
|
||||
if (responseJson.reason !== null && responseJson.reason !== undefined) {
|
||||
//alert.error(responseJson.reason)
|
||||
//toast(responseJson.reason)
|
||||
}
|
||||
|
||||
if (responseJson.source === "") {
|
||||
@@ -1009,13 +1010,13 @@ const UsecaseSearch = (props) => {
|
||||
responseJson.status,
|
||||
).then((response) => {
|
||||
if (response !== undefined) {
|
||||
alert.success("Successfully generated " + responseJson.name);
|
||||
toast("Successfully generated " + responseJson.name);
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error("Generate error: " + error.toString());
|
||||
toast("Generate error: " + error.toString());
|
||||
})
|
||||
|
||||
|
||||
@@ -1057,7 +1058,7 @@ const UsecaseSearch = (props) => {
|
||||
.catch((error) => {
|
||||
setIsUploading(false)
|
||||
console.log("Merge err: ", error.toString())
|
||||
//alert.error("Err: " + error.toString());
|
||||
//toast("Err: " + error.toString());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1207,7 +1208,7 @@ const UsecaseSearch = (props) => {
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
//alert.error("Errors were found. Click them to sort sort them out or go to the next usecase.")
|
||||
//toast("Errors were found. Click them to sort sort them out or go to the next usecase.")
|
||||
|
||||
//setUpdate(Math.random())
|
||||
//setIsUploading(false)
|
||||
@@ -1268,13 +1269,13 @@ const UsecaseSearch = (props) => {
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success === false) {
|
||||
alert.error("Failed to activate the app")
|
||||
toast("Failed to activate the app")
|
||||
} else {
|
||||
//alert.success("App activated for your organization! Refresh the page to use the app.")
|
||||
//toast("App activated for your organization! Refresh the page to use the app.")
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
//alert.error(error.toString())
|
||||
//toast(error.toString())
|
||||
console.log("Activate app error: ", error.toString())
|
||||
});
|
||||
}
|
||||
@@ -1304,9 +1305,9 @@ const UsecaseSearch = (props) => {
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success === false) {
|
||||
if (responseJson.reason !== undefined) {
|
||||
alert.error("Failed updating default app: " + responseJson.reason)
|
||||
toast("Failed updating default app: " + responseJson.reason)
|
||||
} else {
|
||||
alert.error("Failed to update framework for your org.")
|
||||
toast("Failed to update framework for your org.")
|
||||
|
||||
}
|
||||
} else {
|
||||
@@ -1319,7 +1320,7 @@ const UsecaseSearch = (props) => {
|
||||
//setFrameworkData(responseJson)
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
//setFrameworkLoaded(true)
|
||||
})
|
||||
}
|
||||
@@ -1516,7 +1517,7 @@ const UsecaseSearch = (props) => {
|
||||
return (
|
||||
<div key={curindex} style={{display: "flex", maxHeight: 40, minHeight: 40, borderTop: "1px solid rgba(255,255,255,0.3)", }} onClick={() => {
|
||||
if (subdata.disabled === true) {
|
||||
//alert.info("Usecase not available yet.")
|
||||
//toast("Usecase not available yet.")
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ import {
|
||||
Chip,
|
||||
ButtonGroup,
|
||||
} from "@mui/material";
|
||||
import { useAlert } from "react-alert";
|
||||
//import { useAlert
|
||||
|
||||
import { useNavigate, Link } from "react-router-dom";
|
||||
import WorkflowSearch from '../components/Workflowsearch.jsx';
|
||||
@@ -134,7 +134,7 @@ const WelcomeForm = (props) => {
|
||||
|
||||
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
|
||||
|
||||
const alert = useAlert();
|
||||
//const alert = useAlert();
|
||||
let navigate = useNavigate();
|
||||
|
||||
const onNodeSelect = (label) => {
|
||||
@@ -262,16 +262,16 @@ const WelcomeForm = (props) => {
|
||||
response.json().then((responseJson) => {
|
||||
if (responseJson["success"] === false) {
|
||||
console.log("Update user success")
|
||||
//alert.error("Failed updating org: ", responseJson.reason);
|
||||
//toast("Failed updating org: ", responseJson.reason);
|
||||
} else {
|
||||
console.log("Update success!")
|
||||
//alert.success("Successfully edited org!");
|
||||
//toast("Successfully edited org!");
|
||||
}
|
||||
})
|
||||
)
|
||||
.catch((error) => {
|
||||
console.log("Update err: ", error.toString())
|
||||
//alert.error("Err: " + error.toString());
|
||||
//toast("Err: " + error.toString());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -308,15 +308,15 @@ const WelcomeForm = (props) => {
|
||||
response.json().then((responseJson) => {
|
||||
if (responseJson["success"] === false) {
|
||||
console.log("Update of org failed")
|
||||
//alert.error("Failed updating org: ", responseJson.reason);
|
||||
//toast("Failed updating org: ", responseJson.reason);
|
||||
} else {
|
||||
//alert.success("Successfully edited org!");
|
||||
//toast("Successfully edited org!");
|
||||
}
|
||||
})
|
||||
)
|
||||
.catch((error) => {
|
||||
console.log("Update err: ", error.toString())
|
||||
//alert.error("Err: " + error.toString());
|
||||
//toast("Err: " + error.toString());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -615,7 +615,7 @@ const WelcomeForm = (props) => {
|
||||
<Grid item xs={11} style={{marginTop: 25, }}>
|
||||
{/*<FormLabel style={{ color: "#B9B9BA" }}>Find your integrations!</FormLabel>*/}
|
||||
<div style={{display: "flex"}}>
|
||||
<Button disabled={finishedApps.includes("CASES")} variant={defaultSearch === "CASES" ? "contained" : "outlined"} style={{
|
||||
<Button disabled={finishedApps.includes("CASES")} variant={defaultSearch === "CASES" ? "contained" : "outlined"} color="secondary" style={{
|
||||
flex: 1,
|
||||
width: "100%",
|
||||
padding: 25,
|
||||
@@ -627,20 +627,20 @@ const WelcomeForm = (props) => {
|
||||
</Button>
|
||||
</div>
|
||||
<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 />} color="secondary" onClick={(event) => { onNodeSelect("SIEM") }} >
|
||||
SIEM
|
||||
</Button>
|
||||
<Button disabled={finishedApps.includes("EDR & AV") || finishedApps.includes("ERADICATION")} variant={defaultSearch === "Eradication" ? "contained" : "outlined"} style={buttonStyle} startIcon={<NewReleasesIcon />} onClick={(event) => { onNodeSelect("ERADICATION") }} >
|
||||
<Button disabled={finishedApps.includes("EDR & AV") || finishedApps.includes("ERADICATION")} variant={defaultSearch === "Eradication" ? "contained" : "outlined"} style={buttonStyle} startIcon={<NewReleasesIcon />} color="secondary" onClick={(event) => { onNodeSelect("ERADICATION") }} >
|
||||
Endpoint
|
||||
</Button>
|
||||
</div>
|
||||
<div style={{display: "flex"}}>
|
||||
<Button disabled={finishedApps.includes("INTEL")} variant={defaultSearch === "INTEL" ? "contained" : "outlined"} style={buttonStyle} startIcon={<ExtensionIcon />} onClick={(event) => { onNodeSelect("INTEL") }} >
|
||||
<Button disabled={finishedApps.includes("INTEL")} variant={defaultSearch === "INTEL" ? "contained" : "outlined"} style={buttonStyle} startIcon={<ExtensionIcon />} color="secondary" onClick={(event) => { onNodeSelect("INTEL") }} >
|
||||
|
||||
|
||||
Intel
|
||||
</Button>
|
||||
<Button disabled={finishedApps.includes("COMMS") || finishedApps.includes("EMAIL")} 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 />} color="secondary" onClick={(event) => { onNodeSelect("EMAIL") }} >
|
||||
Email
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -25,12 +25,9 @@ import WorkflowPaperNew from "../components/WorkflowPaperNew.jsx"
|
||||
|
||||
const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240")
|
||||
const AppGrid = props => {
|
||||
const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs, alternativeView, } = props
|
||||
|
||||
const isCloud =
|
||||
window.location.host === "localhost:3002" ||
|
||||
window.location.host === "shuffler.io";
|
||||
const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs, alternativeView, onlyResults, inputsearch } = props
|
||||
|
||||
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
|
||||
const rowHandler = maxRows === undefined || maxRows === null ? 50 : maxRows
|
||||
const xs = parsedXs === undefined || parsedXs === null ? isMobile ? 6 : 4 : parsedXs
|
||||
//const [apps, setApps] = React.useState([]);
|
||||
@@ -38,7 +35,9 @@ const AppGrid = props => {
|
||||
const [formMail, setFormMail] = React.useState("");
|
||||
const [message, setMessage] = React.useState("");
|
||||
const [formMessage, setFormMessage] = React.useState("");
|
||||
const [usecases, setUsecases] = React.useState([]);
|
||||
const [usecases, setUsecases] = React.useState([]);
|
||||
|
||||
const [localMessage, setLocalMessage] = React.useState("");
|
||||
|
||||
const buttonStyle = {borderRadius: 30, height: 50, width: 220, margin: isMobile ? "15px auto 15px auto" : 20, fontSize: 18,}
|
||||
|
||||
@@ -70,7 +69,7 @@ const AppGrid = props => {
|
||||
.then(response => {
|
||||
if (response.success === true) {
|
||||
setFormMessage(response.reason)
|
||||
//alert.info("Thanks for submitting!")
|
||||
//toast("Thanks for submitting!")
|
||||
} else {
|
||||
setFormMessage(errorMessage)
|
||||
}
|
||||
@@ -153,21 +152,23 @@ const AppGrid = props => {
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success !== false) {
|
||||
console.log("Usecases: ", responseJson)
|
||||
//handleKeysetting(responseJson, workflows)
|
||||
}
|
||||
if (responseJson.success !== false) {
|
||||
console.log("Usecases: ", responseJson)
|
||||
//handleKeysetting(responseJson, workflows)
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
//alert.error("ERROR: " + error.toString());
|
||||
//toast("ERROR: " + error.toString());
|
||||
console.log("ERROR: " + error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchUsecases()
|
||||
|
||||
}, [])
|
||||
|
||||
|
||||
// value={currentRefinement}
|
||||
const SearchBox = ({currentRefinement, refine, isSearchStalled} ) => {
|
||||
useEffect(() => {
|
||||
@@ -182,16 +183,23 @@ const AppGrid = props => {
|
||||
}
|
||||
}, [])
|
||||
|
||||
if (localMessage !== inputsearch && inputsearch !== undefined && inputsearch !== null && inputsearch.length > 0) {
|
||||
console.log("In refinement: ", inputsearch)
|
||||
//setLocalMessage(inputsearch)
|
||||
refine(inputsearch)
|
||||
} else if (onlyResults === true) {
|
||||
// Don't return anything unless refinement works
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<form noValidate action="" role="search">
|
||||
{onlyResults !== true ?
|
||||
<TextField
|
||||
fullWidth
|
||||
style={{backgroundColor: theme.palette.inputColor, borderRadius: borderRadius, margin: 10, width: "100%",}}
|
||||
InputProps={{
|
||||
style:{
|
||||
color: "white",
|
||||
fontSize: "1em",
|
||||
height: 50,
|
||||
},
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
@@ -210,8 +218,8 @@ const AppGrid = props => {
|
||||
}}
|
||||
limit={5}
|
||||
/>
|
||||
{/*isSearchStalled ? 'My search is stalled' : ''*/}
|
||||
</form>
|
||||
: null}
|
||||
</form>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -228,29 +236,35 @@ const AppGrid = props => {
|
||||
var counted = 0
|
||||
|
||||
return (
|
||||
<Grid container spacing={4} style={paperAppContainer}>
|
||||
{hits.map((data, index) => {
|
||||
workflowDelay += 50
|
||||
<div>
|
||||
{onlyResults === true && hits.length > 0 ?
|
||||
<Typography variant="h6" style={{paddingBottom: 0, }}>
|
||||
Relevant Workflows
|
||||
</Typography>
|
||||
: null}
|
||||
<Grid container spacing={4} style={paperAppContainer}>
|
||||
{hits.map((data, index) => {
|
||||
workflowDelay += 50
|
||||
|
||||
if (counted === 12/xs*rowHandler) {
|
||||
return null
|
||||
}
|
||||
if (counted === 12/xs*rowHandler) {
|
||||
return null
|
||||
}
|
||||
|
||||
counted += 1
|
||||
counted += 1
|
||||
|
||||
return (
|
||||
<Zoom key={index} in={true} style={{ transitionDelay: `${workflowDelay}ms` }}>
|
||||
<Grid item xs={xs} style={{ padding: "12px 10px 12px 10px" }}>
|
||||
return (
|
||||
<Grid item xs={xs} style={{ padding: "12px 10px 12px 10px",}}>
|
||||
{/*<Zoom key={index} in={true} style={{ transitionDelay: `${workflowDelay}ms` }}>*/}
|
||||
{alternativeView === true ?
|
||||
<WorkflowPaperNew key={index} data={data} />
|
||||
:
|
||||
<WorkflowPaper key={index} data={data} />
|
||||
}
|
||||
</Grid>
|
||||
</Zoom>
|
||||
)
|
||||
})}
|
||||
</Grid>
|
||||
)
|
||||
})}
|
||||
</Grid>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -331,11 +345,11 @@ const AppGrid = props => {
|
||||
fullWidth={true}
|
||||
placeholder="What apps do you want to see?"
|
||||
type=""
|
||||
id="standard-required"
|
||||
id="standard-required"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
autoComplete="off"
|
||||
onChange={e => setMessage(e.target.value)}
|
||||
onChange={e => setMessage(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
@@ -353,15 +367,16 @@ const AppGrid = props => {
|
||||
</div>
|
||||
: null
|
||||
}
|
||||
|
||||
<span style={{position: "absolute", display: "flex", textAlign: "right", float: "right", right: 0, bottom: 120, }}>
|
||||
<Typography variant="body2" color="textSecondary" style={{}}>
|
||||
Search by
|
||||
</Typography>
|
||||
<a rel="noopener noreferrer" href="https://www.algolia.com/" target="_blank" style={{textDecoration: "none", color: "white"}}>
|
||||
<img src={"/images/logo-algolia-nebula-blue-full.svg"} alt="Algolia logo" style={{height: 17, marginLeft: 5, marginTop: 3,}} />
|
||||
</a>
|
||||
</span>
|
||||
{onlyResults === true ? null :
|
||||
<span style={{position: "absolute", display: "flex", textAlign: "right", float: "right", right: 0, bottom: 120, }}>
|
||||
<Typography variant="body2" color="textSecondary" style={{}}>
|
||||
Search by
|
||||
</Typography>
|
||||
<a rel="noopener noreferrer" href="https://www.algolia.com/" target="_blank" style={{textDecoration: "none", color: "white"}}>
|
||||
<img src={"/images/logo-algolia-nebula-blue-full.svg"} alt="Algolia logo" style={{height: 17, marginLeft: 5, marginTop: 3,}} />
|
||||
</a>
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,291 +0,0 @@
|
||||
import React, { useState, useEffect, useLayoutEffect } from "react";
|
||||
import theme from '../theme';
|
||||
|
||||
import {
|
||||
Chip,
|
||||
Typography,
|
||||
Paper,
|
||||
Avatar,
|
||||
Grid,
|
||||
Tooltip,
|
||||
} from "@material-ui/core";
|
||||
|
||||
import {
|
||||
AvatarGroup,
|
||||
} from "@mui/material"
|
||||
|
||||
import {
|
||||
Restore as RestoreIcon,
|
||||
Edit as EditIcon,
|
||||
BubbleChart as BubbleChartIcon,
|
||||
MoreVert as MoreVertIcon,
|
||||
} from '@material-ui/icons';
|
||||
|
||||
import { useNavigate, Link, useParams } from "react-router-dom";
|
||||
|
||||
const workflowActionStyle = {
|
||||
display: "flex",
|
||||
width: 160,
|
||||
height: 44,
|
||||
justifyContent: "space-between",
|
||||
}
|
||||
|
||||
const paperAppStyle = {
|
||||
minHeight: 130,
|
||||
maxHeight: 130,
|
||||
overflow: "hidden",
|
||||
width: "100%",
|
||||
color: "white",
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
padding: "12px 12px 0px 15px",
|
||||
borderRadius: 5,
|
||||
display: "flex",
|
||||
boxSizing: "border-box",
|
||||
position: "relative",
|
||||
}
|
||||
|
||||
const chipStyle = {
|
||||
backgroundColor: "#3d3f43",
|
||||
marginRight: 5,
|
||||
paddingLeft: 5,
|
||||
paddingRight: 5,
|
||||
height: 28,
|
||||
cursor: "pointer",
|
||||
borderColor: "#3d3f43",
|
||||
color: "white",
|
||||
}
|
||||
|
||||
const WorkflowPaper = (props) => {
|
||||
const { data } = props;
|
||||
let navigate = useNavigate();
|
||||
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const [anchorEl, setAnchorEl] = React.useState(null);
|
||||
const appGroup = data.action_references === undefined || data.action_references === null ? [] : data.action_references
|
||||
|
||||
//console.log("Workflow: ", data)
|
||||
var boxColor = "#86c142";
|
||||
|
||||
var parsedName = data.name;
|
||||
if (
|
||||
parsedName !== undefined &&
|
||||
parsedName !== null &&
|
||||
parsedName.length > 20
|
||||
) {
|
||||
parsedName = parsedName.slice(0, 21) + "..";
|
||||
}
|
||||
|
||||
|
||||
const imageStyle = {
|
||||
width: 24,
|
||||
height: 24,
|
||||
marginRight: 10,
|
||||
border: "1px solid rgba(255,255,255,0.3)",
|
||||
}
|
||||
var image = data.creator_info !== undefined && data.creator_info !== null && data.creator_info.image !== undefined && data.creator_info.image !== null && data.creator_info.image.length > 0 ? <Avatar alt={data.creator} src={data.creator_info.image} style={imageStyle}/> : <Avatar alt={"shuffle_image"} src={theme.palette.defaultImage} style={imageStyle}/>
|
||||
const creatorname = data.creator_info !== undefined && data.creator_info !== null && data.creator_info.username !== undefined && data.creator_info.username !== null && data.creator_info.username.length > 0 ? data.creator_info.username : ""
|
||||
var orgName = "";
|
||||
var orgId = "";
|
||||
if ((data.objectID === undefined || data.objectID === null) && data.id !== undefined && data.id !== null) {
|
||||
data.objectID = data.id
|
||||
}
|
||||
|
||||
//console.log("IMG: ", data)
|
||||
var parsedUrl = `/workflows/${data.objectID}`
|
||||
if (data.__queryID !== undefined && data.__queryID !== null) {
|
||||
parsedUrl += `?queryID=${data.__queryID}`
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{width: "100%", position: "relative",}}>
|
||||
<Paper square style={paperAppStyle}>
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
bottom: 1,
|
||||
left: 1,
|
||||
height: 12,
|
||||
width: 12,
|
||||
backgroundColor: boxColor,
|
||||
borderRadius: "0 100px 0 0",
|
||||
}}
|
||||
/>
|
||||
<Grid
|
||||
item
|
||||
style={{ display: "flex", flexDirection: "column", width: "100%" }}
|
||||
>
|
||||
<Grid item style={{ display: "flex", maxHeight: 34 }}>
|
||||
<Tooltip title={`${creatorname}`} placement="bottom">
|
||||
<div
|
||||
style={{ cursor: data.creator_info !== undefined ? "pointer" : "inherit" }}
|
||||
onClick={() => {
|
||||
if (data.creator_info !== undefined) {
|
||||
navigate("/creators/"+data.creator_info.username)
|
||||
}
|
||||
}}
|
||||
>
|
||||
{image}
|
||||
</div>
|
||||
</Tooltip>
|
||||
<Tooltip title={`Edit ${data.name}`} placement="bottom">
|
||||
<Typography
|
||||
variant="body1"
|
||||
style={{
|
||||
marginBottom: 0,
|
||||
paddingBottom: 0,
|
||||
maxHeight: 30,
|
||||
flex: 10,
|
||||
}}
|
||||
>
|
||||
<Link
|
||||
to={parsedUrl}
|
||||
style={{ textDecoration: "none", color: "inherit" }}
|
||||
>
|
||||
{parsedName}
|
||||
</Link>
|
||||
</Typography>
|
||||
</Tooltip>
|
||||
</Grid>
|
||||
<Grid item style={workflowActionStyle}>
|
||||
{appGroup.length > 0 ?
|
||||
<div style={{display: "flex", marginTop: 8, }}>
|
||||
<AvatarGroup max={4} style={{marginLeft: 5, maxHeight: 24,}}>
|
||||
{appGroup.map((app, index) => {
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
style={{
|
||||
height: 24,
|
||||
width: 24,
|
||||
filter: "brightness(0.6)",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
onClick={() => {
|
||||
navigate("/apps/"+app.id)
|
||||
}}
|
||||
>
|
||||
<Tooltip color="primary" title={app.name} placement="bottom">
|
||||
<Avatar alt={app.name} src={app.image_url} style={{width: 24, height: 24}}/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</AvatarGroup>
|
||||
</div>
|
||||
:
|
||||
<Tooltip color="primary" title="Action amount" placement="bottom">
|
||||
<span style={{ color: "#979797", display: "flex" }}>
|
||||
<BubbleChartIcon
|
||||
style={{ marginTop: "auto", marginBottom: "auto" }}
|
||||
/>
|
||||
<Typography
|
||||
style={{
|
||||
marginLeft: 5,
|
||||
marginTop: "auto",
|
||||
marginBottom: "auto",
|
||||
}}
|
||||
>
|
||||
{data.actions === undefined || data.actions === null ? 1 : data.actions.length}
|
||||
</Typography>
|
||||
</span>
|
||||
</Tooltip>
|
||||
}
|
||||
<Tooltip
|
||||
color="primary"
|
||||
title="Trigger amount"
|
||||
placement="bottom"
|
||||
>
|
||||
<span
|
||||
style={{ marginLeft: 15, color: "#979797", display: "flex" }}
|
||||
>
|
||||
<RestoreIcon
|
||||
style={{
|
||||
color: "#979797",
|
||||
marginTop: "auto",
|
||||
marginBottom: "auto",
|
||||
}}
|
||||
/>
|
||||
<Typography
|
||||
style={{
|
||||
marginLeft: 5,
|
||||
marginTop: "auto",
|
||||
marginBottom: "auto",
|
||||
}}
|
||||
>
|
||||
{data.triggers === undefined || data.triggers === null ? 1 : data.triggers.length}
|
||||
</Typography>
|
||||
</span>
|
||||
</Tooltip>
|
||||
<Tooltip color="primary" title="Subflows used" placement="bottom">
|
||||
<span
|
||||
style={{
|
||||
marginLeft: 15,
|
||||
display: "flex",
|
||||
color: "#979797",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
onClick={() => {
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
width="18"
|
||||
height="18"
|
||||
viewBox="0 0 18 18"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
style={{
|
||||
color: "#979797",
|
||||
marginTop: "auto",
|
||||
marginBottom: "auto",
|
||||
}}
|
||||
>
|
||||
<path
|
||||
d="M0 0H15V15H0V0ZM16 16H18V18H16V16ZM16 13H18V15H16V13ZM16 10H18V12H16V10ZM16 7H18V9H16V7ZM16 4H18V6H16V4ZM13 16H15V18H13V16ZM10 16H12V18H10V16ZM7 16H9V18H7V16ZM4 16H6V18H4V16Z"
|
||||
fill="#979797"
|
||||
/>
|
||||
</svg>
|
||||
<Typography
|
||||
style={{
|
||||
marginLeft: 5,
|
||||
marginTop: "auto",
|
||||
marginBottom: "auto",
|
||||
}}
|
||||
>
|
||||
{0}
|
||||
</Typography>
|
||||
</span>
|
||||
</Tooltip>
|
||||
</Grid>
|
||||
<Grid
|
||||
item
|
||||
style={{
|
||||
justifyContent: "left",
|
||||
overflow: "hidden",
|
||||
marginTop: 5,
|
||||
}}
|
||||
>
|
||||
{data.tags !== undefined && data.tags !== null
|
||||
? data.tags.map((tag, index) => {
|
||||
if (index >= 3) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Chip
|
||||
key={index}
|
||||
style={chipStyle}
|
||||
label={tag}
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
/>
|
||||
);
|
||||
})
|
||||
: null}
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Paper>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default WorkflowPaper
|
||||
@@ -53,7 +53,7 @@ const WorkflowSearch = props => {
|
||||
.then(response => {
|
||||
if (response.success === true) {
|
||||
setFormMessage(response.reason)
|
||||
//alert.info("Thanks for submitting!")
|
||||
//toast("Thanks for submitting!")
|
||||
} else {
|
||||
setFormMessage(errorMessage)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { createTheme, adaptV4Theme } from "@mui/material/styles";
|
||||
//const theme = createTheme({
|
||||
const theme = createTheme(adaptV4Theme({
|
||||
palette: {
|
||||
theme: "dark",
|
||||
main: "#F86743",
|
||||
primary: {
|
||||
main: "#F86743",
|
||||
@@ -17,8 +18,8 @@ const theme = createTheme(adaptV4Theme({
|
||||
secondary: "rgba(255,255,255,0.7)",
|
||||
},
|
||||
type: "dark",
|
||||
surfaceColor: "#27292d",
|
||||
inputColor: "#383B40",
|
||||
surfaceColor: "#27292d",
|
||||
platformColor: "#1c1c1d",
|
||||
backgroundColor: "#1a1a1a",
|
||||
borderRadius: 5,
|
||||
@@ -34,6 +35,7 @@ const theme = createTheme(adaptV4Theme({
|
||||
borderRadius: 5,
|
||||
},
|
||||
innerTextfieldStyle: {
|
||||
// Removed since upgrading to mui 18
|
||||
//color: "white",
|
||||
//minHeight: 50,
|
||||
//marginLeft: "5px",
|
||||
|
||||
+354
-198
File diff suppressed because it is too large
Load Diff
+1096
-514
File diff suppressed because it is too large
Load Diff
@@ -45,8 +45,9 @@ import {
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { Link, useParams } from "react-router-dom";
|
||||
import YAML from "yaml";
|
||||
import ChipInput from "material-ui-chip-input";
|
||||
import { useAlert } from "react-alert";
|
||||
import { MuiChipsInput } from "mui-chips-input";
|
||||
//import { useAlert
|
||||
import { ToastContainer, toast } from "react-toastify"
|
||||
import words from "shellwords";
|
||||
|
||||
import AvatarEditor from "react-avatar-editor";
|
||||
@@ -350,7 +351,7 @@ const getJsonObject = (properties) => {
|
||||
const AppCreator = (defaultprops) => {
|
||||
const { globalUrl, isLoaded } = defaultprops;
|
||||
const classes = useStyles();
|
||||
const alert = useAlert();
|
||||
//const alert = useAlert();
|
||||
|
||||
const params = useParams();
|
||||
var props = JSON.parse(JSON.stringify(defaultprops))
|
||||
@@ -451,7 +452,7 @@ const AppCreator = (defaultprops) => {
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success === false) {
|
||||
alert.error("Failed to get the app");
|
||||
toast("Failed to get the app");
|
||||
setIsAppLoaded(true);
|
||||
window.location.pathname = "/search";
|
||||
} else {
|
||||
@@ -459,7 +460,7 @@ const AppCreator = (defaultprops) => {
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
@@ -494,14 +495,14 @@ const AppCreator = (defaultprops) => {
|
||||
.then((responseJson) => {
|
||||
setIsAppLoaded(true);
|
||||
if (!responseJson.success) {
|
||||
alert.error("Failed to get app config. Do you have access?");
|
||||
toast("Failed to get app config. Do you have access?");
|
||||
} else {
|
||||
parseIncomingOpenapiData(responseJson);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log("Error: ", error.toString());
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
@@ -568,7 +569,7 @@ const AppCreator = (defaultprops) => {
|
||||
}
|
||||
|
||||
if (data.openapi === null) {
|
||||
alert.info("Failed to load OpenAPI for app. Please contact support if this persists.")
|
||||
toast("Failed to load OpenAPI for app. Please contact support if this persists.")
|
||||
setIsAppLoaded(true);
|
||||
return
|
||||
}
|
||||
@@ -600,7 +601,7 @@ const AppCreator = (defaultprops) => {
|
||||
}
|
||||
|
||||
if (!jsonvalid) {
|
||||
alert.info("OpenAPI data is invalid.");
|
||||
toast("OpenAPI data is invalid.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -726,7 +727,7 @@ const AppCreator = (defaultprops) => {
|
||||
|
||||
for (let [method, methodvalue] of Object.entries(pathvalue)) {
|
||||
if (methodvalue === null) {
|
||||
alert.info("Skipped method (null)" + method);
|
||||
toast("Skipped method (null)" + method);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -734,7 +735,7 @@ const AppCreator = (defaultprops) => {
|
||||
// Typical YAML issue
|
||||
if (method !== "parameters") {
|
||||
console.log("Invalid method: ", method, "data: ", methodvalue);
|
||||
//alert.info("Skipped method (not allowed): " + method);
|
||||
//toast("Skipped method (not allowed): " + method);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@@ -1596,7 +1597,7 @@ const AppCreator = (defaultprops) => {
|
||||
setParameterLocation(value.in);
|
||||
if (!apikeySelection.includes(value.in)) {
|
||||
console.log("APIKEY SELECT: ", apikeySelection);
|
||||
alert.error("Might be error in setting up API key authentication");
|
||||
toast("Might be error in setting up API key authentication");
|
||||
}
|
||||
|
||||
console.log("PARAM NAME: ", value.name);
|
||||
@@ -1637,7 +1638,7 @@ const AppCreator = (defaultprops) => {
|
||||
optionset = true
|
||||
|
||||
} else if (value.type === "oauth2" || key === "Oauth2" || key === "Oauth2c" || (key !== undefined && key !== null && key.toLowerCase().includes("oauth2"))) {
|
||||
//alert.info("Can't handle Oauth2 auth yet.")
|
||||
//toast("Can't handle Oauth2 auth yet.")
|
||||
setAuthenticationOption("Oauth2");
|
||||
setAuthenticationRequired(true);
|
||||
optionset = true
|
||||
@@ -1686,7 +1687,7 @@ const AppCreator = (defaultprops) => {
|
||||
const scopekeysplit = scopekey.split("/");
|
||||
if (scopekeysplit.length < 5) {
|
||||
console.log("Skipping scope: ", scopekey);
|
||||
alert.info("Skipping scope: " + scopekey);
|
||||
toast("Skipping scope: " + scopekey);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1707,7 +1708,7 @@ const AppCreator = (defaultprops) => {
|
||||
);
|
||||
}
|
||||
} else {
|
||||
alert.error("Couldn't handle AUTH type: ", key);
|
||||
toast("Couldn't handle AUTH type: ", key);
|
||||
//newauth.push({
|
||||
// "name": key,
|
||||
// "type": value.in,
|
||||
@@ -1716,7 +1717,7 @@ const AppCreator = (defaultprops) => {
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
alert.error("Failed to handle auth")
|
||||
toast("Failed to handle auth")
|
||||
console.log("Error: ", e)
|
||||
}
|
||||
|
||||
@@ -1790,7 +1791,7 @@ const AppCreator = (defaultprops) => {
|
||||
|
||||
//const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"
|
||||
if (newActions.length > 1000 && isCloud) {
|
||||
alert.error("Cut down actions from " + newActions.length + " to 999 because of limit");
|
||||
toast("Cut down actions from " + newActions.length + " to 999 because of limit");
|
||||
newActions = newActions.slice(0, 999);
|
||||
}
|
||||
|
||||
@@ -1812,7 +1813,7 @@ const AppCreator = (defaultprops) => {
|
||||
// Saving the app that's been configured.
|
||||
// Save SAVE app
|
||||
const submitApp = () => {
|
||||
alert.info("Uploading and building app " + name);
|
||||
toast("Uploading and building app " + name);
|
||||
setAppBuilding(true);
|
||||
setErrorCode("");
|
||||
|
||||
@@ -1881,7 +1882,7 @@ const AppCreator = (defaultprops) => {
|
||||
for (let actionkey in actions) {
|
||||
var item = JSON.parse(JSON.stringify(actions[actionkey]))
|
||||
if (item.errors.length > 0) {
|
||||
alert.error("Saving with error in action " + item.name);
|
||||
toast("Saving with error in action " + item.name);
|
||||
}
|
||||
|
||||
if (item.name === undefined && item.description !== undefined) {
|
||||
@@ -2107,7 +2108,7 @@ const AppCreator = (defaultprops) => {
|
||||
|
||||
// Bad code as it doesn't allow for "anything".
|
||||
if (skipped) {
|
||||
alert.info(
|
||||
toast(
|
||||
"Bad configuration of " +
|
||||
item.name +
|
||||
". Skipping because queries are invalid."
|
||||
@@ -2358,7 +2359,7 @@ const AppCreator = (defaultprops) => {
|
||||
|
||||
if (authenticationOption === "API key") {
|
||||
if (parameterName.length === 0) {
|
||||
alert.error("A field name for the APIkey must be defined");
|
||||
toast("A field name for the APIkey must be defined");
|
||||
setAppBuilding(false);
|
||||
return;
|
||||
}
|
||||
@@ -2424,7 +2425,7 @@ const AppCreator = (defaultprops) => {
|
||||
const curauth = extraAuth[authkey];
|
||||
|
||||
if (curauth.name.toLowerCase() == "url") {
|
||||
alert.error("Can't add extra auth with Name URL");
|
||||
toast("Can't add extra auth with Name URL");
|
||||
setAppBuilding(false);
|
||||
return;
|
||||
}
|
||||
@@ -2459,10 +2460,10 @@ const AppCreator = (defaultprops) => {
|
||||
if (!responseJson.success) {
|
||||
if (responseJson.reason !== undefined) {
|
||||
setErrorCode(responseJson.reason);
|
||||
alert.error("Failed to verify: " + responseJson.reason);
|
||||
toast("Failed to verify: " + responseJson.reason);
|
||||
}
|
||||
} else {
|
||||
alert.success("Successfully uploaded openapi");
|
||||
toast("Successfully uploaded openapi");
|
||||
if (window.location.pathname.includes("/new")) {
|
||||
if (responseJson.id !== undefined && responseJson.id !== null) {
|
||||
window.location = `/apps/edit/${responseJson.id}`;
|
||||
@@ -2473,7 +2474,7 @@ const AppCreator = (defaultprops) => {
|
||||
.catch((error) => {
|
||||
setAppBuilding(false);
|
||||
setErrorCode(error.toString());
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
@@ -2805,7 +2806,7 @@ const AppCreator = (defaultprops) => {
|
||||
!tmpstring.startsWith("http") &&
|
||||
!tmpstring.startsWith("ftp")
|
||||
) {
|
||||
alert.error("Auth URL must start with http(s)://");
|
||||
toast("Auth URL must start with http(s)://");
|
||||
}
|
||||
|
||||
if (tmpstring.includes("?")) {
|
||||
@@ -2854,7 +2855,7 @@ const AppCreator = (defaultprops) => {
|
||||
!tmpstring.startsWith("http") &&
|
||||
!tmpstring.startsWith("ftp")
|
||||
) {
|
||||
alert.error("Token URL must start with http(s)://");
|
||||
toast("Token URL must start with http(s)://");
|
||||
}
|
||||
|
||||
if (tmpstring.includes("?")) {
|
||||
@@ -2900,7 +2901,7 @@ const AppCreator = (defaultprops) => {
|
||||
!tmpstring.startsWith("http") &&
|
||||
!tmpstring.startsWith("ftp")
|
||||
) {
|
||||
alert.error("Refresh URL must start with http(s)://");
|
||||
toast("Refresh URL must start with http(s)://");
|
||||
}
|
||||
|
||||
if (tmpstring.includes("?")) {
|
||||
@@ -2925,7 +2926,7 @@ const AppCreator = (defaultprops) => {
|
||||
>
|
||||
Scopes for Oauth2
|
||||
</Typography>
|
||||
<ChipInput
|
||||
<MuiChipsInput
|
||||
style={{border: "2px solid #f86a3e", borderRadius: theme.palette.borderRadius,}}
|
||||
required
|
||||
InputProps={{
|
||||
@@ -2939,6 +2940,10 @@ const AppCreator = (defaultprops) => {
|
||||
color="primary"
|
||||
fullWidth
|
||||
value={oauth2Scopes}
|
||||
onChange={(chips) => {
|
||||
setOauth2Scopes(chips)
|
||||
setUpdate(Math.random())
|
||||
}}
|
||||
onAdd={(chip) => {
|
||||
oauth2Scopes.push(chip);
|
||||
console.log(oauth2Scopes);
|
||||
@@ -3787,7 +3792,7 @@ const AppCreator = (defaultprops) => {
|
||||
value = keysplit[1].trim()
|
||||
|
||||
} else {
|
||||
alert.error("Removed key: ", key)
|
||||
toast("Removed key: ", key)
|
||||
continue
|
||||
}
|
||||
}
|
||||
@@ -4396,7 +4401,7 @@ const AppCreator = (defaultprops) => {
|
||||
})}
|
||||
</Select>
|
||||
<h4>Tags</h4>
|
||||
<ChipInput
|
||||
<MuiChipsInput
|
||||
style={{ marginTop: 10 }}
|
||||
InputProps={{
|
||||
style: {
|
||||
@@ -4407,6 +4412,10 @@ const AppCreator = (defaultprops) => {
|
||||
color="primary"
|
||||
fullWidth
|
||||
value={newWorkflowTags}
|
||||
onChange={(chips) => {
|
||||
setNewWorkflowTags(chips)
|
||||
setUpdate("added "+chips)
|
||||
}}
|
||||
onAdd={(chip) => {
|
||||
newWorkflowTags.push(chip);
|
||||
setNewWorkflowTags(newWorkflowTags);
|
||||
@@ -4618,11 +4627,11 @@ const AppCreator = (defaultprops) => {
|
||||
setSelectedAction(selectedAction);
|
||||
}
|
||||
|
||||
//alert.error("Failed getting authentications")
|
||||
//toast("Failed getting authentications")
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error("Auth loading error: " + error.toString());
|
||||
toast("Auth loading error: " + error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
@@ -4678,17 +4687,17 @@ const AppCreator = (defaultprops) => {
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (!responseJson.success) {
|
||||
alert.error("Failed to set app auth: " + responseJson.reason);
|
||||
toast("Failed to set app auth: " + responseJson.reason);
|
||||
} else {
|
||||
getAppAuthentication(true);
|
||||
setAuthenticationModalOpen(false);
|
||||
|
||||
// Needs a refresh with the new authentication..
|
||||
//alert.success("Successfully saved new app auth")
|
||||
//toast("Successfully saved new app auth")
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
@@ -4739,7 +4748,7 @@ const AppCreator = (defaultprops) => {
|
||||
console.log("NEW AUTH: ", authenticationOption);
|
||||
if (authenticationOption.label.length === 0) {
|
||||
authenticationOption.label = `Auth for ${selectedApp.name}`;
|
||||
//alert.info("Label can't be empty")
|
||||
//toast("Label can't be empty")
|
||||
//return
|
||||
}
|
||||
|
||||
@@ -4749,7 +4758,7 @@ const AppCreator = (defaultprops) => {
|
||||
selectedApp.authentication.parameters[key].name
|
||||
].length === 0
|
||||
) {
|
||||
alert.info(
|
||||
toast(
|
||||
"Field " +
|
||||
selectedApp.authentication.parameters[key].name +
|
||||
" can't be empty"
|
||||
@@ -5157,7 +5166,7 @@ const AppCreator = (defaultprops) => {
|
||||
setFileBase64(canvasUrl);
|
||||
}
|
||||
} catch (e) {
|
||||
alert.error("Failed to parse canvasurl!");
|
||||
toast("Failed to parse canvasurl!");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -5245,7 +5254,7 @@ const AppCreator = (defaultprops) => {
|
||||
setOpenImageModal(false);
|
||||
setDisableImageUpload(true);
|
||||
} catch (e) {
|
||||
alert.error("Failed to set image. Replace it if this persists.");
|
||||
toast("Failed to set image. Replace it if this persists.");
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -5460,7 +5469,7 @@ const AppCreator = (defaultprops) => {
|
||||
const invalid = ["#", ":", "."];
|
||||
for (var key in invalid) {
|
||||
if (e.target.value.includes(invalid[key])) {
|
||||
alert.error("Can't use " + invalid[key] + " in name");
|
||||
toast("Can't use " + invalid[key] + " in name");
|
||||
setName(e.target.value.replaceAll(".", "").replaceAll("#", "").replaceAll(":", "").replaceAll(",", ""))
|
||||
|
||||
return;
|
||||
@@ -5468,7 +5477,7 @@ const AppCreator = (defaultprops) => {
|
||||
}
|
||||
|
||||
if (e.target.value.length > 29) {
|
||||
alert.error("Choose a shorter name (max 29).");
|
||||
toast("Choose a shorter name (max 29).");
|
||||
setName(e.target.value.slice(0,28))
|
||||
return;
|
||||
}
|
||||
@@ -5580,7 +5589,7 @@ const AppCreator = (defaultprops) => {
|
||||
!tmpstring.startsWith("http") &&
|
||||
!tmpstring.startsWith("ftp")
|
||||
) {
|
||||
alert.error("URL must start with http(s)://");
|
||||
toast("URL must start with http(s)://");
|
||||
}
|
||||
|
||||
if (tmpstring.includes("?")) {
|
||||
|
||||
@@ -1,468 +0,0 @@
|
||||
|
||||
import { Grid, Divider, List, ListItem, ListItemText } from "@mui/material";
|
||||
import { experimentalStyled as styled } from '@mui/material/styles';
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import Paper from "@material-ui/core/Paper";
|
||||
import Button from '@mui/material/Button';
|
||||
import Box from '@material-ui/core/Box';
|
||||
import React, { useState, useEffect } from "react";
|
||||
|
||||
|
||||
import algoliasearch from "algoliasearch";
|
||||
//import algoliarecommend from "algoliarecommend";
|
||||
|
||||
const searchClient = algoliasearch(
|
||||
"JNSS5CFDZZ",
|
||||
"db08e40265e2941b9a7d8f644b6e5240"
|
||||
);
|
||||
|
||||
// https://www.algolia.com/doc/api-client/getting-started/install/
|
||||
/*const algoliarecommend = require('@algolia/recommend');
|
||||
|
||||
const client = algoliarecommend(
|
||||
"NSS5CFDZZ",
|
||||
"db08e40265e2941b9a7d8f644b6e5240"
|
||||
);*/
|
||||
|
||||
|
||||
const Item = styled(Paper)(({ theme }) => ({
|
||||
padding: theme.spacing(2),
|
||||
border: "0.0625rem solid #b2b2b2",
|
||||
borderRadius: "1.5625rem",
|
||||
boxSizing: "content-box",
|
||||
backgroundColor: "transparent",
|
||||
width: "200px",
|
||||
textAlign: 'center',
|
||||
color: theme.palette.text.secondary,
|
||||
marginTop: "20px",
|
||||
marginBottom: "20px",
|
||||
color: "textPrimary"
|
||||
}));
|
||||
const AppExplorer = (props) => {
|
||||
const [algoliaResult, setAlgoliaResult] = useState("");
|
||||
|
||||
const runAlgoliaAppSearch = (query) => {
|
||||
const index = searchClient.initIndex("appsearch");
|
||||
|
||||
index
|
||||
.search(`${query}`)
|
||||
.then(({ hits }) => {
|
||||
setAlgoliaResult(hits);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
runAlgoliaAppSearch("wazuh")
|
||||
|
||||
}, [])
|
||||
|
||||
|
||||
const brandApp = () => {
|
||||
const index = searchClient.initIndex("appsearch");
|
||||
|
||||
const replicaIndex = searchClient.initIndex('appsearch');
|
||||
replicaIndex.setSettings({
|
||||
customRanking: [
|
||||
"asc(time_edited)"
|
||||
]
|
||||
})
|
||||
|
||||
.then(({ hits }) => {
|
||||
console.log(hits);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
brandApp()
|
||||
|
||||
}, [])
|
||||
|
||||
/*const trandingApp = () => {
|
||||
|
||||
const index = client.getTrendingGlobalItems([
|
||||
{
|
||||
indexName: "appsearch",
|
||||
threshold: 60
|
||||
},
|
||||
])
|
||||
.then(({ results }) => {
|
||||
console.log(results);
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err);
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
trandingApp();
|
||||
|
||||
}, [])
|
||||
*/
|
||||
|
||||
const SideBar = {
|
||||
minWidth: 250,
|
||||
|
||||
borderRight: "1px solid rgba(255,255,255,0.3)",
|
||||
left: 0,
|
||||
position: "sticky",
|
||||
minHeight: "90vh",
|
||||
maxHeight: "90vh",
|
||||
overflowX: "hidden",
|
||||
overflowY: "auto",
|
||||
zIndex: 1000,
|
||||
color: "white"
|
||||
};
|
||||
const contentbar = {
|
||||
padding: "40px",
|
||||
|
||||
};
|
||||
const boxdata = {
|
||||
paddingLeft: "30px"
|
||||
}
|
||||
const link = {
|
||||
textDecoration: "none"
|
||||
}
|
||||
const catItems = (
|
||||
<div style={SideBar}>
|
||||
<List>
|
||||
<ListItem >
|
||||
<ListItemText>
|
||||
<span><Typography variant="primary">Categories</Typography></span>
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
<span></span>
|
||||
<Divider />
|
||||
<ListItem>
|
||||
<ListItemText>
|
||||
<Button variant="primary">
|
||||
ASSETS
|
||||
</Button>
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<ListItemText>
|
||||
<Button variant="primary">
|
||||
CASES
|
||||
</Button>
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<ListItemText>
|
||||
<Button variant="primary">
|
||||
COMMS
|
||||
</Button>
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<ListItemText>
|
||||
<Button variant="primary">
|
||||
EDR & AV
|
||||
</Button>
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<ListItemText>
|
||||
<Button variant="primary">
|
||||
IAM
|
||||
</Button>
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<ListItemText>
|
||||
<Button variant="primary">
|
||||
INTEL
|
||||
</Button>
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<ListItemText>
|
||||
<Button variant="primary">
|
||||
NETWORK
|
||||
</Button>
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<ListItemText>
|
||||
<Button variant="primary">
|
||||
SIEM
|
||||
</Button>
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
</List>
|
||||
</div>
|
||||
)
|
||||
return (
|
||||
<div>
|
||||
<div style={{ display: "flex" }}>
|
||||
|
||||
{catItems}
|
||||
<div style={contentbar}>
|
||||
<Grid>
|
||||
<Grid item xl={8} style={{ "border": "20px" }}>
|
||||
|
||||
<Typography type="title" variant="h6">
|
||||
Getting Started
|
||||
</Typography>
|
||||
<div style={{
|
||||
paddingLeft: "50px",
|
||||
|
||||
}}>
|
||||
</div>
|
||||
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Box sx={{ flexGrow: 1 }}>
|
||||
<Grid
|
||||
container
|
||||
spacing={{ xs: 1, md: 4 }}
|
||||
columns={{ xs: 4, sm: 8, md: 12 }}
|
||||
>
|
||||
{Array.from(Array(algoliaResult.length)).map((_, index) => (
|
||||
|
||||
<Grid item xs={2} sm={4} md={4} key={index}>
|
||||
<a href={algoliaResult[0]["objectID"]} style={link}>
|
||||
<Item>
|
||||
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="column" style={{ float: "left" }}>
|
||||
<img src={algoliaResult[0]["image_url"]} alt="shuffle" width="50px" />
|
||||
</div>
|
||||
<div class="column " style={boxdata}>
|
||||
<div style={boxdata}>
|
||||
<Typography align="left" variant="body1">
|
||||
{algoliaResult[0]["name"]}
|
||||
</Typography>
|
||||
</div>
|
||||
<div style={boxdata}>
|
||||
<Typography align="left" variant="body2">
|
||||
{algoliaResult[0]["description"].substring(0, 20)}
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</Item>
|
||||
</a>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
</Box>
|
||||
<Grid item xl={8} style={{ "border": "20px" }}>
|
||||
|
||||
<Typography type="title" variant="h6">
|
||||
Most Popular
|
||||
</Typography>
|
||||
<div style={{
|
||||
paddingLeft: "50px",
|
||||
|
||||
}}>
|
||||
</div>
|
||||
|
||||
</Grid>
|
||||
<Box sx={{ flexGrow: 1 }}>
|
||||
<Grid
|
||||
container
|
||||
spacing={{ xs: 1, md: 3 }}
|
||||
columns={{ xs: 4, sm: 8, md: 12 }}
|
||||
>
|
||||
{Array.from(Array(3)).map((_, index) => (
|
||||
<Grid item xs={2} sm={4} md={4} key={index}>
|
||||
<a href="#" style={link}>
|
||||
<Item>
|
||||
<div class="row">
|
||||
<div class="column" style={{ float: "left" }}>
|
||||
<img src="/images/testing.png" alt="shuffle" width="50px" />
|
||||
</div>
|
||||
<div class="column " style={boxdata}>
|
||||
<div style={boxdata}>
|
||||
<Typography align="left" variant="body1">
|
||||
App Name
|
||||
</Typography>
|
||||
</div>
|
||||
<div style={boxdata}>
|
||||
<Typography align="left" variant="body2">
|
||||
Description
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</Item>
|
||||
</a>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
</Box>
|
||||
|
||||
<Grid item xl={8} style={{ "border": "20px" }}>
|
||||
|
||||
<Typography type="title" variant="h6">
|
||||
Brand New
|
||||
</Typography>
|
||||
<div style={{
|
||||
paddingLeft: "50px",
|
||||
|
||||
}}>
|
||||
</div>
|
||||
|
||||
</Grid>
|
||||
<Box sx={{ flexGrow: 1 }}>
|
||||
<Grid
|
||||
container
|
||||
spacing={{ xs: 1, md: 3 }}
|
||||
columns={{ xs: 4, sm: 8, md: 12 }}
|
||||
>
|
||||
{Array.from(Array(3)).map((_, index) => (
|
||||
<Grid item xs={2} sm={4} md={4} key={index}>
|
||||
<a href="#" style={link}>
|
||||
<Item>
|
||||
<div class="row">
|
||||
<div class="column" style={{ float: "left" }}>
|
||||
<img src="/images/testing.png" alt="shuffle" width="50px" />
|
||||
</div>
|
||||
<div class="column " style={boxdata}>
|
||||
<div style={boxdata}>
|
||||
<Typography align="left" variant="body1">
|
||||
App Name
|
||||
</Typography>
|
||||
</div>
|
||||
<div style={boxdata}>
|
||||
<Typography align="left" variant="body2">
|
||||
Description
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</Item>
|
||||
</a>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
</Box>
|
||||
|
||||
|
||||
<Box sx={{ flexGrow: 1 }}>
|
||||
<Grid
|
||||
container
|
||||
spacing={{ xs: 1, md: 3 }}
|
||||
columns={{ xs: 4, sm: 8, md: 12 }}
|
||||
>
|
||||
{Array.from(Array(3)).map((_, index) => (
|
||||
<Grid item xs={2} sm={4} md={4} key={index}>
|
||||
|
||||
<div className="row" >
|
||||
<div className="column" style={{ float: "left", width: "33.33%", marginTop: "20px", marginBottom: "20px", }}>
|
||||
<img src="/images/shuffle_logo.png" alt="shuffle" width="200px" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
</Box>
|
||||
|
||||
<Grid item xl={8} style={{ "border": "20px" }}>
|
||||
|
||||
<Typography type="title" variant="h6">
|
||||
Hybrid work
|
||||
</Typography>
|
||||
<div style={{
|
||||
paddingLeft: "50px",
|
||||
|
||||
}}>
|
||||
</div>
|
||||
|
||||
</Grid>
|
||||
|
||||
<Box sx={{ flexGrow: 1 }}>
|
||||
<Grid
|
||||
container
|
||||
spacing={{ xs: 1, md: 3 }}
|
||||
columns={{ xs: 4, sm: 8, md: 12 }}
|
||||
>
|
||||
{Array.from(Array(3)).map((_, index) => (
|
||||
<Grid item xs={2} sm={4} md={4} key={index}>
|
||||
<a href="#" style={link}>
|
||||
<Item>
|
||||
<div class="row">
|
||||
<div class="column" style={{ float: "left" }}>
|
||||
<img src="/images/testing.png" alt="shuffle" width="50px" />
|
||||
</div>
|
||||
<div class="column " style={boxdata}>
|
||||
<div style={boxdata}>
|
||||
<Typography align="left" variant="body1">
|
||||
App Name
|
||||
</Typography>
|
||||
</div>
|
||||
<div style={boxdata}>
|
||||
<Typography align="left" variant="body2">
|
||||
Description
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</Item>
|
||||
</a>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
</Box>
|
||||
|
||||
<div className="row" style={{ display: "flex" }}>
|
||||
<div className="col" style={{ width: "40%", marginTop: "50px" }}>
|
||||
<Typography variant="h6">Don't see it? Build it!</Typography>
|
||||
<Typography variant="body2">Use our APIs to create an app that makes your working life better.And maybe even share it with the world.</Typography>
|
||||
<a href="#" target="_blank" rel="nonref"
|
||||
style={{
|
||||
background: "#FF4500",
|
||||
borderRadius: "3.125rem",
|
||||
color: "#fff",
|
||||
display: "block",
|
||||
fontSize: ".9375rem",
|
||||
fontWeight: "500",
|
||||
height: "1rem",
|
||||
letterSpacing: "-.02em",
|
||||
lineHeight: ".875rem",
|
||||
marginTop: "1.5rem",
|
||||
padding: "1.3125rem 1.375rem",
|
||||
textAlign: "center",
|
||||
textDecoration: "none",
|
||||
width: "8.5rem"
|
||||
}}><span>visit developer portal</span></a>
|
||||
</div>
|
||||
<div className="col" style={{ float: "right" }}>
|
||||
<div className="row" style={{ float: "left", marginLeft: "90px" }}>
|
||||
<div className="column" style={{ float: "left", margin: "60px 10px 20px 30px" }}>
|
||||
<img src="/images/demo1.png" alt="shuffle" width="90px" />
|
||||
</div>
|
||||
<div className="column" style={{ float: "left", margin: "60px 10px 20px 30px" }}>
|
||||
<img src="/images/demo1.png" alt="shuffle" width="90px" />
|
||||
</div>
|
||||
<div className="column" style={{ float: "left", margin: "60px 10px 20px 30px" }}>
|
||||
<img src="/images/demo1.png" alt="shuffle" width="90px" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
);
|
||||
}
|
||||
export default AppExplorer;
|
||||
@@ -1,789 +0,0 @@
|
||||
import React from "react";
|
||||
import { Grid, Container, Divider, CardMedia, List, ListItem, ListItemText } from "@mui/material";
|
||||
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import Card from "@material-ui/core/Card";
|
||||
import CardContent from "@material-ui/core/CardContent";
|
||||
|
||||
import Table from "@material-ui/core/Table";
|
||||
import TableBody from "@material-ui/core/TableBody";
|
||||
import TableCell from "@material-ui/core/TableCell";
|
||||
import TableContainer from "@material-ui/core/TableContainer";
|
||||
import TableHead from "@material-ui/core/TableHead";
|
||||
import TableRow from "@material-ui/core/TableRow";
|
||||
import Paper from "@material-ui/core/Paper";
|
||||
|
||||
import { LineChart, LineSeries, BarChart } from "reaviz";
|
||||
import { Gridline, GridStripe } from "reaviz";
|
||||
import { GridlineSeries } from "reaviz";
|
||||
|
||||
import InputLabel from '@material-ui/core/InputLabel';
|
||||
import FormControl from '@material-ui/core/FormControl';
|
||||
import Select from '@material-ui/core/Select';
|
||||
|
||||
import { styled, alpha } from '@mui/material/styles';
|
||||
import AppBar from '@mui/material/AppBar';
|
||||
import Box from '@mui/material/Box';
|
||||
import Toolbar from '@mui/material/Toolbar';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import InputBase from '@mui/material/InputBase';
|
||||
import Badge from '@mui/material/Badge';
|
||||
import MenuItem from '@mui/material/MenuItem';
|
||||
import Menu from '@mui/material/Menu';
|
||||
import MenuIcon from '@mui/icons-material/Menu';
|
||||
import SearchIcon from '@mui/icons-material/Search';
|
||||
import AccountCircle from '@mui/icons-material/AccountCircle';
|
||||
import MailIcon from '@mui/icons-material/Mail';
|
||||
import NotificationsIcon from '@mui/icons-material/Notifications';
|
||||
import MoreIcon from '@mui/icons-material/MoreVert';
|
||||
import SearchField from "../components/Searchfield";
|
||||
import { SpaRounded } from "@material-ui/icons";
|
||||
import { isMobile } from "react-device-detect"
|
||||
|
||||
const data = [
|
||||
{
|
||||
key: new Date("11/29/2019"),
|
||||
data: 10,
|
||||
},
|
||||
{
|
||||
key: new Date("11/30/2019"),
|
||||
data: 14,
|
||||
},
|
||||
{
|
||||
key: new Date("12/01/2019"),
|
||||
data: 5,
|
||||
},
|
||||
{
|
||||
key: new Date("12/02/2019"),
|
||||
data: 18,
|
||||
},
|
||||
];
|
||||
|
||||
const useStyles1 = makeStyles((theme) => ({
|
||||
formControl: {
|
||||
margin: theme.spacing(1),
|
||||
minWidth: 120,
|
||||
},
|
||||
selectEmpty: {
|
||||
marginTop: theme.spacing(2),
|
||||
},
|
||||
}));
|
||||
|
||||
|
||||
const useStyles = makeStyles({
|
||||
table: {
|
||||
minWidth: 650,
|
||||
},
|
||||
root: {
|
||||
minWidth: 275,
|
||||
},
|
||||
bullet: {
|
||||
display: "inline-block",
|
||||
margin: "0 2px",
|
||||
transform: "scale(0.8)",
|
||||
},
|
||||
title: {
|
||||
fontSize: 14,
|
||||
},
|
||||
pos: {
|
||||
marginBottom: 12,
|
||||
},
|
||||
});
|
||||
|
||||
function createData(name, calories, fat, carbs, protein) {
|
||||
return { name, calories, fat, carbs, protein };
|
||||
}
|
||||
|
||||
const rows = [
|
||||
createData("Frozen yoghurt", 159, 6.0, 24, 4.0),
|
||||
createData("Ice cream sandwich", 237, 9.0, 37, 4.3),
|
||||
createData("Eclair", 262, 16.0, 24, 6.0),
|
||||
createData("Cupcake", 305, 3.7, 67, 4.3),
|
||||
createData("Gingerbread", 356, 16.0, 49, 3.9),
|
||||
];
|
||||
|
||||
const Search = styled('div')(({ theme }) => ({
|
||||
position: 'relative',
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
backgroundColor: alpha(theme.palette.common.white, 0.15),
|
||||
'&:hover': {
|
||||
backgroundColor: alpha(theme.palette.common.white, 0.25),
|
||||
},
|
||||
marginRight: theme.spacing(2),
|
||||
marginLeft: 0,
|
||||
width: '100%',
|
||||
[theme.breakpoints.up('sm')]: {
|
||||
marginLeft: theme.spacing(3),
|
||||
width: 'auto',
|
||||
},
|
||||
}));
|
||||
|
||||
const SearchIconWrapper = styled('div')(({ theme }) => ({
|
||||
padding: theme.spacing(0, 2),
|
||||
height: '100%',
|
||||
position: 'absolute',
|
||||
pointerEvents: 'none',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}));
|
||||
|
||||
const StyledInputBase = styled(InputBase)(({ theme }) => ({
|
||||
color: 'inherit',
|
||||
'& .MuiInputBase-input': {
|
||||
padding: theme.spacing(1, 1, 1, 0),
|
||||
// vertical padding + font size from searchIcon
|
||||
paddingLeft: `calc(1em + ${theme.spacing(4)})`,
|
||||
transition: theme.transitions.create('width'),
|
||||
width: '100%',
|
||||
[theme.breakpoints.up('md')]: {
|
||||
width: '20ch',
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
function PrimarySearchAppBar() {
|
||||
const [anchorEl, setAnchorEl] = React.useState(null);
|
||||
const [mobileMoreAnchorEl, setMobileMoreAnchorEl] = React.useState(null);
|
||||
|
||||
const isMenuOpen = Boolean(anchorEl);
|
||||
const isMobileMenuOpen = Boolean(mobileMoreAnchorEl);
|
||||
|
||||
const handleProfileMenuOpen = (event) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
};
|
||||
|
||||
const handleMobileMenuClose = () => {
|
||||
setMobileMoreAnchorEl(null);
|
||||
};
|
||||
|
||||
const handleMenuClose = () => {
|
||||
setAnchorEl(null);
|
||||
handleMobileMenuClose();
|
||||
};
|
||||
|
||||
const handleMobileMenuOpen = (event) => {
|
||||
setMobileMoreAnchorEl(event.currentTarget);
|
||||
};
|
||||
|
||||
const menuId = 'primary-search-account-menu';
|
||||
const renderMenu = (
|
||||
<Menu
|
||||
anchorEl={anchorEl}
|
||||
anchorOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
id={menuId}
|
||||
keepMounted
|
||||
transformOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
open={isMenuOpen}
|
||||
onClose={handleMenuClose}
|
||||
>
|
||||
<MenuItem onClick={handleMenuClose}>Profile</MenuItem>
|
||||
<MenuItem onClick={handleMenuClose}>My account</MenuItem>
|
||||
</Menu>
|
||||
);
|
||||
|
||||
const mobileMenuId = 'primary-search-account-menu-mobile';
|
||||
const renderMobileMenu = (
|
||||
<Menu
|
||||
anchorEl={mobileMoreAnchorEl}
|
||||
anchorOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
id={mobileMenuId}
|
||||
keepMounted
|
||||
transformOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
open={isMobileMenuOpen}
|
||||
onClose={handleMobileMenuClose}
|
||||
>
|
||||
<MenuItem>
|
||||
<IconButton size="large" aria-label="show 4 new mails" color="inherit">
|
||||
<Badge badgeContent={4} color="error">
|
||||
<MailIcon />
|
||||
</Badge>
|
||||
</IconButton>
|
||||
<p>Messages</p>
|
||||
</MenuItem>
|
||||
<MenuItem>
|
||||
<IconButton
|
||||
size="large"
|
||||
aria-label="show 17 new notifications"
|
||||
color="inherit"
|
||||
>
|
||||
<Badge badgeContent={17} color="error">
|
||||
<NotificationsIcon />
|
||||
</Badge>
|
||||
</IconButton>
|
||||
<p>Notifications</p>
|
||||
</MenuItem>
|
||||
<MenuItem onClick={handleProfileMenuOpen}>
|
||||
<IconButton
|
||||
size="large"
|
||||
aria-label="account of current user"
|
||||
aria-controls="primary-search-account-menu"
|
||||
aria-haspopup="true"
|
||||
color="inherit"
|
||||
>
|
||||
<AccountCircle />
|
||||
</IconButton>
|
||||
<p>Profile</p>
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
);
|
||||
|
||||
return (
|
||||
<Box sx={{ flexGrow: 1 }}>
|
||||
<AppBar position="fixed" style={{ backgroundColor: "black", boxShadow: "unset" }}>
|
||||
<Toolbar>
|
||||
<img src="/images/Shuffle_logo.png" style={{ height: "3rem", width: "3rem" }} alt="shuffle img" />
|
||||
<SearchField />
|
||||
{/* <Box sx={{ flexGrow: 1 }} /> */}
|
||||
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
{renderMobileMenu}
|
||||
{renderMenu}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
const AppHub = () => {
|
||||
const classes = useStyles();
|
||||
const classes1 = useStyles1();
|
||||
|
||||
const [usecases, setUsecases] = React.useState([
|
||||
{
|
||||
"name": "1. Collect",
|
||||
"color": "#c51152",
|
||||
"list": [
|
||||
{
|
||||
"name": "Email management",
|
||||
"priority": 100,
|
||||
"type": "communication",
|
||||
"items": {
|
||||
"name": "Release a quarantined message",
|
||||
"items": {}
|
||||
},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "EDR to ticket",
|
||||
"priority": 100,
|
||||
"type": "edr",
|
||||
"items": {
|
||||
"name": "Get host information",
|
||||
"items": {}
|
||||
},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "SIEM to ticket",
|
||||
"priority": 100,
|
||||
"type": "siem",
|
||||
"description": "Ensure tickets are forwarded to the correct destination. Alternatively add enrichment on it's way there.",
|
||||
"video": "https://www.youtube.com/watch?v=FBISHA7V15c&t=197s&ab_channel=OpenSecure",
|
||||
"blogpost": "https://medium.com/shuffle-automation/introducing-shuffle-an-open-source-soar-platform-part-1-58a529de7d12",
|
||||
"reference_image": "/images/detectionframework.png",
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "2-way Ticket synchronization",
|
||||
"priority": 90,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "ChatOps",
|
||||
"priority": 70,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Threat Intel received",
|
||||
"priority": 50,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Assign tickets",
|
||||
"priority": 30,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Firewall alerts",
|
||||
"priority": 90,
|
||||
"items": {
|
||||
"name": "URL filtering",
|
||||
"items": {}
|
||||
},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "IDS/IPS alerts",
|
||||
"priority": 90,
|
||||
"items": {
|
||||
"name": "Manage policies",
|
||||
"items": {}
|
||||
},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Deduplicate information",
|
||||
"priority": 70,
|
||||
"items": {},
|
||||
"matches": []
|
||||
}
|
||||
],
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "2. Enrich",
|
||||
"color": "#f4c20d",
|
||||
"list": [
|
||||
{
|
||||
"name": "Internal Enrichment",
|
||||
"priority": 100,
|
||||
"items": {
|
||||
"name": "...",
|
||||
"items": {}
|
||||
},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "External historical Enrichment",
|
||||
"priority": 90,
|
||||
"items": {
|
||||
"name": "...",
|
||||
"items": {}
|
||||
},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Realtime",
|
||||
"priority": 50,
|
||||
"items": {
|
||||
"name": "Analyze screenshots",
|
||||
"items": {}
|
||||
},
|
||||
"matches": []
|
||||
}
|
||||
],
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "3. Detect",
|
||||
"color": "#3cba54",
|
||||
"list": [
|
||||
{
|
||||
"name": "Search SIEM (Sigma)",
|
||||
"priority": 90,
|
||||
"items": {
|
||||
"name": "Endpoint",
|
||||
"items": {}
|
||||
},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Search EDR (OSQuery)",
|
||||
"priority": 90,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Search emails (Sublime)",
|
||||
"priority": 90,
|
||||
"items": {
|
||||
"name": "Check headers and IOCs",
|
||||
"items": {}
|
||||
},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Search IOCs (ioc-finder)",
|
||||
"priority": 50,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Search files (Yara)",
|
||||
"priority": 50,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Memory Analysis (Volatility)",
|
||||
"priority": 50,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "IDS & IPS (Snort/Surricata)",
|
||||
"priority": 50,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Validate old tickets",
|
||||
"priority": 50,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Honeypot access",
|
||||
"priority": 50,
|
||||
"items": {
|
||||
"name": "...",
|
||||
"items": {}
|
||||
},
|
||||
"matches": []
|
||||
}
|
||||
],
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "4. Respond",
|
||||
"color": "#4885ed",
|
||||
"list": [
|
||||
{
|
||||
"name": "Eradicate malware",
|
||||
"priority": 90,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Quarantine host(s)",
|
||||
"priority": 90,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Block IPs, URLs, Domains and Hashes",
|
||||
"priority": 90,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Trigger scans",
|
||||
"priority": 50,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Update indicators (FW, EDR, SIEM...)",
|
||||
"priority": 50,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Autoblock activity when threat intel is received",
|
||||
"priority": 50,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Lock/Delete/Reset account",
|
||||
"priority": 50,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Lock vault",
|
||||
"priority": 50,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Increase authentication",
|
||||
"priority": 50,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Get policies from assets",
|
||||
"priority": 50,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Run ansible scripts",
|
||||
"priority": 50,
|
||||
"items": {},
|
||||
"matches": []
|
||||
}
|
||||
],
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "5. Verify",
|
||||
"color": "#7f00ff",
|
||||
"list": [
|
||||
{
|
||||
"name": "Discover vulnerabilities",
|
||||
"priority": 80,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Discover assets",
|
||||
"priority": 80,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Ensure policies are followed",
|
||||
"priority": 80,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Find Inactive users",
|
||||
"priority": 50,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Botnet tracker",
|
||||
"priority": 50,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Ensure access rights match HR systems",
|
||||
"priority": 50,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Ensure onboarding is followed",
|
||||
"priority": 50,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Third party apps in SaaS",
|
||||
"priority": 50,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Devices used for your cloud account",
|
||||
"priority": 50,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Too much access in GCP/Azure/AWS/ other clouds",
|
||||
"priority": 50,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Certificate validation",
|
||||
"priority": 50,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Domain investigation with LetsEncrypt",
|
||||
"priority": 50,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Monitor new DNS entries for domain with passive DNS",
|
||||
"priority": 50,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Monitor and track password dumps",
|
||||
"priority": 50,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Monitor for mentions of domain on darknet sites",
|
||||
"priority": 50,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Reporting",
|
||||
"priority": 50,
|
||||
"items": {
|
||||
"name": "Monthly reports",
|
||||
"items": {
|
||||
"name": "...",
|
||||
"items": {}
|
||||
}
|
||||
},
|
||||
"matches": []
|
||||
}
|
||||
],
|
||||
"matches": []
|
||||
}
|
||||
]);
|
||||
|
||||
const SideBar = {
|
||||
minWidth: 250,
|
||||
maxWidth: 300,
|
||||
borderRight: "1px solid rgba(255,255,255,0.3)",
|
||||
left: 0,
|
||||
position: "sticky",
|
||||
minHeight: "90vh",
|
||||
maxHeight: "90vh",
|
||||
overflowX: "hidden",
|
||||
overflowY: "auto",
|
||||
zIndex: 1000,
|
||||
color: "black"
|
||||
};
|
||||
|
||||
const [age, setAge] = React.useState(0);
|
||||
|
||||
const handleChange = (event) => {
|
||||
setAge(event.target.value);
|
||||
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Card>
|
||||
<CardContent style={{ padding: 0 }}>
|
||||
<div style={{
|
||||
background: "url('/images/home-header-bg.png')", height: "450px", backgroundSize: "cover",
|
||||
backgroundRepeat: "no-repeat",
|
||||
backgroundPosition: "center",
|
||||
position: "relative"
|
||||
}}>
|
||||
<div style={{ width: "95%", margin: "auto", position: "relative", height: "450px" }}>
|
||||
<div>
|
||||
<PrimarySearchAppBar />
|
||||
</div>
|
||||
<div style={{
|
||||
position: "absolute",
|
||||
bottom: "10%",
|
||||
display: "flex",
|
||||
alignItems: "flex-end",
|
||||
justifyContent: "space-between",
|
||||
width: "100%"
|
||||
}}>
|
||||
<div>
|
||||
<img src="/images/Shuffle_logo.png" style={{ height: "4rem", width: "4rem" }} alt="shuffle img" />
|
||||
<Typography type="title" variant="h1" color="#ef5d29">
|
||||
SHUFFLE
|
||||
</Typography>
|
||||
</div>
|
||||
<div>
|
||||
<SearchField />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<div style={{ display: "flex" }}>
|
||||
<div style={SideBar}>
|
||||
<List>
|
||||
<ListItem >
|
||||
<ListItemText>
|
||||
<span style={{ fontSize: "25px", fontFamily: "revert", fontWeight: "bold" }}>Categories</span>
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
<span></span>
|
||||
<Divider />
|
||||
<ListItem>
|
||||
<ListItemText>
|
||||
<span style={{ fontSize: "25px", fontFamily: "revert" }}>Workflows</span>
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<ListItemText>
|
||||
<span style={{ fontSize: "25px", fontFamily: "revert" }}>Apps</span>
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<ListItemText>
|
||||
<span style={{ fontSize: "25px", fontFamily: "revert" }}>Docs</span>
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
</List>
|
||||
</div>
|
||||
<div style={{ padding: "20px", width: "100%" }}>
|
||||
<Typography type="title" variant="h2" color="black">
|
||||
Workflow
|
||||
</Typography>
|
||||
<div style={{ width: "100%", minHeight: isMobile ? 0 : 71, maxHeight: isMobile ? 0 : 71, }}>
|
||||
{!isMobile && usecases !== null && usecases !== undefined && usecases.length > 0 ?
|
||||
<div style={{ display: "flex", }}>
|
||||
<Grid container spacing={2}>
|
||||
{usecases.map((usecase, index) => {
|
||||
//console.log(usecase)
|
||||
return (
|
||||
<Grid item xs={4}>
|
||||
<Paper
|
||||
key={usecase.name}
|
||||
style={{
|
||||
flex: 1,
|
||||
backgroundColor: "transparent",
|
||||
marginRight: index === usecases.length - 1 ? 0 : 10,
|
||||
cursor: "pointer",
|
||||
overflow: "hidden",
|
||||
padding: 10,
|
||||
border: "0.0625rem solid #b2b2b2",
|
||||
borderRadius: "1.5625rem",
|
||||
boxSizing: "content-box",
|
||||
cursor: "pointer",
|
||||
height: "70px",
|
||||
}}
|
||||
onClick={() => {
|
||||
console.log("clicked...")
|
||||
}}
|
||||
>
|
||||
<a href={`/usecases?selected=${usecase.name}`} rel="noopener noreferrer" target="_blank" style={{ textDecoration: "none", }}>
|
||||
<Typography variant="body1" color="textPrimary">
|
||||
{usecase.name}
|
||||
</Typography>
|
||||
<Typography variant="body2" color="textSecondary">
|
||||
In use: {usecase.matches.length}/{usecase.list.length}
|
||||
</Typography>
|
||||
</a>
|
||||
</Paper>
|
||||
</Grid>
|
||||
)
|
||||
})}
|
||||
</Grid>
|
||||
</div>
|
||||
: null}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Card>
|
||||
<CardContent style={{ padding: 0 }}>
|
||||
<Typography type="title" variant="h3" color="#ffffff" style={{
|
||||
backgroundColor: "black", padding: "10px", height: "200px",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center"
|
||||
}}>
|
||||
footer
|
||||
</Typography>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AppHub;
|
||||
@@ -1,268 +0,0 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { Grid, Container, Divider, CardMedia, List, ListItem, ListItemText } from "@mui/material";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
|
||||
import theme from '../theme';
|
||||
import {isMobile} from "react-device-detect";
|
||||
import AppGrid1 from "../components/AppGrid1.jsx"
|
||||
import WorkflowGrid from "../components/WorkflowGrid.jsx"
|
||||
import CreatorGrid from "../components/CreatorGrid.jsx"
|
||||
import DocsGrid from "../components/DocsGrid.jsx"
|
||||
import Button from '@mui/material/Button';
|
||||
import { useNavigate, Link } from "react-router-dom";
|
||||
|
||||
import {
|
||||
Tabs,
|
||||
Paper,
|
||||
Tab,
|
||||
} from "@material-ui/core";
|
||||
|
||||
import {
|
||||
Business as BusinessIcon,
|
||||
Apps as AppsIcon,
|
||||
Polymer as PolymerIcon,
|
||||
EmojiObjects as EmojiObjectsIcon,
|
||||
Description as DescriptionIcon,
|
||||
} from "@material-ui/icons";
|
||||
|
||||
|
||||
const bodyDivStyle = {
|
||||
margin: "auto",
|
||||
maxWidth: 1024,
|
||||
scrollX: "hidden",
|
||||
overflowX: "hidden",
|
||||
}
|
||||
|
||||
// Should be different if logged in :|
|
||||
const Appdemo = (props) => {
|
||||
const { globalUrl, isLoaded, serverside, userdata, hidemargins, } = props;
|
||||
const [appCategory,setAppCategory] = useState();
|
||||
let navigate = useNavigate();
|
||||
|
||||
const [curTab, setCurTab] = useState(0);
|
||||
const iconStyle = { marginRight: 10 };
|
||||
|
||||
useEffect(() => {
|
||||
if (serverside !== true && 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) {
|
||||
for (var key in Object.keys(views)) {
|
||||
const value = views[key]
|
||||
console.log(key, value)
|
||||
if (value === foundTab) {
|
||||
setConfig("", key)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
|
||||
if (serverside === true) {
|
||||
return null
|
||||
}
|
||||
|
||||
const boxStyle = {
|
||||
color: "white",
|
||||
flex: "1",
|
||||
marginLeft: 10,
|
||||
marginRight: 10,
|
||||
paddingLeft: 30,
|
||||
paddingRight: 30,
|
||||
paddingBottom: 30,
|
||||
paddingTop: hidemargins === true ? 0 : 30,
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
overflowX: "hidden",
|
||||
minHeight: 400,
|
||||
}
|
||||
const NoArguments_NoReturn = () => {
|
||||
|
||||
alert('Function Called...');
|
||||
|
||||
}
|
||||
const SideBar = {
|
||||
minWidth: 250,
|
||||
maxWidth: 300,
|
||||
borderRight: "1px solid rgba(255,255,255,0.3)",
|
||||
left: 0,
|
||||
position: "sticky",
|
||||
minHeight: "90vh",
|
||||
maxHeight: "90vh",
|
||||
overflowX: "hidden",
|
||||
overflowY: "auto",
|
||||
zIndex: 1000,
|
||||
color: "white"
|
||||
};
|
||||
const catItems = (
|
||||
<div style={SideBar}>
|
||||
<List>
|
||||
<ListItem >
|
||||
<ListItemText>
|
||||
<span><Typography variant="primary">Categories</Typography></span>
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
<span></span>
|
||||
<Divider />
|
||||
<ListItem>
|
||||
<ListItemText>
|
||||
<Button id="ASSETS" variant="primary" onClick={()=>{setAppCategory("assets")}}>
|
||||
ASSETS
|
||||
</Button>
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<ListItemText>
|
||||
<Button variant="primary" onClick={()=>{setAppCategory("cases")}}>
|
||||
CASES
|
||||
</Button>
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<ListItemText>
|
||||
<Button variant="primary" onClick={()=>{setAppCategory("comms")}}>
|
||||
COMMS
|
||||
</Button>
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<ListItemText>
|
||||
<Button variant="primary" onClick={()=>{setAppCategory("edr av")}}>
|
||||
EDR & AV
|
||||
</Button>
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<ListItemText>
|
||||
<Button variant="primary" onClick={()=>{setAppCategory("iam")}}>
|
||||
IAM
|
||||
</Button>
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<ListItemText>
|
||||
<Button variant="primary" onClick={()=>{setAppCategory("intel")}}>
|
||||
INTEL
|
||||
</Button>
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<ListItemText>
|
||||
<Button variant="primary" onClick={()=>{setAppCategory("network")}}>
|
||||
NETWORK
|
||||
</Button>
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<ListItemText>
|
||||
<Button variant="primary" onClick={()=>{setAppCategory("siem")}}>
|
||||
SIEM
|
||||
</Button>
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
</List>
|
||||
</div>
|
||||
)
|
||||
|
||||
const views = {
|
||||
0: "apps",
|
||||
1: "workflows",
|
||||
2: "docs",
|
||||
3: "creators",
|
||||
}
|
||||
|
||||
const setConfig = (event, inputValue) => {
|
||||
const newValue = parseInt(inputValue)
|
||||
|
||||
setCurTab(newValue)
|
||||
if (newValue === 0) {
|
||||
document.title = "Shuffle - search - apps";
|
||||
} else if (newValue === 1) {
|
||||
document.title = "Shuffle - search - workflows";
|
||||
} else if (newValue === 2) {
|
||||
document.title = "Shuffle - search - documentation";
|
||||
} else if (newValue === 3) {
|
||||
document.title = "Shuffle - search - creators";
|
||||
} else {
|
||||
document.title = "Shuffle - search";
|
||||
}
|
||||
|
||||
|
||||
const urlSearchParams = new URLSearchParams(window.location.search)
|
||||
const params = Object.fromEntries(urlSearchParams.entries())
|
||||
const foundQuery = params["q"]
|
||||
var extraQ = ""
|
||||
if (foundQuery !== null && foundQuery !== undefined) {
|
||||
extraQ = "&q="+foundQuery
|
||||
}
|
||||
|
||||
|
||||
if ((serverside === false || serverside === undefined) && window.location.pathname.includes("/search")) {
|
||||
navigate(`/search?tab=${views[newValue]}`+extraQ)
|
||||
}
|
||||
}
|
||||
|
||||
if (isLoaded === false) {
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
// Random names for type & autoComplete. Didn't research :^)
|
||||
const landingpageDataBrowser =
|
||||
<div style={{paddingBottom: hidemargins === true ? 0 : 100, color: "white", backgroundColor: theme.palette.surfacColor}}>
|
||||
<div style={boxStyle}>
|
||||
<Tabs
|
||||
style={{width: 610, margin: "auto", marginTop: hidemargins === true ? 0 : 25, }}
|
||||
value={curTab}
|
||||
indicatorColor="primary"
|
||||
textColor="secondary"
|
||||
onChange={setConfig}
|
||||
aria-label="disabled tabs example"
|
||||
>
|
||||
<Tab
|
||||
label=<span>
|
||||
<AppsIcon style={iconStyle} /> Apps
|
||||
</span>
|
||||
/>
|
||||
|
||||
</Tabs>
|
||||
{curTab === 0 ?
|
||||
<AppGrid1 maxRows={3} showSuggestion={true} globalUrl={globalUrl} isMobile={isMobile} userdata={userdata} searchValue={appCategory} key={appCategory} />
|
||||
:
|
||||
curTab === 1 ?
|
||||
window.location.pathname === "/search" ?
|
||||
<WorkflowGrid maxRows={3} showSuggestion={true} globalUrl={globalUrl} isMobile={isMobile} userdata={userdata} />
|
||||
:
|
||||
<WorkflowGrid maxRows={3} showSuggestion={true} globalUrl={globalUrl} isMobile={isMobile} userdata={userdata} />
|
||||
:
|
||||
curTab === 2 ?
|
||||
<DocsGrid maxRows={6} parsedXs={12} showSuggestion={true} globalUrl={globalUrl} isMobile={isMobile} userdata={userdata} />
|
||||
:
|
||||
curTab === 3 ?
|
||||
<CreatorGrid parsedXs={4} showSuggestion={true} globalUrl={globalUrl} isMobile={isMobile} userdata={userdata} />
|
||||
:
|
||||
null}
|
||||
</div>
|
||||
</div>
|
||||
//{/*alternativeView={true} />*/}
|
||||
|
||||
const loadedCheck = isLoaded ?
|
||||
<div>
|
||||
<div style={bodyDivStyle}>{landingpageDataBrowser}</div>
|
||||
</div>
|
||||
:
|
||||
<div>
|
||||
</div>
|
||||
|
||||
// #1f2023?
|
||||
return(
|
||||
<div style={{backgroundColor: "#1f2023", display: "flex"}}>
|
||||
{catItems}
|
||||
{loadedCheck}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Appdemo;
|
||||
+41
-46
@@ -59,7 +59,8 @@ import algoliasearch from 'algoliasearch/lite';
|
||||
|
||||
import YAML from "yaml";
|
||||
import { useNavigate, Link, useParams } from "react-router-dom";
|
||||
import { useAlert } from "react-alert";
|
||||
//import { useAlert
|
||||
import { ToastContainer, toast } from "react-toastify"
|
||||
import Dropzone from "../components/Dropzone.jsx";
|
||||
|
||||
const surfaceColor = "#27292D";
|
||||
@@ -272,7 +273,7 @@ const Apps = (props) => {
|
||||
|
||||
//const [workflows, setWorkflows] = React.useState([]);
|
||||
const baseRepository = "https://github.com/frikky/shuffle-apps";
|
||||
const alert = useAlert();
|
||||
//const alert = useAlert();
|
||||
let navigate = useNavigate();
|
||||
|
||||
const [selectedApp, setSelectedApp] = React.useState({});
|
||||
@@ -454,7 +455,7 @@ const Apps = (props) => {
|
||||
//}, 5000)
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
setIsLoading(false);
|
||||
});
|
||||
};
|
||||
@@ -462,7 +463,7 @@ const Apps = (props) => {
|
||||
const downloadApp = (inputdata) => {
|
||||
const id = inputdata.id;
|
||||
|
||||
alert.info("Downloading..");
|
||||
toast("Downloading..");
|
||||
fetch(globalUrl + "/api/v1/apps/" + id + "/config", {
|
||||
method: "GET",
|
||||
headers: {
|
||||
@@ -480,7 +481,7 @@ const Apps = (props) => {
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (!responseJson.success) {
|
||||
alert.error("Failed to download file");
|
||||
toast("Failed to download file");
|
||||
} else {
|
||||
console.log(responseJson);
|
||||
const basedata = atob(responseJson.openapi);
|
||||
@@ -538,7 +539,7 @@ const Apps = (props) => {
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1152,7 +1153,7 @@ const Apps = (props) => {
|
||||
<Select
|
||||
value={sharingConfiguration}
|
||||
onChange={(event) => {
|
||||
alert.info("Changing sharing to " + event.target.value);
|
||||
toast("Changing sharing to " + event.target.value);
|
||||
|
||||
setSharingConfiguration(event.target.value);
|
||||
|
||||
@@ -1479,7 +1480,7 @@ const Apps = (props) => {
|
||||
try {
|
||||
reader.readAsText(files[0]);
|
||||
} catch (error) {
|
||||
alert.error("Failed to read file");
|
||||
toast("Failed to read file");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1578,9 +1579,9 @@ const Apps = (props) => {
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success === false) {
|
||||
alert.error("Failed to activate the app")
|
||||
toast("Failed to activate the app")
|
||||
} else {
|
||||
alert.success("App activated for your organization! Refresh the page to use the app.")
|
||||
toast("App activated for your organization! Refresh the page to use the app.")
|
||||
|
||||
if (refresh === true) {
|
||||
getApps()
|
||||
@@ -1588,7 +1589,7 @@ const Apps = (props) => {
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
//alert.error(error.toString())
|
||||
//toast(error.toString())
|
||||
console.log("Activate app error: ", error.toString())
|
||||
});
|
||||
}
|
||||
@@ -1692,13 +1693,13 @@ const Apps = (props) => {
|
||||
return (
|
||||
<div style={{ textDecoration: "none", color: "white", }} onClick={(event) => {
|
||||
//if (!isCloud) {
|
||||
// alert.info("Since this is an on-prem instance. You will need to activate the app yourself. Opening link to download it in a new window.")
|
||||
// toast("Since this is an on-prem instance. You will need to activate the app yourself. Opening link to download it in a new window.")
|
||||
// setTimeout(() => {
|
||||
// event.preventDefault()
|
||||
// window.open(parsedUrl, '_blank')
|
||||
// }, 2000)
|
||||
//} else {
|
||||
alert.info(`Activating ${name}`)
|
||||
toast(`Activating ${name}`)
|
||||
//}
|
||||
|
||||
console.log("CLICK: ", hit)
|
||||
@@ -1867,12 +1868,6 @@ const Apps = (props) => {
|
||||
style={{ backgroundColor: inputColor, borderRadius: 5 }}
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
minHeight: "50px",
|
||||
marginLeft: "5px",
|
||||
maxWidth: "95%",
|
||||
fontSize: "1em",
|
||||
borderRadius: 5,
|
||||
},
|
||||
}}
|
||||
disabled={
|
||||
@@ -2016,7 +2011,7 @@ const Apps = (props) => {
|
||||
|
||||
parsedData["force_update"] = forceUpdate;
|
||||
|
||||
alert.success("Getting specific apps from your URL.");
|
||||
toast("Getting specific apps from your URL.");
|
||||
var cors = "cors";
|
||||
fetch(globalUrl + "/api/v1/apps/get_existing", {
|
||||
method: "POST",
|
||||
@@ -2029,7 +2024,7 @@ const Apps = (props) => {
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status === 200) {
|
||||
alert.success("Loaded existing apps!");
|
||||
toast("Loaded existing apps!");
|
||||
}
|
||||
|
||||
//stop()
|
||||
@@ -2040,12 +2035,12 @@ const Apps = (props) => {
|
||||
.then((responseJson) => {
|
||||
console.log("DATA: ", responseJson);
|
||||
if (responseJson.reason !== undefined) {
|
||||
alert.error("Failed loading: " + responseJson.reason);
|
||||
toast("Failed loading: " + responseJson.reason);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log("ERROR: ", error.toString());
|
||||
//alert.error(error.toString());
|
||||
//toast(error.toString());
|
||||
//stop()
|
||||
|
||||
setIsLoading(false);
|
||||
@@ -2055,7 +2050,7 @@ const Apps = (props) => {
|
||||
|
||||
// Locally hotloads app from folder
|
||||
const hotloadApps = () => {
|
||||
alert.info("Hotloading apps from location in .env");
|
||||
toast("Hotloading apps from location in .env");
|
||||
setIsLoading(true);
|
||||
fetch(globalUrl + "/api/v1/apps/run_hotload", {
|
||||
mode: "cors",
|
||||
@@ -2067,7 +2062,7 @@ const Apps = (props) => {
|
||||
.then((response) => {
|
||||
setIsLoading(false);
|
||||
if (response.status === 200) {
|
||||
//alert.success("Hotloaded apps!")
|
||||
//toast("Hotloaded apps!")
|
||||
getApps();
|
||||
}
|
||||
|
||||
@@ -2075,14 +2070,14 @@ const Apps = (props) => {
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success === true) {
|
||||
alert.info("Successfully finished hotload");
|
||||
toast("Successfully finished hotload");
|
||||
} else {
|
||||
alert.error("Failed hotload: ", responseJson.reason);
|
||||
toast("Failed hotload: ", responseJson.reason);
|
||||
//(responseJson.reason !== undefined && responseJson.reason.length > 0) {
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
@@ -2106,7 +2101,7 @@ const Apps = (props) => {
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
@@ -2119,9 +2114,9 @@ const Apps = (props) => {
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status === 200) {
|
||||
//alert.success("Successfully GOT app "+appId)
|
||||
//toast("Successfully GOT app "+appId)
|
||||
} else {
|
||||
alert.error("Failed getting app");
|
||||
toast("Failed getting app");
|
||||
}
|
||||
|
||||
return response.json();
|
||||
@@ -2144,7 +2139,7 @@ const Apps = (props) => {
|
||||
responseJson.loop_versions = selectedApp.loop_versions;
|
||||
}
|
||||
|
||||
//alert.info("Should set app to selected")
|
||||
//toast("Should set app to selected")
|
||||
if (
|
||||
responseJson.actions !== undefined &&
|
||||
responseJson.actions !== null &&
|
||||
@@ -2158,12 +2153,12 @@ const Apps = (props) => {
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
const deleteApp = (appId) => {
|
||||
alert.info("Attempting to delete app");
|
||||
toast("Attempting to delete app");
|
||||
fetch(globalUrl + "/api/v1/apps/" + appId, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
@@ -2173,16 +2168,16 @@ const Apps = (props) => {
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status === 200) {
|
||||
alert.success("Successfully deleted app");
|
||||
toast("Successfully deleted app");
|
||||
setTimeout(() => {
|
||||
getApps();
|
||||
}, 1000);
|
||||
} else {
|
||||
alert.error("Failed deleting app. Does it still exist?");
|
||||
toast("Failed deleting app. Does it still exist?");
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
@@ -2206,19 +2201,19 @@ const Apps = (props) => {
|
||||
})
|
||||
.then((responseJson) => {
|
||||
//console.log(responseJson)
|
||||
//alert.info(responseJson)
|
||||
//toast(responseJson)
|
||||
if (responseJson.success) {
|
||||
alert.success("Successfully updated app configuration");
|
||||
toast("Successfully updated app configuration");
|
||||
} else {
|
||||
if (responseJson.reason !== undefined && responseJson.reason !== null) {
|
||||
alert.error("Error: "+responseJson.reason);
|
||||
toast("Error: "+responseJson.reason);
|
||||
} else {
|
||||
alert.error("Error updating app configuration");
|
||||
toast("Error updating app configuration");
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
@@ -2249,7 +2244,7 @@ const Apps = (props) => {
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
@@ -2287,7 +2282,7 @@ const Apps = (props) => {
|
||||
validateOpenApi(responseJson);
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
setOpenApiError(error.toString());
|
||||
});
|
||||
};
|
||||
@@ -2344,12 +2339,12 @@ const Apps = (props) => {
|
||||
if (responseJson.reason !== undefined) {
|
||||
setOpenApiError(responseJson.reason);
|
||||
}
|
||||
alert.error("An error occurred in the response");
|
||||
toast("An error occurred in the response");
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
setValidation(false);
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
setOpenApiError(error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,343 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
import { BrowserView, MobileView } from "react-device-detect";
|
||||
import theme from '../theme.jsx';
|
||||
|
||||
import Paper from '@material-ui/core/Paper';
|
||||
import Button from '@material-ui/core/Button';
|
||||
|
||||
import TextField from '@material-ui/core/TextField';
|
||||
|
||||
|
||||
const bodyDivStyle = {
|
||||
margin: "auto",
|
||||
textAlign: "center",
|
||||
width: "900px",
|
||||
}
|
||||
|
||||
|
||||
// Should be different if logged in :|
|
||||
const Contact = (props) => {
|
||||
const { globalUrl, isLoaded } = props;
|
||||
|
||||
const boxStyle = {
|
||||
flex: "1",
|
||||
marginLeft: "10px",
|
||||
marginRight: "10px",
|
||||
paddingLeft: "30px",
|
||||
paddingRight: "30px",
|
||||
paddingBottom: "30px",
|
||||
paddingTop: "30px",
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
display: "flex",
|
||||
flexDirection: "column"
|
||||
}
|
||||
|
||||
const bodyTextStyle = {
|
||||
color: "#ffffff",
|
||||
}
|
||||
|
||||
const [firstname, setFirstname] = useState("");
|
||||
const [lastname, setLastname] = useState("");
|
||||
const [title, setTitle] = useState("");
|
||||
const [companyname, setCompanyname] = useState("");
|
||||
const [email, setEmail] = useState("");
|
||||
const [phone, setPhone] = useState("");
|
||||
const [message, setMessage] = useState("");
|
||||
|
||||
const [formMessage, setFormMessage] = useState("");
|
||||
|
||||
const submitContact = () => {
|
||||
const data = {
|
||||
"firstname": firstname,
|
||||
"lastname": lastname,
|
||||
"title": title,
|
||||
"companyname": companyname,
|
||||
"email": email,
|
||||
"phone": phone,
|
||||
"message": message,
|
||||
}
|
||||
console.log(data)
|
||||
|
||||
fetch(globalUrl + "/api/v1/contact", {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(response => {
|
||||
if (response.success === true) {
|
||||
setFormMessage(response.message)
|
||||
} else {
|
||||
setFormMessage("Something went wrong. Please contact frikky@shuffler.io.")
|
||||
}
|
||||
console.log(response)
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error)
|
||||
});
|
||||
}
|
||||
|
||||
// Random names for type & autoComplete. Didn't research :^)
|
||||
const landingpageDataBrowser =
|
||||
<div>
|
||||
<div style={bodyTextStyle}>
|
||||
<h3 style={{ color: "#f85a3e" }}>Contact us</h3>
|
||||
<h2>Lets talk!</h2>
|
||||
</div>
|
||||
<div style={{ display: "flex" }}>
|
||||
<Paper style={boxStyle}>
|
||||
<h2>Contact Details</h2>
|
||||
<div style={{ flex: "1", display: "flex", flexDirection: "row" }}>
|
||||
<TextField
|
||||
required
|
||||
style={{ flex: "1", marginRight: "15px", backgroundColor: theme.palette.inputColor }}
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
color="primary"
|
||||
fullWidth={true}
|
||||
placeholder="First Name"
|
||||
type="firstname"
|
||||
id="standard-required"
|
||||
autoComplete="firstname"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
onChange={e => setFirstname(e.target.value)}
|
||||
/>
|
||||
<TextField
|
||||
style={{ flex: "1", marginLeft: "15px", backgroundColor: theme.palette.inputColor }}
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
color="primary"
|
||||
fullWidth={true}
|
||||
placeholder="Last Name"
|
||||
type="lastname"
|
||||
id="standard"
|
||||
autoComplete="lastname"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
onChange={e => setLastname(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ flex: "1", display: "flex", flexDirection: "row" }}>
|
||||
<TextField
|
||||
style={{ flex: "1", marginRight: "15px", backgroundColor: theme.palette.inputColor }}
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
color="primary"
|
||||
fullWidth={true}
|
||||
placeholder="Job Title"
|
||||
type="jobtitle"
|
||||
id="standard-required"
|
||||
autoComplete="jobtitle"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
onChange={e => setTitle(e.target.value)}
|
||||
/>
|
||||
<TextField
|
||||
style={{ flex: "1", marginLeft: "15px", backgroundColor: theme.palette.inputColor }}
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
color="primary"
|
||||
fullWidth={true}
|
||||
type="companyname"
|
||||
placeholder="Company Name"
|
||||
id="standard-required"
|
||||
autoComplete="companyname"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
onChange={e => setCompanyname(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ flex: "1", display: "flex", flexDirection: "row" }}>
|
||||
<TextField
|
||||
required
|
||||
style={{ flex: "1", marginRight: "15px", backgroundColor: theme.palette.inputColor }}
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
color="primary"
|
||||
fullWidth={true}
|
||||
placeholder="Email"
|
||||
type="email"
|
||||
id="standard-required"
|
||||
autoComplete="email"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
onChange={e => setEmail(e.target.value)}
|
||||
/>
|
||||
<TextField
|
||||
style={{ flex: "1", marginLeft: "15px", backgroundColor: theme.palette.inputColor }}
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
color="primary"
|
||||
fullWidth={true}
|
||||
type="phone"
|
||||
placeholder="Phone number"
|
||||
id="standard-required"
|
||||
autoComplete="phone"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
onChange={e => setPhone(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ flex: 1 }}>
|
||||
<h2>Message</h2>
|
||||
</div>
|
||||
<div style={{ flex: 4 }}>
|
||||
<TextField
|
||||
multiline
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
color="primary"
|
||||
style={{ flex: "1", backgroundColor: theme.palette.inputColor }}
|
||||
rows="6"
|
||||
fullWidth={true}
|
||||
placeholder="What can we help you with?"
|
||||
id="filled-multiline-static"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
onChange={e => setMessage(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
disabled={email.length <= 0 || message.length <= 0}
|
||||
style={{ width: "100%", height: "60px", marginTop: "10px" }}
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={submitContact}
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
<h3>{formMessage}</h3>
|
||||
</Paper>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
const landingpageDataMobile =
|
||||
<div style={{ paddingBottom: "50px" }}>
|
||||
<div style={{ color: "white", textAlign: "center" }}>
|
||||
<h3 style={{ color: "#f85a3e" }}>Contact us</h3>
|
||||
<h2>Lets talk!</h2>
|
||||
</div>
|
||||
<div style={{ display: "flex" }}>
|
||||
<Paper style={boxStyle}>
|
||||
<h2>Contact Details</h2>
|
||||
<div style={{ flex: "1", display: "flex", flexDirection: "row" }}>
|
||||
<TextField
|
||||
required
|
||||
style={{ flex: "1", backgroundColor: theme.palette.inputColor }}
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
color="primary"
|
||||
fullWidth={true}
|
||||
placeholder="Name"
|
||||
type="firstname"
|
||||
id="standard-required"
|
||||
autoComplete="firstname"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
onChange={e => setFirstname(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ flex: "1", display: "flex", flexDirection: "row" }}>
|
||||
<TextField
|
||||
required
|
||||
style={{ flex: "1", backgroundColor: theme.palette.inputColor }}
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
color="primary"
|
||||
fullWidth={true}
|
||||
placeholder="Email"
|
||||
type="email"
|
||||
id="standard-required"
|
||||
autoComplete="email"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
onChange={e => setEmail(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ flex: 1 }}>
|
||||
<h2>Message</h2>
|
||||
</div>
|
||||
<div style={{ flex: 4 }}>
|
||||
<TextField
|
||||
multiline
|
||||
style={{ flex: "1", backgroundColor: theme.palette.inputColor }}
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
color="primary"
|
||||
rows="6"
|
||||
fullWidth={true}
|
||||
placeholder="What can we help you with?"
|
||||
id="filled-multiline-static"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
onChange={e => setMessage(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
disabled={email.length <= 0 || message.length <= 0}
|
||||
style={{ width: "100%", height: "60px", marginTop: "10px" }}
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={submitContact}
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
<h3>{formMessage}</h3>
|
||||
</Paper>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
const loadedCheck = isLoaded ?
|
||||
<div>
|
||||
<BrowserView>
|
||||
<div style={bodyDivStyle}>{landingpageDataBrowser}</div>
|
||||
</BrowserView>
|
||||
<MobileView>
|
||||
{landingpageDataMobile}
|
||||
</MobileView>
|
||||
</div>
|
||||
:
|
||||
<div>
|
||||
</div>
|
||||
|
||||
return (
|
||||
<div>
|
||||
{loadedCheck}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default Contact;
|
||||
@@ -9,7 +9,8 @@ import { useNavigate, Link, useParams } from "react-router-dom";
|
||||
|
||||
// react plugin used to create charts
|
||||
//import { Line, Bar } from "react-chartjs-2";
|
||||
import { useAlert } from "react-alert";
|
||||
//import { useAlert
|
||||
import { ToastContainer, toast } from "react-toastify"
|
||||
|
||||
import {
|
||||
Autocomplete,
|
||||
@@ -83,7 +84,6 @@ const useStyles = makeStyles({
|
||||
},
|
||||
inputRoot: {
|
||||
color: "white",
|
||||
// This matches the specificity of the default styles at https://github.com/mui-org/material-ui/blob/v4.11.3/packages/material-ui-lab/src/Autocomplete/Autocomplete.js#L90
|
||||
"&:hover .MuiOutlinedInput-notchedOutline": {
|
||||
borderColor: "#f86a3e",
|
||||
},
|
||||
@@ -156,7 +156,7 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLogged
|
||||
}, 100);
|
||||
})
|
||||
.catch((error) => {
|
||||
//alert.error(error.toString());
|
||||
//toast(error.toString());
|
||||
setInputUsecase({})
|
||||
setExpandedIndex(index)
|
||||
setExpandedItem(subindex)
|
||||
@@ -213,16 +213,16 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLogged
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success === false) {
|
||||
if (responseJson.reason !== undefined) {
|
||||
//alert.error("Failed updating: " + responseJson.reason)
|
||||
//toast("Failed updating: " + responseJson.reason)
|
||||
} else {
|
||||
//alert.error("Failed to update framework for your org.")
|
||||
//toast("Failed to update framework for your org.")
|
||||
}
|
||||
} else {
|
||||
//alert.info("Updated usecase.")
|
||||
//toast("Updated usecase.")
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
//alert.error(error.toString());
|
||||
//toast(error.toString());
|
||||
//setFrameworkLoaded(true)
|
||||
})
|
||||
}
|
||||
@@ -250,9 +250,9 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLogged
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success === false) {
|
||||
if (responseJson.reason !== undefined) {
|
||||
alert.error("Error updating workflow: ", responseJson.reason)
|
||||
toast("Error updating workflow: ", responseJson.reason)
|
||||
} else {
|
||||
alert.error("Error updating workflow.")
|
||||
toast("Error updating workflow.")
|
||||
}
|
||||
|
||||
return
|
||||
@@ -261,7 +261,7 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLogged
|
||||
return responseJson;
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error("Problem setting workflow: ", error.toString());
|
||||
toast("Problem setting workflow: ", error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1026,7 +1026,7 @@ const RadialChart = ({keys, setSelectedCategory}) => {
|
||||
// What data do we fill in here? Idk
|
||||
const Dashboard = (props) => {
|
||||
const { globalUrl, isLoggedIn } = props;
|
||||
const alert = useAlert();
|
||||
//const alert = useAlert();
|
||||
const [bigChartData, setBgChartData] = useState("data1");
|
||||
const [dayAmount, setDayAmount] = useState(7);
|
||||
const [firstRequest, setFirstRequest] = useState(true);
|
||||
@@ -1118,16 +1118,16 @@ const Dashboard = (props) => {
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success === false) {
|
||||
if (responseJson.reason !== undefined) {
|
||||
//alert.error("Failed loading: " + responseJson.reason)
|
||||
//toast("Failed loading: " + responseJson.reason)
|
||||
} else {
|
||||
//alert.error("Failed to load framework for your org.")
|
||||
//toast("Failed to load framework for your org.")
|
||||
}
|
||||
} else {
|
||||
setFrameworkData(responseJson)
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1158,7 +1158,7 @@ const Dashboard = (props) => {
|
||||
})
|
||||
.catch((error) => {
|
||||
fetchUsecases()
|
||||
//alert.error(error.toString());
|
||||
//toast(error.toString());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1258,7 +1258,7 @@ const Dashboard = (props) => {
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
//alert.error("ERROR: " + error.toString());
|
||||
//toast("ERROR: " + error.toString());
|
||||
console.log("ERROR: " + error.toString());
|
||||
});
|
||||
};
|
||||
@@ -1292,7 +1292,7 @@ const Dashboard = (props) => {
|
||||
setChangeme(stats_id);
|
||||
})
|
||||
.catch((error) => {
|
||||
//alert.error("ERROR: " + error.toString());
|
||||
//toast("ERROR: " + error.toString());
|
||||
console.log("ERROR: " + error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
@@ -8,7 +8,8 @@ import { useNavigate, Link, useParams } from "react-router-dom";
|
||||
|
||||
// react plugin used to create charts
|
||||
//import { Line, Bar } from "react-chartjs-2";
|
||||
import { useAlert } from "react-alert";
|
||||
//import { useAlert
|
||||
import { ToastContainer, toast } from "react-toastify"
|
||||
import Draggable from "react-draggable";
|
||||
|
||||
import {
|
||||
@@ -93,7 +94,6 @@ const useStyles = makeStyles({
|
||||
},
|
||||
inputRoot: {
|
||||
color: "white",
|
||||
// This matches the specificity of the default styles at https://github.com/mui-org/material-ui/blob/v4.11.3/packages/material-ui-lab/src/Autocomplete/Autocomplete.js#L90
|
||||
"&:hover .MuiOutlinedInput-notchedOutline": {
|
||||
borderColor: "#f86a3e",
|
||||
},
|
||||
@@ -333,7 +333,7 @@ const RadialChart = ({keys, setSelectedCategory}) => {
|
||||
// What data do we fill in here? Idk
|
||||
const Dashboard = (props) => {
|
||||
const { globalUrl, isLoggedIn } = props;
|
||||
const alert = useAlert();
|
||||
//const alert = useAlert();
|
||||
const [bigChartData, setBgChartData] = useState("data1");
|
||||
const [dayAmount, setDayAmount] = useState(7);
|
||||
const [firstRequest, setFirstRequest] = useState(true);
|
||||
@@ -428,9 +428,9 @@ const Dashboard = (props) => {
|
||||
console.log("Resp: ", responseJson)
|
||||
if (responseJson.success === false) {
|
||||
if (responseJson.reason !== undefined) {
|
||||
//alert.error("Failed loading: " + responseJson.reason)
|
||||
//toast("Failed loading: " + responseJson.reason)
|
||||
} else {
|
||||
//alert.error("Failed to load framework for your org.")
|
||||
//toast("Failed to load framework for your org.")
|
||||
}
|
||||
} else {
|
||||
var tmpdata = responseJson
|
||||
@@ -453,7 +453,7 @@ const Dashboard = (props) => {
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
//alert.error(error.toString());
|
||||
//toast(error.toString());
|
||||
})
|
||||
}
|
||||
|
||||
@@ -505,7 +505,7 @@ const Dashboard = (props) => {
|
||||
setChangeme(stats_id);
|
||||
})
|
||||
.catch((error) => {
|
||||
//alert.error("ERROR: " + error.toString());
|
||||
//toast("ERROR: " + error.toString());
|
||||
console.log("ERROR: " + error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
@@ -436,7 +436,7 @@ const Docs = (defaultprops) => {
|
||||
href={selectedMeta.link}
|
||||
style={{ textDecoration: "none", color: "#f85a3e" }}
|
||||
>
|
||||
<Button style={{}} variant="outlined">
|
||||
<Button style={{}} variant="outlined" color="secondary">
|
||||
<EditIcon /> Edit
|
||||
</Button>
|
||||
</a>
|
||||
@@ -792,17 +792,21 @@ const Docs = (defaultprops) => {
|
||||
<div id="markdown_wrapper_outer" style={markdownStyle}>
|
||||
<ReactMarkdown
|
||||
components={{
|
||||
link: OuterLink,
|
||||
image: Img,
|
||||
img: Img,
|
||||
code: CodeHandler,
|
||||
heading: Heading,
|
||||
h1: Heading,
|
||||
h2: Heading,
|
||||
h3: Heading,
|
||||
h4: Heading,
|
||||
h5: Heading,
|
||||
h6: Heading,
|
||||
a: OuterLink,
|
||||
}}
|
||||
id="markdown_wrapper"
|
||||
escapeHtml={false}
|
||||
style={{
|
||||
maxWidth: "100%", minWidth: "100%",
|
||||
}}
|
||||
remarkPlugins={[remarkGfm]}
|
||||
>
|
||||
{data}
|
||||
</ReactMarkdown>
|
||||
@@ -811,6 +815,7 @@ const Docs = (defaultprops) => {
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
// remarkPlugins={[remarkGfm]}
|
||||
|
||||
const mobileStyle = {
|
||||
color: "white",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,393 +0,0 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
|
||||
import Button from "@material-ui/core/Button";
|
||||
import Paper from "@material-ui/core/Paper";
|
||||
import Divider from "@material-ui/core/Divider";
|
||||
import Select from "@material-ui/core/Select";
|
||||
import MenuItem from "@material-ui/core/MenuItem";
|
||||
|
||||
import WebhookImage from "../assets/img/webhook.png";
|
||||
import KafkaImage from "../assets/img/kafka.png";
|
||||
|
||||
import EditWorkflow from "./EditWorkflow";
|
||||
|
||||
const EditWebhook = (props) => {
|
||||
const { globalUrl, isLoaded } = props;
|
||||
|
||||
// FIXME
|
||||
//const [webhookData, setWebhookData] = useState(webhooktest)
|
||||
const [webhookData, setWebhookData] = useState({});
|
||||
const [workflows, setWorkflows] = useState([]);
|
||||
const [firstrequest, setFirstrequest] = React.useState(true);
|
||||
|
||||
const [selectedWorkflows, setSelectedWorkflows] = useState([]);
|
||||
|
||||
const getWorkflows = () => {
|
||||
fetch(globalUrl + "/api/v1/workflows", {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
console.log("Status not 200 for workflows :O!");
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
setWorkflows(responseJson);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
});
|
||||
};
|
||||
|
||||
const setWebhook = (inputdata) => {
|
||||
console.log(inputdata);
|
||||
|
||||
fetch(globalUrl + "/api/v1/hooks/" + props.match.params.key, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
credentials: "include",
|
||||
body: JSON.stringify(inputdata),
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then((responseJson) => {
|
||||
console.log(responseJson);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
});
|
||||
};
|
||||
|
||||
const getCurrentWebhook = () => {
|
||||
fetch(globalUrl + "/api/v1/hooks/" + props.match.params.key, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
console.log("Status not 200!");
|
||||
window.location.pathname = "webhooks";
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson.actions === null) {
|
||||
responseJson.actions = [];
|
||||
}
|
||||
|
||||
if (responseJson.transforms === null) {
|
||||
responseJson.transforms = [];
|
||||
}
|
||||
|
||||
setWebhookData(responseJson);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
//window.location.pathname = "webhooks"
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (firstrequest) {
|
||||
setFirstrequest(false);
|
||||
getCurrentWebhook();
|
||||
if (workflows.length <= 0) {
|
||||
getWorkflows();
|
||||
}
|
||||
}
|
||||
|
||||
// After everything is loaded
|
||||
if (
|
||||
Object.getOwnPropertyNames(webhookData).length > 0 &&
|
||||
webhookData.actions.length > 0 &&
|
||||
workflows.length > 0 &&
|
||||
selectedWorkflows.length === 0
|
||||
) {
|
||||
// Setting startup actions. making like this in case we want other actions
|
||||
var tmpActionWorkflows = [];
|
||||
for (var key in webhookData.actions) {
|
||||
if (webhookData.actions[key].type === "workflow") {
|
||||
tmpActionWorkflows.push(webhookData.actions[key]);
|
||||
}
|
||||
}
|
||||
|
||||
// Fix duplicates... Meh
|
||||
var foundWorkflowIds = [];
|
||||
var tmpWorkflows = [];
|
||||
for (key in tmpActionWorkflows) {
|
||||
if (foundWorkflowIds.includes(tmpActionWorkflows[key].id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (var subkey in workflows) {
|
||||
if (tmpActionWorkflows[key].id === workflows[subkey]["id_"]) {
|
||||
console.log(tmpActionWorkflows[key].id, workflows[subkey]["id_"]);
|
||||
foundWorkflowIds.push(tmpActionWorkflows[key].id);
|
||||
tmpWorkflows.push(workflows[subkey]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tmpWorkflows.length > 0) {
|
||||
setSelectedWorkflows(tmpWorkflows);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const hookPicture =
|
||||
Object.getOwnPropertyNames(webhookData).length > 0 &&
|
||||
webhookData.type === "webhook" ? (
|
||||
<img src={WebhookImage} alt="webhook" width="100px" height="100px" />
|
||||
) : (
|
||||
<img src={KafkaImage} alt="MQ" width="100px" height="100px" />
|
||||
);
|
||||
|
||||
const executeHook = (action) => {
|
||||
fetch(
|
||||
globalUrl + "/api/v1/hooks/" + props.match.params.key + "/" + action,
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
credentials: "include",
|
||||
}
|
||||
)
|
||||
.then((response) => response.json())
|
||||
.then((responseJson) => {
|
||||
setWebhookData({});
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
});
|
||||
};
|
||||
|
||||
const headerPaperStyle = {
|
||||
display: "flex",
|
||||
maxHeight: "800px",
|
||||
minHeight: "800px",
|
||||
margin: "10px 30px 10px 10px",
|
||||
padding: "10px 5px 5px 5px",
|
||||
flexDirection: "column",
|
||||
};
|
||||
|
||||
// FIXME - add with counter to change the correct one (not just edit)
|
||||
const addNewWorkflow = (event) => {
|
||||
// Verify if it already exists in the array. Returns if it exists
|
||||
for (var key in selectedWorkflows) {
|
||||
var item = selectedWorkflows[key];
|
||||
if (item["id_"] === event.target.value["id_"]) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME - make this possible for all accounts
|
||||
if (selectedWorkflows.length === 0) {
|
||||
console.log("ADD FIRST ITEM FOR SELECTEDWORKFLOWS");
|
||||
console.log(event.target.value);
|
||||
|
||||
// Cleanup previous actions
|
||||
var newActions = [];
|
||||
if (webhookData.actions.length > 0) {
|
||||
for (key in webhookData.actions) {
|
||||
if (
|
||||
webhookData.actions[key].type === "" ||
|
||||
webhookData.actions[key].type === undefined
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
newActions.push(webhookData.actions[key]);
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME - how to stringify this better hurr
|
||||
var formattedWorkflow = {
|
||||
type: "workflow",
|
||||
name: event.target.value.name,
|
||||
id: event.target.value.id_,
|
||||
field: "",
|
||||
};
|
||||
|
||||
// FIXME: patch this n
|
||||
newActions.push(formattedWorkflow);
|
||||
console.log(newActions);
|
||||
|
||||
webhookData.actions = newActions;
|
||||
setWebhook(webhookData);
|
||||
}
|
||||
|
||||
var tmpSelectedWorkflows = [].concat(selectedWorkflows, [
|
||||
event.target.value,
|
||||
]);
|
||||
setSelectedWorkflows(tmpSelectedWorkflows);
|
||||
};
|
||||
|
||||
// FIXME
|
||||
// Create a list with + button
|
||||
// For each, choose the new workflow I wanna add
|
||||
// Current: JUST ONE
|
||||
const selectedWorkflowIds = selectedWorkflows.map((data) => {
|
||||
return data["id_"];
|
||||
});
|
||||
const availableWorkflows = workflows.filter(
|
||||
(data) => !selectedWorkflowIds.includes(data["id_"])
|
||||
);
|
||||
|
||||
const WorkflowSelect = (counter) => {
|
||||
if (selectedWorkflows[counter.counter] === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
console.log(selectedWorkflows[0]);
|
||||
console.log(selectedWorkflows[0]);
|
||||
console.log(selectedWorkflows[0]);
|
||||
console.log(selectedWorkflows[counter.counter]);
|
||||
console.log(selectedWorkflows[counter.counter].name);
|
||||
return (
|
||||
<div>
|
||||
Workflow select:
|
||||
<Select
|
||||
value={selectedWorkflows[counter.counter].name}
|
||||
onChange={(event) => {
|
||||
addNewWorkflow(event, counter.counter);
|
||||
}}
|
||||
displayEmpty
|
||||
name="workflow"
|
||||
>
|
||||
{availableWorkflows.map((data) => (
|
||||
<MenuItem key={data.name} value={data} name={data.name}>
|
||||
{data.name}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const extraWorkflow =
|
||||
workflows.length > 0 && availableWorkflows.length > 0 ? (
|
||||
<WorkflowSelect counter={selectedWorkflows.length} />
|
||||
) : null;
|
||||
|
||||
const multiWorkflowSelect =
|
||||
workflows.length > 0 && selectedWorkflows.length > 0 ? (
|
||||
<div>
|
||||
{selectedWorkflows.map((data, count) => (
|
||||
<WorkflowSelect key={count} counter={count} />
|
||||
))}
|
||||
{extraWorkflow}
|
||||
</div>
|
||||
) : (
|
||||
<WorkflowSelect counter={0} />
|
||||
);
|
||||
|
||||
const headerInfo =
|
||||
Object.getOwnPropertyNames(webhookData).length > 0 ? (
|
||||
<div>
|
||||
<Paper style={headerPaperStyle}>
|
||||
<div style={{ display: "flex", flex: "1" }}>
|
||||
<div style={{ flex: "1" }}>{hookPicture}</div>
|
||||
<div
|
||||
style={{ display: "flex", flexDirection: "column", flex: "5" }}
|
||||
>
|
||||
<div style={{ flex: "1" }}>
|
||||
<h1>Name: {webhookData.info.name}</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ flex: "4" }}>
|
||||
Description: {webhookData.info.description}
|
||||
<div>Id: {webhookData.id}</div>
|
||||
<div>Url: {webhookData.info.url}</div>
|
||||
<div>Type: {webhookData.type}</div>
|
||||
<div>Status: {webhookData.status}</div>
|
||||
<div>
|
||||
CHOOSE ACTIONS:
|
||||
{multiWorkflowSelect}
|
||||
</div>
|
||||
</div>
|
||||
<Divider />
|
||||
<div style={{ flex: "1", display: "flex", flexDirection: "row" }}>
|
||||
<div style={{ flex: "1" }}>
|
||||
<Button
|
||||
disabled={
|
||||
webhookData.running === true && webhookData.name !== ""
|
||||
}
|
||||
onClick={() => {
|
||||
executeHook("start");
|
||||
}}
|
||||
style={{
|
||||
left: "50%",
|
||||
top: "50%",
|
||||
transform: "translate(-50%, -50%)",
|
||||
}}
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
>
|
||||
Start {webhookData.type}
|
||||
</Button>
|
||||
</div>
|
||||
<div style={{ flex: "1" }}>
|
||||
<Button
|
||||
disabled={webhookData.running === false}
|
||||
onClick={() => {
|
||||
executeHook("stop");
|
||||
}}
|
||||
style={{
|
||||
left: "50%",
|
||||
top: "50%",
|
||||
transform: "translate(-50%, -50%)",
|
||||
}}
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
>
|
||||
Stop {webhookData.type}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Paper>
|
||||
</div>
|
||||
) : null;
|
||||
|
||||
// FIXME - needs refresh every time you add a new workflow
|
||||
const workflowdata =
|
||||
Object.getOwnPropertyNames(webhookData).length > 0 &&
|
||||
selectedWorkflows.length > 0 ? (
|
||||
<EditWorkflow
|
||||
globalUrl={globalUrl}
|
||||
inputworkflows={selectedWorkflows}
|
||||
inputname={webhookData.info.name}
|
||||
inputtype={webhookData.type}
|
||||
/>
|
||||
) : null;
|
||||
|
||||
const loadedCheck = isLoaded ? (
|
||||
<div style={{ display: "flex", backgroundColor: "#f7f7f7" }}>
|
||||
<div style={{ flex: 1 }}>{workflowdata}</div>
|
||||
<div style={{ flex: 1 }}>{headerInfo}</div>
|
||||
</div>
|
||||
) : (
|
||||
<div></div>
|
||||
);
|
||||
|
||||
// FIXME: Use this for testing
|
||||
// <EditWorkflow globalUrl={globalUrl} inputname={"Helo?"} inputtype={"webhook"} /> : null
|
||||
return <div>{loadedCheck}</div>;
|
||||
};
|
||||
|
||||
export default EditWebhook;
|
||||
@@ -1,118 +0,0 @@
|
||||
/* eslint-disable react/no-multi-comp */
|
||||
import React, { useState } from "react";
|
||||
|
||||
import TextField from "@material-ui/core/TextField";
|
||||
import Button from "@material-ui/core/Button";
|
||||
import Paper from "@material-ui/core/Paper";
|
||||
|
||||
const bodyDivStyle = {
|
||||
margin: "auto",
|
||||
marginTop: "100px",
|
||||
width: "500px",
|
||||
};
|
||||
|
||||
const ForgotPassword = (props) => {
|
||||
const { globalUrl, isLoaded, isLoggedIn, surfaceColor, inputColor } = props;
|
||||
|
||||
const boxStyle = {
|
||||
paddingLeft: "30px",
|
||||
paddingRight: "30px",
|
||||
paddingBottom: "30px",
|
||||
paddingTop: "30px",
|
||||
backgroundColor: surfaceColor,
|
||||
};
|
||||
|
||||
const [username, setUsername] = useState("");
|
||||
const [resetInfo, setResetInfo] = useState(
|
||||
"You will receive an email with instructions shortly."
|
||||
);
|
||||
|
||||
const handleValidateForm = () => {
|
||||
return username.length > 3;
|
||||
};
|
||||
|
||||
if (isLoggedIn === true) {
|
||||
window.location.pathname = "/";
|
||||
}
|
||||
|
||||
const onSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
// FIXME - add some check here ROFL
|
||||
|
||||
// Just use this one?
|
||||
var data = { username: username };
|
||||
var baseurl = globalUrl;
|
||||
var url = baseurl + "/api/v1/passwordresetmail";
|
||||
fetch(url, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(data),
|
||||
headers: {
|
||||
"Content-Type": "application/json; charset=utf-8",
|
||||
},
|
||||
})
|
||||
.then((response) =>
|
||||
response.json().then((responseJson) => {
|
||||
if (responseJson["success"] === false) {
|
||||
setResetInfo(responseJson["reason"]);
|
||||
}
|
||||
})
|
||||
)
|
||||
.catch((error) => {
|
||||
setResetInfo("Error in userdata: " + error);
|
||||
});
|
||||
};
|
||||
|
||||
const onChangeUser = (e) => {
|
||||
setUsername(e.target.value);
|
||||
};
|
||||
|
||||
const data = (
|
||||
<div style={bodyDivStyle}>
|
||||
<Paper style={boxStyle}>
|
||||
<form onSubmit={onSubmit} style={{ margin: "15px 15px 15px 15px" }}>
|
||||
<h2>Password reset</h2>
|
||||
<div>
|
||||
<TextField
|
||||
required
|
||||
fullWidth={true}
|
||||
color="primary"
|
||||
style={{ backgroundColor: inputColor }}
|
||||
InputProps={{
|
||||
style: {
|
||||
height: "50px",
|
||||
color: "white",
|
||||
fontSize: "1em",
|
||||
},
|
||||
}}
|
||||
type="username"
|
||||
placeholder="Username / Email"
|
||||
id="standard-required"
|
||||
autoComplete="username"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
onChange={onChangeUser}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ display: "flex", marginTop: "15px" }}>
|
||||
<Button
|
||||
color="primary"
|
||||
variant="contained"
|
||||
type="submit"
|
||||
style={{ flex: "1", marginRight: "5px" }}
|
||||
disabled={!handleValidateForm()}
|
||||
>
|
||||
SUBMIT
|
||||
</Button>
|
||||
</div>
|
||||
<div style={{ marginTop: "20px" }}>{resetInfo}</div>
|
||||
</form>
|
||||
</Paper>
|
||||
</div>
|
||||
);
|
||||
|
||||
const loadedCheck = isLoaded ? <div>{data}</div> : <div></div>;
|
||||
|
||||
return <div>{loadedCheck}</div>;
|
||||
};
|
||||
|
||||
export default ForgotPassword;
|
||||
@@ -1,136 +0,0 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
|
||||
import Paper from "@material-ui/core/Paper";
|
||||
import Button from "@material-ui/core/Button";
|
||||
|
||||
import TextField from "@material-ui/core/TextField";
|
||||
|
||||
const bodyDivStyle = {
|
||||
margin: "auto",
|
||||
textAlign: "center",
|
||||
width: "768px",
|
||||
};
|
||||
|
||||
const boxStyle = {
|
||||
flex: "1",
|
||||
marginLeft: "10px",
|
||||
marginRight: "10px",
|
||||
paddingLeft: "30px",
|
||||
paddingRight: "30px",
|
||||
paddingBottom: "30px",
|
||||
paddingTop: "30px",
|
||||
backgroundColor: "#e8eaf6",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
};
|
||||
|
||||
//const tmpdata = {
|
||||
// "username": "frikky",
|
||||
// "firstname": "fred",
|
||||
// "lastname": "ode",
|
||||
// "title": "topkek",
|
||||
// "companyname": "company here",
|
||||
// "email": "your email pls",
|
||||
// "phone": "PHONE!!",
|
||||
//}
|
||||
|
||||
// FIXME - add fetch for data fields
|
||||
// FIXME - remove tmpdata
|
||||
// FIXME: Use isLoggedIn :)
|
||||
const Settings = (props) => {
|
||||
const { globalUrl, isLoaded } = props;
|
||||
|
||||
const [newPassword, setNewPassword] = useState("");
|
||||
const [newPassword2, setNewPassword2] = useState("");
|
||||
const [passwordFormMessage, setPasswordFormMessage] = useState("");
|
||||
|
||||
const onPasswordChange = () => {
|
||||
const data = {
|
||||
newpassword: newPassword,
|
||||
newpassword2: newPassword2,
|
||||
reference: props.match.params.key,
|
||||
};
|
||||
const url = globalUrl + "/api/v1/passwordreset";
|
||||
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) {
|
||||
setPasswordFormMessage(responseJson["reason"]);
|
||||
}
|
||||
})
|
||||
)
|
||||
.catch((error) => {
|
||||
setPasswordFormMessage("Something went wrong.");
|
||||
});
|
||||
};
|
||||
|
||||
// This should "always" have data
|
||||
useEffect(() => {});
|
||||
|
||||
// Random names for type & autoComplete. Didn't research :^)
|
||||
const landingpageData = (
|
||||
<div style={{ display: "flex", marginTop: "80px" }}>
|
||||
<Paper style={boxStyle}>
|
||||
<h2>Password Reset</h2>
|
||||
<div style={{ flex: "1", display: "flex", flexDirection: "column" }}>
|
||||
<TextField
|
||||
required
|
||||
style={{ flex: "1" }}
|
||||
fullWidth={true}
|
||||
placeholder="New password"
|
||||
type="password"
|
||||
id="standard-required"
|
||||
autoComplete="password"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
onChange={(e) => setNewPassword(e.target.value)}
|
||||
/>
|
||||
<TextField
|
||||
required
|
||||
style={{ flex: "1" }}
|
||||
fullWidth={true}
|
||||
type="password"
|
||||
placeholder="Repeat new password"
|
||||
id="standard-required"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
onChange={(e) => setNewPassword2(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
disabled={
|
||||
newPassword.length < 10 ||
|
||||
newPassword2.length < 10 ||
|
||||
newPassword !== newPassword2
|
||||
}
|
||||
style={{ width: "100%", height: "60px", marginTop: "10px" }}
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={() => onPasswordChange()}
|
||||
>
|
||||
Submit password change
|
||||
</Button>
|
||||
<h3>{passwordFormMessage}</h3>
|
||||
</Paper>
|
||||
</div>
|
||||
);
|
||||
|
||||
const loadedCheck = isLoaded ? (
|
||||
<div style={bodyDivStyle}>{landingpageData}</div>
|
||||
) : (
|
||||
<div></div>
|
||||
);
|
||||
|
||||
return <div>{loadedCheck}</div>;
|
||||
};
|
||||
export default Settings;
|
||||
@@ -2,7 +2,8 @@ import React, { useEffect, useState } from 'react';
|
||||
import ReactDOM from "react-dom"
|
||||
|
||||
import AppFramework from "../components/AppFramework.jsx";
|
||||
import { useAlert } from "react-alert";
|
||||
//import { useAlert
|
||||
import { ToastContainer, toast } from "react-toastify"
|
||||
import { Link, useParams } from "react-router-dom";
|
||||
import theme from '../theme.jsx';
|
||||
|
||||
@@ -13,7 +14,7 @@ import {
|
||||
const Framework = (props) => {
|
||||
const {globalUrl, isLoaded, isLoggedIn, showOptions, selectedOption, rolling, } = props;
|
||||
|
||||
const alert = useAlert()
|
||||
//const alert = useAlert()
|
||||
const [frameworkLoaded, setFrameworkLoaded] = useState(false)
|
||||
const [frameworkData, setFrameworkData] = useState()
|
||||
|
||||
@@ -36,9 +37,9 @@ const Framework = (props) => {
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success === false) {
|
||||
if (responseJson.reason !== undefined) {
|
||||
alert.error("Failed loading: " + responseJson.reason)
|
||||
toast("Failed loading: " + responseJson.reason)
|
||||
} else {
|
||||
alert.error("Failed to load framework for your org.")
|
||||
toast("Failed to load framework for your org.")
|
||||
}
|
||||
|
||||
setFrameworkLoaded(true)
|
||||
@@ -51,7 +52,7 @@ const Framework = (props) => {
|
||||
})
|
||||
.catch((error) => {
|
||||
setFrameworkLoaded(true)
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -58,9 +58,7 @@ import {
|
||||
CloudDownload as CloudDownloadIcon,
|
||||
} from "@mui/icons-material";
|
||||
|
||||
//import {Search as SearchIcon, ArrowUpward as ArrowUpwardIcon, Visibility as VisibilityIcon, Done as DoneIcon, Close as CloseIcon, Error as ErrorIcon, FindReplace as FindreplaceIcon, ArrowLeft as ArrowLeftIcon, Cached as CachedIcon, DirectionsRun as DirectionsRunIcon, Add as AddIcon, Polymer as PolymerIcon, FormatListNumbered as FormatListNumberedIcon, Create as CreateIcon, PlayArrow as PlayArrowIcon, AspectRatio as AspectRatioIcon, MoreVert as MoreVertIcon, Apps as AppsIcon, Schedule as ScheduleIcon, FavoriteBorder as FavoriteBorderIcon, Pause as PauseIcon, Delete as DeleteIcon, AddCircleOutline as AddCircleOutlineIcon, Save as SaveIcon, KeyboardArrowLeft as KeyboardArrowLeftIcon, KeyboardArrowRight as KeyboardArrowRightIcon, ArrowBack as ArrowBackIcon, Settings as SettingsIcon, LockOpen as LockOpenIcon, ExpandMore as ExpandMoreIcon, VpnKey as VpnKeyIcon} from '@material-ui/icons';
|
||||
|
||||
//https://next.material-ui.com/components/material-icons/
|
||||
import { DataGrid, GridToolbar } from "@mui/x-data-grid";
|
||||
|
||||
//import JSONPretty from 'react-json-pretty';
|
||||
@@ -68,8 +66,9 @@ import { DataGrid, GridToolbar } from "@mui/x-data-grid";
|
||||
import Dropzone from "../components/Dropzone.jsx";
|
||||
|
||||
import { useNavigate, Link, useParams } from "react-router-dom";
|
||||
import { useAlert } from "react-alert";
|
||||
import ChipInput from "material-ui-chip-input";
|
||||
//import { useAlert
|
||||
import { ToastContainer, toast } from "react-toastify"
|
||||
import { MuiChipsInput } from "mui-chips-input";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
const inputColor = "#383B40";
|
||||
@@ -125,7 +124,7 @@ const GettingStarted = (props) => {
|
||||
const { globalUrl, isLoggedIn, isLoaded, userdata } = props;
|
||||
|
||||
document.title = "Getting Started with Shuffle";
|
||||
const alert = useAlert();
|
||||
//const alert = useAlert();
|
||||
const classes = useStyles(theme);
|
||||
let navigate = useNavigate();
|
||||
const imgSize = 60;
|
||||
@@ -444,7 +443,7 @@ const GettingStarted = (props) => {
|
||||
const files = isDropzone ? e.dataTransfer.files : e.target.files;
|
||||
|
||||
const reader = new FileReader();
|
||||
alert.info("Starting upload. Please wait while we validate the workflows");
|
||||
toast("Starting upload. Please wait while we validate the workflows");
|
||||
|
||||
try {
|
||||
reader.addEventListener("load", (e) => {
|
||||
@@ -453,7 +452,7 @@ const GettingStarted = (props) => {
|
||||
try {
|
||||
data = JSON.parse(reader.result);
|
||||
} catch (e) {
|
||||
alert.error("Invalid JSON: " + e);
|
||||
toast("Invalid JSON: " + e);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -481,13 +480,13 @@ const GettingStarted = (props) => {
|
||||
false
|
||||
).then((response) => {
|
||||
if (response !== undefined) {
|
||||
alert.success(`Successfully imported ${data.name}`);
|
||||
toast(`Successfully imported ${data.name}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error("Import error: " + error.toString());
|
||||
toast("Import error: " + error.toString());
|
||||
});
|
||||
});
|
||||
} catch (e) {
|
||||
@@ -520,7 +519,7 @@ const GettingStarted = (props) => {
|
||||
window.location.pathname = "/login";
|
||||
}
|
||||
|
||||
alert.info("Failed getting workflows.");
|
||||
toast("Failed getting workflows.");
|
||||
setWorkflowDone(true);
|
||||
|
||||
return;
|
||||
@@ -561,7 +560,7 @@ const GettingStarted = (props) => {
|
||||
}, 100)
|
||||
} else {
|
||||
if (isLoggedIn) {
|
||||
alert.error("An error occurred while loading workflows");
|
||||
toast("An error occurred while loading workflows");
|
||||
}
|
||||
|
||||
return;
|
||||
@@ -570,7 +569,7 @@ const GettingStarted = (props) => {
|
||||
.catch((error) => {
|
||||
setVideoViewOpen(true)
|
||||
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
@@ -687,7 +686,7 @@ const GettingStarted = (props) => {
|
||||
trigger.parameters[1].value = "webhook_" + trigger.id;
|
||||
// FIXME: Add auth here?
|
||||
} else {
|
||||
alert.info("Something is wrong with the webhook in the copy");
|
||||
toast("Something is wrong with the webhook in the copy");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -805,7 +804,7 @@ const GettingStarted = (props) => {
|
||||
data = sanitizeWorkflow(data);
|
||||
|
||||
if (data.subflows !== null && data.subflows !== undefined) {
|
||||
alert.info(
|
||||
toast(
|
||||
"Not exporting with subflows when sanitizing. Please manually export them."
|
||||
);
|
||||
data.subflows = [];
|
||||
@@ -832,7 +831,7 @@ const GettingStarted = (props) => {
|
||||
const publishWorkflow = (data) => {
|
||||
data = JSON.parse(JSON.stringify(data));
|
||||
data = sanitizeWorkflow(data);
|
||||
alert.info("Sanitizing and publishing " + data.name);
|
||||
toast("Sanitizing and publishing " + data.name);
|
||||
|
||||
// This ALWAYS talks to Shuffle cloud
|
||||
fetch(globalUrl + "/api/v1/workflows/" + data.id + "/publish", {
|
||||
@@ -849,9 +848,9 @@ const GettingStarted = (props) => {
|
||||
console.log("Status not 200 for workflow publish :O!");
|
||||
} else {
|
||||
if (isCloud) {
|
||||
alert.success("Successfully published workflow");
|
||||
toast("Successfully published workflow");
|
||||
} else {
|
||||
alert.success(
|
||||
toast(
|
||||
"Successfully published workflow to https://shuffler.io"
|
||||
);
|
||||
}
|
||||
@@ -861,19 +860,19 @@ const GettingStarted = (props) => {
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson.reason !== undefined) {
|
||||
alert.error("Failed publishing: ", responseJson.reason);
|
||||
toast("Failed publishing: ", responseJson.reason);
|
||||
}
|
||||
|
||||
getAvailableWorkflows();
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
const copyWorkflow = (data) => {
|
||||
data = JSON.parse(JSON.stringify(data));
|
||||
alert.success("Copying workflow " + data.name);
|
||||
toast("Copying workflow " + data.name);
|
||||
data.id = "";
|
||||
data.name = data.name + "_copy";
|
||||
data = deduplicateIds(data);
|
||||
@@ -900,7 +899,7 @@ const GettingStarted = (props) => {
|
||||
}, 1000);
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
@@ -916,9 +915,9 @@ const GettingStarted = (props) => {
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
console.log("Status not 200 for setting workflows :O!");
|
||||
alert.error("Failed deleting workflow. Do you have access?");
|
||||
toast("Failed deleting workflow. Do you have access?");
|
||||
} else {
|
||||
alert.success("Deleted workflow " + id);
|
||||
toast("Deleted workflow " + id);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
@@ -929,7 +928,7 @@ const GettingStarted = (props) => {
|
||||
}, 1000);
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1274,7 +1273,7 @@ const GettingStarted = (props) => {
|
||||
}}
|
||||
onClick={() => {
|
||||
if (subflows === 0) {
|
||||
alert.info("No subflows for " + data.name);
|
||||
toast("No subflows for " + data.name);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1436,9 +1435,9 @@ const GettingStarted = (props) => {
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success === false) {
|
||||
if (responseJson.reason !== undefined) {
|
||||
alert.error("Error setting workflow: ", responseJson.reason)
|
||||
toast("Error setting workflow: ", responseJson.reason)
|
||||
} else {
|
||||
alert.error("Error setting workflow.")
|
||||
toast("Error setting workflow.")
|
||||
}
|
||||
|
||||
return
|
||||
@@ -1455,14 +1454,14 @@ const GettingStarted = (props) => {
|
||||
setImportLoading(false);
|
||||
setModalOpen(false);
|
||||
} else {
|
||||
alert.info("Successfully changed basic info for workflow");
|
||||
toast("Successfully changed basic info for workflow");
|
||||
setModalOpen(false);
|
||||
}
|
||||
|
||||
return responseJson;
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
setImportLoading(false);
|
||||
setModalOpen(false);
|
||||
setSubmitLoading(false);
|
||||
@@ -1478,7 +1477,7 @@ const GettingStarted = (props) => {
|
||||
const file = event.target.files[key];
|
||||
if (file.type !== "application/json") {
|
||||
if (file.type !== undefined) {
|
||||
alert.error("File has to contain valid json");
|
||||
toast("File has to contain valid json");
|
||||
setImportLoading(false);
|
||||
}
|
||||
|
||||
@@ -1492,7 +1491,7 @@ const GettingStarted = (props) => {
|
||||
try {
|
||||
data = JSON.parse(reader.result);
|
||||
} catch (e) {
|
||||
alert.error("Invalid JSON: " + e);
|
||||
toast("Invalid JSON: " + e);
|
||||
setImportLoading(false);
|
||||
return;
|
||||
}
|
||||
@@ -1524,13 +1523,13 @@ const GettingStarted = (props) => {
|
||||
false
|
||||
).then((response) => {
|
||||
if (response !== undefined) {
|
||||
alert.success("Successfully imported " + data.name);
|
||||
toast("Successfully imported " + data.name);
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error("Import error: " + error.toString());
|
||||
toast("Import error: " + error.toString());
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1729,7 +1728,7 @@ const GettingStarted = (props) => {
|
||||
}}
|
||||
onClick={() => {
|
||||
if (subflows === 0) {
|
||||
alert.info("No subflows for " + data.name);
|
||||
toast("No subflows for " + data.name);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1930,7 +1929,7 @@ const GettingStarted = (props) => {
|
||||
margin="dense"
|
||||
fullWidth
|
||||
/>
|
||||
<ChipInput
|
||||
<MuiChipsInput
|
||||
style={{ marginTop: 10 }}
|
||||
InputProps={{
|
||||
style: {
|
||||
@@ -2170,7 +2169,7 @@ const GettingStarted = (props) => {
|
||||
})
|
||||
return
|
||||
} else {
|
||||
alert.success("TBD: Coming in version 1.0.0");
|
||||
toast("TBD: Coming in version 1.0.0");
|
||||
}
|
||||
|
||||
const ele = document.getElementById("shuffle_search_field")
|
||||
@@ -2181,7 +2180,7 @@ const GettingStarted = (props) => {
|
||||
ele.style.borderWidth = "2px"
|
||||
|
||||
} else {
|
||||
//alert.success("TBD: Coming in version 1.0.0");
|
||||
//toast("TBD: Coming in version 1.0.0");
|
||||
}
|
||||
}}>
|
||||
workflows made by other creators</span>!
|
||||
@@ -2435,7 +2434,7 @@ const GettingStarted = (props) => {
|
||||
</Typography>
|
||||
</div>
|
||||
<div style={{ flex: 1, float: "right" }}>
|
||||
<ChipInput
|
||||
<MuiChipsInput
|
||||
style={{}}
|
||||
InputProps={{
|
||||
style: {
|
||||
@@ -2601,7 +2600,7 @@ const GettingStarted = (props) => {
|
||||
parsedData["field_2"] = field2;
|
||||
}
|
||||
|
||||
alert.success("Getting specific workflows from your URL.");
|
||||
toast("Getting specific workflows from your URL.");
|
||||
fetch(globalUrl + "/api/v1/workflows/download_remote", {
|
||||
method: "POST",
|
||||
mode: "cors",
|
||||
@@ -2613,7 +2612,7 @@ const GettingStarted = (props) => {
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status === 200) {
|
||||
alert.success("Successfully loaded workflows from " + downloadUrl);
|
||||
toast("Successfully loaded workflows from " + downloadUrl);
|
||||
setTimeout(() => {
|
||||
getAvailableWorkflows();
|
||||
}, 1000);
|
||||
@@ -2624,14 +2623,14 @@ const GettingStarted = (props) => {
|
||||
.then((responseJson) => {
|
||||
if (!responseJson.success) {
|
||||
if (responseJson.reason !== undefined) {
|
||||
alert.error("Failed loading: " + responseJson.reason);
|
||||
toast("Failed loading: " + responseJson.reason);
|
||||
} else {
|
||||
alert.error("Failed loading");
|
||||
toast("Failed loading");
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -1,221 +0,0 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Link, useParams } from "react-router-dom";
|
||||
import theme from '../theme.jsx';
|
||||
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import Card from "@material-ui/core/Card";
|
||||
import CardActionArea from "@material-ui/core/CardActionArea";
|
||||
import CardContent from "@material-ui/core/CardContent";
|
||||
import CardHeader from "@material-ui/core/CardHeader";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import Button from "@material-ui/core/Button";
|
||||
|
||||
const Workflows = (defaultprops) => {
|
||||
const { globalUrl, isLoggedIn, isLoaded } = defaultprops;
|
||||
|
||||
const params = useParams();
|
||||
var props = JSON.parse(JSON.stringify(defaultprops))
|
||||
props.match = {}
|
||||
props.match.params = params
|
||||
|
||||
const [curView, setCurView] = useState(0);
|
||||
const [firstrequest, setFirstrequest] = useState(true);
|
||||
const [selectedItems, setSelectedItems] = useState([]);
|
||||
|
||||
const viewdata1 = [
|
||||
{
|
||||
title: "General",
|
||||
content: "Learn about our ticketing solutions",
|
||||
subitems: [
|
||||
{
|
||||
name: "Search",
|
||||
subtitle: "Search for anything, anywhere",
|
||||
},
|
||||
{
|
||||
name: "Message",
|
||||
subtitle: "Read and send messages",
|
||||
},
|
||||
{
|
||||
name: "Parse emails",
|
||||
subtitle: "what",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Ticketing",
|
||||
subitems: [
|
||||
{
|
||||
name: "Search",
|
||||
subtitle: "Search for anything, anywhere",
|
||||
},
|
||||
{
|
||||
name: "Message",
|
||||
subtitle: "Read and send messages",
|
||||
},
|
||||
{
|
||||
name: "Parse emails",
|
||||
subtitle: "what",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Threat intel",
|
||||
subitems: [
|
||||
{
|
||||
name: "Search",
|
||||
subtitle: "Search for anything, anywhere",
|
||||
},
|
||||
{
|
||||
name: "Message",
|
||||
subtitle: "Read and send messages",
|
||||
},
|
||||
{
|
||||
name: "Parse emails",
|
||||
subtitle: "what",
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
if (firstrequest) {
|
||||
setFirstrequest(false);
|
||||
if (props.match.params.key) {
|
||||
console.log("PROPS: ", props.match.params.key);
|
||||
const viewitem = viewdata1.find(
|
||||
(item) =>
|
||||
item.title.toLowerCase() === props.match.params.key.toLowerCase()
|
||||
);
|
||||
if (viewitem !== undefined && viewitem !== null) {
|
||||
setCurView(1);
|
||||
//setSelectedItem(viewitem)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const cardContentStyle = {
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
padding: 40,
|
||||
};
|
||||
|
||||
const outerGridView = {
|
||||
width: "100%",
|
||||
marginTop: 15,
|
||||
};
|
||||
|
||||
const paperStyle = {
|
||||
height: 300,
|
||||
color: "white",
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
color: "white",
|
||||
cursor: "pointer",
|
||||
display: "flex",
|
||||
textAlign: "center",
|
||||
};
|
||||
|
||||
const HandleSelection = (data) => {
|
||||
const [selected, setSelected] = useState(false);
|
||||
|
||||
var baseStyle = JSON.parse(JSON.stringify(paperStyle));
|
||||
if (selected) {
|
||||
baseStyle.backgroundColor = "white";
|
||||
baseStyle.color = "black";
|
||||
}
|
||||
|
||||
return (
|
||||
<Grid
|
||||
item
|
||||
xs={4}
|
||||
onClick={() => {
|
||||
console.log(selectedItems);
|
||||
if (selected) {
|
||||
const index = selectedItems.findIndex(
|
||||
(item) => item.title === data.title
|
||||
);
|
||||
if (index >= 0) {
|
||||
selectedItems.splice(index, 1);
|
||||
setSelectedItems(selectedItems);
|
||||
}
|
||||
} else {
|
||||
selectedItems.push(data);
|
||||
setSelectedItems(selectedItems);
|
||||
}
|
||||
|
||||
setSelected(!selected);
|
||||
|
||||
//setCurView(1)
|
||||
//setSelectedItem(data)
|
||||
//window.location.pathname += "/"+data.title.toLowerCase()
|
||||
}}
|
||||
>
|
||||
<Card style={baseStyle}>
|
||||
<CardActionArea style={cardContentStyle}>
|
||||
<CardContent>
|
||||
<Typography variant="h4">{data.title}</Typography>
|
||||
</CardContent>
|
||||
</CardActionArea>
|
||||
</Card>
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
|
||||
const view1 =
|
||||
curView === 0 ? (
|
||||
<div>
|
||||
<Typography variant="h4">What are you interested in?</Typography>
|
||||
<Grid container style={outerGridView} spacing={3}>
|
||||
{viewdata1.map((data) => {
|
||||
return HandleSelection(data);
|
||||
})}
|
||||
</Grid>
|
||||
{/*
|
||||
<Button variant="contained" color="primary" style={{height: 50, width: 300, margin: "auto",}} onClick={() => {
|
||||
setCurView(1)
|
||||
}}>
|
||||
Continue
|
||||
</Button>
|
||||
*/}
|
||||
</div>
|
||||
) : null;
|
||||
|
||||
const view2 =
|
||||
curView === 1 ? (
|
||||
<div>
|
||||
<Typography variant="h4">Step 2.</Typography>
|
||||
{/*
|
||||
<Grid container style={outerGridView} spacing={3}>
|
||||
{selectedItem.subitems === undefined ? null :
|
||||
selectedItem.subitems.map(data => {
|
||||
return (
|
||||
<Grid item xs={4}>
|
||||
<Card style={paperStyle}>
|
||||
<CardActionArea style={cardContentStyle}>
|
||||
<CardContent>
|
||||
<Typography variant="h4">
|
||||
{data.name}
|
||||
</Typography>
|
||||
<Typography variant="body1" style={{marginTop: 10}}>
|
||||
{data.subtitle}
|
||||
</Typography>
|
||||
</CardContent>
|
||||
</CardActionArea>
|
||||
</Card>
|
||||
</Grid>
|
||||
)
|
||||
})}
|
||||
</Grid>
|
||||
*/}
|
||||
</div>
|
||||
) : null;
|
||||
|
||||
const baseView = (
|
||||
<div style={{ maxWidth: 1024, margin: "auto", paddingTop: 50 }}>
|
||||
{view1}
|
||||
{view2}
|
||||
</div>
|
||||
);
|
||||
|
||||
return <div>{baseView}</div>;
|
||||
};
|
||||
|
||||
export default Workflows;
|
||||
@@ -1,207 +0,0 @@
|
||||
import React from "react";
|
||||
|
||||
import Paper from "@material-ui/core/Paper";
|
||||
import Button from "@material-ui/core/Button";
|
||||
import Divider from "@material-ui/core/Divider";
|
||||
import { BrowserView, MobileView } from "react-device-detect";
|
||||
import {
|
||||
Schedule as ScheduleIcon,
|
||||
Web as WebIcon,
|
||||
AccountTree as AccountTreeIcon,
|
||||
} from "@mui/icons-material";
|
||||
|
||||
const bodyDivStyle = {
|
||||
margin: "auto",
|
||||
marginTop: "75px",
|
||||
textAlign: "center",
|
||||
width: "1100px",
|
||||
};
|
||||
|
||||
const surfaceColor = "#27292D";
|
||||
const boxStyle = {
|
||||
flex: "1",
|
||||
marginLeft: "10px",
|
||||
marginRight: "10px",
|
||||
height: "400px",
|
||||
//backgroundColor: "#e8eaf6",
|
||||
backgroundColor: surfaceColor,
|
||||
textAlign: "center",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
};
|
||||
|
||||
const bodyTextStyle = {
|
||||
color: "#ffffff",
|
||||
};
|
||||
|
||||
const hrefStyle = {
|
||||
color: "black",
|
||||
textDecoration: "none",
|
||||
};
|
||||
|
||||
// Should be different if logged in :|
|
||||
const LandingPage = (props) => {
|
||||
const { isLoaded } = props;
|
||||
|
||||
const textColor = "#8899A6";
|
||||
const iconColor = "#1DA1F2";
|
||||
const iconSize = "8em";
|
||||
const GridLayout = (header, description, link, icon) => {
|
||||
return (
|
||||
<Paper style={boxStyle}>
|
||||
<a href={link} style={hrefStyle}>
|
||||
<div style={{ flex: "1", color: "#FFFFFF" }}>
|
||||
<h2>{header}</h2>
|
||||
</div>
|
||||
<Divider />
|
||||
<div
|
||||
style={{
|
||||
flex: "3",
|
||||
marginLeft: "10px",
|
||||
marginRight: "10px",
|
||||
marginTop: "10px",
|
||||
color: textColor,
|
||||
}}
|
||||
>
|
||||
{description}
|
||||
</div>
|
||||
<div style={{ margin: "auto" }}>{icon}</div>
|
||||
<Divider style={{ marginTop: "20px", marginBottom: "20px" }} />
|
||||
<div style={{ flex: "1", color: "#f85a3e" }}>
|
||||
<div style={{}}>Learn more</div>
|
||||
</div>
|
||||
</a>
|
||||
</Paper>
|
||||
);
|
||||
};
|
||||
|
||||
const listitems = [
|
||||
GridLayout(
|
||||
"Simple integrations",
|
||||
"Easily use others' or create your own integration",
|
||||
"/docs/apps",
|
||||
<Web
|
||||
style={{ fontSize: iconSize, marginTop: "20px", color: iconColor }}
|
||||
/>
|
||||
),
|
||||
GridLayout(
|
||||
"Workflows",
|
||||
"Access the power of automation within minutes, whether its on premise or in the cloud",
|
||||
"/docs/workflows",
|
||||
<AccountTree
|
||||
style={{ fontSize: iconSize, marginTop: "20px", color: iconColor }}
|
||||
/>
|
||||
),
|
||||
GridLayout(
|
||||
"Realtime actions",
|
||||
"Beat the clock by leveraging our realtime triggers",
|
||||
"/docs/triggers",
|
||||
<ScheduleIcon
|
||||
style={{ fontSize: iconSize, marginTop: "20px", color: iconColor }}
|
||||
/>
|
||||
),
|
||||
];
|
||||
|
||||
// The actual landing page
|
||||
// <img style={{width: "400px"}} alt={"logo"} src={Default}/>
|
||||
const landingpageDataBrowser = (
|
||||
<div>
|
||||
<div style={bodyTextStyle}>
|
||||
<h1>Shuffle</h1>
|
||||
<h3 style={{ color: "#8899A6" }}>
|
||||
A general automation solution for Infosec and IT Professionals
|
||||
</h3>
|
||||
</div>
|
||||
<a href="/register" style={hrefStyle}>
|
||||
<Button
|
||||
style={{ width: "180px", height: "50px", borderRadius: "0px" }}
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
>
|
||||
Try it out
|
||||
</Button>
|
||||
</a>
|
||||
<a href="/contact" style={hrefStyle}>
|
||||
<Button
|
||||
style={{ width: "180px", height: "50px", borderRadius: "0px" }}
|
||||
variant="contained"
|
||||
color="primary"
|
||||
>
|
||||
Contact
|
||||
</Button>
|
||||
</a>
|
||||
<div style={{ display: "flex", marginTop: "100px" }}>
|
||||
{listitems.map((item) => {
|
||||
return <div>{item}</div>;
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const landingpageDataMobile = (
|
||||
<div>
|
||||
<div
|
||||
style={{
|
||||
color: "white",
|
||||
textAlign: "center",
|
||||
marginLeft: "10px",
|
||||
marginRight: "10px",
|
||||
}}
|
||||
>
|
||||
<h1>Shuffle</h1>
|
||||
<h3>A general automation solution for Infosec and IT Professionals</h3>
|
||||
<a href="/contact" style={hrefStyle}>
|
||||
<Button
|
||||
style={{ width: "220px", height: "60px", borderRadius: "0px" }}
|
||||
variant="contained"
|
||||
color="primary"
|
||||
>
|
||||
Contact
|
||||
</Button>
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
style={{ display: "flex", flexDirection: "column", marginTop: "100px" }}
|
||||
>
|
||||
<div>{listitems[0]}</div>
|
||||
<div style={{ marginTop: "20px" }}>{listitems[1]}</div>
|
||||
<div style={{ marginTop: "20px", marginBottom: "30px" }}>
|
||||
{listitems[2]}
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
marginTop: "20px",
|
||||
marginBottom: "30px",
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
<a href="/contact" style={hrefStyle}>
|
||||
<Button
|
||||
style={{ width: "220px", height: "60px", borderRadius: "0px" }}
|
||||
variant="contained"
|
||||
color="primary"
|
||||
>
|
||||
Contact
|
||||
</Button>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
// Reroute if the user is logged in
|
||||
// const landingSite = isLoggedIn ? <Workflows globalUrl={globalUrl}{...props} /> : <div style={bodyDivStyle}>{landingpageData}</div>
|
||||
const landingSite = <div style={bodyDivStyle}>{landingpageDataBrowser}</div>;
|
||||
|
||||
const loadedCheck = isLoaded ? (
|
||||
<div>
|
||||
<BrowserView>{landingSite}</BrowserView>
|
||||
<MobileView>{landingpageDataMobile}</MobileView>
|
||||
</div>
|
||||
) : (
|
||||
<div></div>
|
||||
);
|
||||
|
||||
return <div>{loadedCheck}</div>;
|
||||
};
|
||||
export default LandingPage;
|
||||
@@ -1,531 +0,0 @@
|
||||
import React, { useState } from "react";
|
||||
|
||||
import Paper from "@material-ui/core/Paper";
|
||||
import Card from "@material-ui/core/Card";
|
||||
import CardActionArea from "@material-ui/core/CardActionArea";
|
||||
import CardMedia from "@material-ui/core/CardMedia";
|
||||
import CardContent from "@material-ui/core/CardContent";
|
||||
import CardActions from "@material-ui/core/CardActions";
|
||||
import Button from "@material-ui/core/Button";
|
||||
import Divider from "@material-ui/core/Divider";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import { BrowserView, MobileView } from "react-device-detect";
|
||||
|
||||
import {
|
||||
Schedule as ScheduleIcon,
|
||||
Web as WebIcon,
|
||||
AccountTree as AccountTreeIcon,
|
||||
Info as InfoIcon,
|
||||
ArrowForward as ArrowForwardIcon,
|
||||
Create as CreateIcon,
|
||||
} from "@mui/icons-material";
|
||||
|
||||
const bodyDivStyle = {
|
||||
margin: "auto",
|
||||
};
|
||||
|
||||
const surfaceColor = "#27292D";
|
||||
const boxStyle = {
|
||||
flex: "1",
|
||||
marginLeft: "10px",
|
||||
marginRight: "10px",
|
||||
height: "400px",
|
||||
//backgroundColor: "#e8eaf6",
|
||||
backgroundColor: surfaceColor,
|
||||
textAlign: "center",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
};
|
||||
|
||||
const bodyTextStyle = {
|
||||
color: "#ffffff",
|
||||
};
|
||||
|
||||
const hrefStyle = {
|
||||
color: "inherit",
|
||||
textDecoration: "none",
|
||||
};
|
||||
|
||||
// Should be different if logged in :|
|
||||
const LandingPage = (props) => {
|
||||
const { isLoaded } = props;
|
||||
|
||||
const textColor = "#8899A6";
|
||||
const iconColor = "#1DA1F2";
|
||||
const iconSize = "8em";
|
||||
|
||||
const GridLayout = (header, description, link, icon) => {
|
||||
return (
|
||||
<Paper style={boxStyle}>
|
||||
<a href={link} style={hrefStyle}>
|
||||
<div style={{ flex: "1", color: "#FFFFFF" }}>
|
||||
<h2>{header}</h2>
|
||||
</div>
|
||||
<Divider />
|
||||
<div
|
||||
style={{
|
||||
flex: "3",
|
||||
marginLeft: "10px",
|
||||
marginRight: "10px",
|
||||
marginTop: "10px",
|
||||
color: textColor,
|
||||
}}
|
||||
>
|
||||
{description}
|
||||
</div>
|
||||
<div style={{ margin: "auto" }}>{icon}</div>
|
||||
<Divider style={{ marginTop: "20px", marginBottom: "20px" }} />
|
||||
<div style={{ flex: "1", color: "#f85a3e" }}>
|
||||
<div style={{}}>Learn more</div>
|
||||
</div>
|
||||
</a>
|
||||
</Paper>
|
||||
);
|
||||
};
|
||||
|
||||
const listitems = [
|
||||
GridLayout(
|
||||
"Simple integrations",
|
||||
"Easily use others' or create your own integration",
|
||||
"/docs/features",
|
||||
<WebIcon
|
||||
style={{ fontSize: iconSize, marginTop: "20px", color: iconColor }}
|
||||
/>
|
||||
),
|
||||
GridLayout(
|
||||
"Workflows",
|
||||
"Access the power of automation within minutes, whether its on premise or in the cloud",
|
||||
"/docs/features",
|
||||
<AccountTreeIcon
|
||||
style={{ fontSize: iconSize, marginTop: "20px", color: iconColor }}
|
||||
/>
|
||||
),
|
||||
GridLayout(
|
||||
"Realtime actions",
|
||||
"Beat the clock by leveraging our realtime triggers",
|
||||
"/docs/features",
|
||||
<ScheduleIcon
|
||||
style={{ fontSize: iconSize, marginTop: "20px", color: iconColor }}
|
||||
/>
|
||||
),
|
||||
];
|
||||
|
||||
// The actual landing page
|
||||
// <img style={{width: "400px"}} alt={"logo"} src={Default}/>
|
||||
//We start by understanding your unique environment to help identify the right thing to automate.
|
||||
const secondaryColor = "rgba(167,46,87,1)";
|
||||
const primaryColor = "rgba(25, 35, 94, 1)";
|
||||
|
||||
const paperStyle = {
|
||||
flex: 1,
|
||||
backgroundColor: "inherit",
|
||||
cursor: "pointer",
|
||||
};
|
||||
|
||||
const secondaryItemList = [
|
||||
{
|
||||
primaryText: "No time to waste",
|
||||
secondaryText:
|
||||
"Bring all your applications into a single view, and make them all work together flawlessly!",
|
||||
image: "/images/time.jpg",
|
||||
},
|
||||
{
|
||||
primaryText: "Get a better overview",
|
||||
secondaryText:
|
||||
"Don't know what's happening? We'll help you track and act on your most valuable KPI's!",
|
||||
image: "/images/overview.jpg",
|
||||
},
|
||||
{
|
||||
primaryText: "Conquer your tasks",
|
||||
secondaryText:
|
||||
"Get access to powerful tools and pre-made workflows to help you crush your teams daily tasks!",
|
||||
image: "/images/burnout.jpg",
|
||||
},
|
||||
];
|
||||
const [image, setImage] = useState(secondaryItemList[0].image);
|
||||
|
||||
const landingpageDataBrowser = (
|
||||
<div>
|
||||
<div
|
||||
style={{
|
||||
backgroundImage: "url('/images/test.jpg')",
|
||||
backgroundSize: "80% 100%",
|
||||
backgroundRepeat: "no-repeat",
|
||||
minHeight: "100vh",
|
||||
maxHeight: 1024,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
textAlign: "left",
|
||||
paddingTop: 135,
|
||||
maxWidth: 700,
|
||||
paddingLeft: "50%",
|
||||
color: secondaryColor,
|
||||
display: "flex",
|
||||
fontSize: 25,
|
||||
}}
|
||||
>
|
||||
<div style={{ flex: 1 }}>
|
||||
<a href="/docs/about" style={hrefStyle}>
|
||||
<Grid container direction="row" alignItems="center">
|
||||
<Grid item>
|
||||
<InfoIcon />
|
||||
</Grid>
|
||||
<Grid item style={{ marginLeft: 5 }}>
|
||||
About
|
||||
</Grid>
|
||||
</Grid>
|
||||
</a>
|
||||
</div>
|
||||
<div style={{ flex: 1 }}>
|
||||
<a href="/contact" style={hrefStyle}>
|
||||
<Grid container direction="row" alignItems="center">
|
||||
<Grid item>
|
||||
<CreateIcon />
|
||||
</Grid>
|
||||
<Grid item style={{ marginLeft: 5 }}>
|
||||
Get in touch
|
||||
</Grid>
|
||||
</Grid>
|
||||
</a>
|
||||
</div>
|
||||
<div style={{ flex: 1 }}>
|
||||
<a href="/login" style={hrefStyle}>
|
||||
<Button
|
||||
style={{
|
||||
borderRadius: 25,
|
||||
height: 50,
|
||||
minWidth: 200,
|
||||
backgroundColor: secondaryColor,
|
||||
color: "white",
|
||||
}}
|
||||
variant="contained"
|
||||
>
|
||||
Try it out <ArrowForwardIcon />
|
||||
</Button>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
style={
|
||||
(bodyTextStyle,
|
||||
{
|
||||
textAlign: "left",
|
||||
paddingTop: "8%",
|
||||
paddingLeft: "28%",
|
||||
maxWidth: 430,
|
||||
})
|
||||
}
|
||||
>
|
||||
<div style={{ fontSize: 25, color: "rgba(0,0,0,0.45)" }}>Shuffle</div>
|
||||
<div style={{ fontSize: 50, color: "rgba(0,0,0,0.7)" }}>
|
||||
INFORMATION <div style={{ color: secondaryColor }}>OVERLOAD</div>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
fontSize: 20,
|
||||
color: "rgba(0, 0, 0, 0.45)",
|
||||
marginTop: 20,
|
||||
}}
|
||||
>
|
||||
Everyone run into the same fundamental operational problems. Mailbox
|
||||
chaos, tickets getting out of hand and a constant feeling of being
|
||||
overwhelmed. The good news?{" "}
|
||||
<div style={{ color: secondaryColor, marginTop: 10 }}>
|
||||
Shuffle solves them.
|
||||
</div>
|
||||
</div>
|
||||
<a href="/docs/features" style={hrefStyle}>
|
||||
<Button
|
||||
style={{
|
||||
borderRadius: 25,
|
||||
height: 50,
|
||||
marginTop: 50,
|
||||
width: 200,
|
||||
backgroundColor: secondaryColor,
|
||||
color: "white",
|
||||
}}
|
||||
variant="contained"
|
||||
>
|
||||
Learn how
|
||||
</Button>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
minHeight: 1024,
|
||||
width: "100%",
|
||||
backgroundImage: "linear-gradient(to bottom right, #19235e, #19235e)",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
minHeight: 1000,
|
||||
paddingTop: 150,
|
||||
maxWidth: 1250,
|
||||
margin: "auto",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
color: "rgba(255,255,255,0.8",
|
||||
fontSize: 60,
|
||||
marginLeft: 25,
|
||||
}}
|
||||
>
|
||||
<b>Automation is just the beginning </b>
|
||||
</div>
|
||||
<div style={{ marginTop: 40, display: "flex", flexDirection: "row" }}>
|
||||
<div
|
||||
style={{
|
||||
flex: 8,
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
fontSize: 40,
|
||||
}}
|
||||
>
|
||||
{secondaryItemList.map((data, index) => {
|
||||
const color =
|
||||
image === data.image
|
||||
? "rgba(255,255,255,1)"
|
||||
: "rgba(255,255,255,0.4)";
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
borderRadius: 15,
|
||||
padding: 25,
|
||||
maxWidth: 600,
|
||||
height: 150,
|
||||
fontSize: 40,
|
||||
color: color,
|
||||
cursor: "pointer",
|
||||
}}
|
||||
onClick={() => setImage(data.image)}
|
||||
>
|
||||
{data.primaryText}
|
||||
<div style={{ fontSize: 22, marginTop: 10 }}>
|
||||
{data.secondaryText}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div style={{ flex: 1 }} />
|
||||
<div style={{ flex: 10, height: "100%", width: "100%" }}>
|
||||
<img
|
||||
src={image}
|
||||
style={{
|
||||
borderRadius: 15,
|
||||
minHeight: "100%",
|
||||
minWidth: "100%",
|
||||
maxWidth: "100%",
|
||||
maxHeight: "100%",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
maxWidth: 1250,
|
||||
paddingTop: 100,
|
||||
paddingBottom: 100,
|
||||
margin: "auto",
|
||||
color: "rgba(255,255,255,0.8)",
|
||||
}}
|
||||
>
|
||||
<Divider style={{ backgroundColor: "rgba(255,255,255,0.6)" }} />
|
||||
<div
|
||||
style={{
|
||||
marginTop: 100,
|
||||
fontSize: 40,
|
||||
display: "flex",
|
||||
marginLeft: 100,
|
||||
marginRight: 100,
|
||||
}}
|
||||
>
|
||||
<div style={{ flex: 3 }}>
|
||||
Learn more about the benefits of Shuffle
|
||||
</div>
|
||||
<div style={{ flex: 1 }}>
|
||||
<a href="/docs/features" style={hrefStyle}>
|
||||
<Button
|
||||
fullWidth
|
||||
style={{
|
||||
borderRadius: 25,
|
||||
minHeight: 50,
|
||||
minWidth: 200,
|
||||
backgroundColor: secondaryColor,
|
||||
color: "white",
|
||||
}}
|
||||
variant="contained"
|
||||
>
|
||||
See features
|
||||
</Button>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
textAlign: "center",
|
||||
maxWidth: 1100,
|
||||
minHeight: 600,
|
||||
paddingTop: 100,
|
||||
margin: "auto",
|
||||
color: "rgba(0,0,0,1)",
|
||||
}}
|
||||
>
|
||||
<div style={{ fontSize: 50 }}>
|
||||
<b>Focus on the work that matters to you</b>
|
||||
</div>
|
||||
<div
|
||||
style={{ fontSize: 20, color: "rgba(0,0,0,0.7)", maxWidth: "100%" }}
|
||||
>
|
||||
Menial tasks, scattered content, constant copy pasting, waste of
|
||||
talent - <b>there's a smarter way to work.</b>
|
||||
</div>
|
||||
<div style={{ display: "flex", marginTop: 50 }}>
|
||||
<Card
|
||||
onClick={() => {
|
||||
window.location.pathname = "/docs/features";
|
||||
}}
|
||||
style={{ flex: 1, margin: 10, textAlign: "center" }}
|
||||
>
|
||||
<CardActionArea>
|
||||
<CardMedia title="TEST" image="/images/time.jpg" />
|
||||
<CardContent>
|
||||
<h3>Premade playbooks</h3>
|
||||
<p>Get your automation done with minimal effort</p>
|
||||
</CardContent>
|
||||
</CardActionArea>
|
||||
<Button size="medium" color="green">
|
||||
Learn more
|
||||
</Button>
|
||||
</Card>
|
||||
<Card
|
||||
onClick={() => {
|
||||
window.location.pathname = "/docs/features";
|
||||
}}
|
||||
style={{ flex: 1, margin: 10, textAlign: "center" }}
|
||||
>
|
||||
<CardActionArea>
|
||||
<CardMedia title="TEST" image="/images/time.jpg" />
|
||||
<CardContent>
|
||||
<h3>Open frameworks</h3>
|
||||
<p>Mitre Att&ck, OpenAPI and more!</p>
|
||||
</CardContent>
|
||||
</CardActionArea>
|
||||
<Button size="medium" color="green">
|
||||
Learn more
|
||||
</Button>
|
||||
</Card>
|
||||
<Card
|
||||
onClick={() => {
|
||||
window.location.pathname = "/docs/features";
|
||||
}}
|
||||
style={{ flex: 1, margin: 10, textAlign: "center" }}
|
||||
>
|
||||
<CardActionArea>
|
||||
<CardMedia title="TEST" image="/images/time.jpg" />
|
||||
<CardContent>
|
||||
<h3>Hundreds of integrations</h3>
|
||||
<p>Quickly integrate your software applications</p>
|
||||
</CardContent>
|
||||
</CardActionArea>
|
||||
<Button size="medium" color="green">
|
||||
Learn more
|
||||
</Button>
|
||||
</Card>
|
||||
<Card
|
||||
style={{ flex: 1, margin: 10, textAlign: "center" }}
|
||||
onClick={() => {
|
||||
window.location.pathname = "/docs/features";
|
||||
}}
|
||||
>
|
||||
<CardActionArea>
|
||||
<CardMedia title="TEST" image="images/time.jpg" />
|
||||
<CardContent>
|
||||
<h3>Automated compliance</h3>
|
||||
<p>Stuck with compliance needs you can't meet?</p>
|
||||
</CardContent>
|
||||
</CardActionArea>
|
||||
<Button size="medium" color="green">
|
||||
Learn more
|
||||
</Button>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const landingpageDataMobile = (
|
||||
<div style={{ backgroundColor: "#1F2023", paddingTop: 30 }}>
|
||||
<div
|
||||
style={{
|
||||
color: "white",
|
||||
textAlign: "center",
|
||||
marginLeft: "10px",
|
||||
marginRight: "10px",
|
||||
}}
|
||||
>
|
||||
<h1>Shuffle</h1>
|
||||
<h3>A general automation solution for Infosec and IT Professionals</h3>
|
||||
<a href="/contact" style={hrefStyle}>
|
||||
<Button
|
||||
style={{ width: "220px", height: "60px", borderRadius: "0px" }}
|
||||
variant="contained"
|
||||
color="primary"
|
||||
>
|
||||
Contact
|
||||
</Button>
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
style={{ display: "flex", flexDirection: "column", marginTop: "100px" }}
|
||||
>
|
||||
<div>{listitems[0]}</div>
|
||||
<div style={{ marginTop: "20px" }}>{listitems[1]}</div>
|
||||
<div style={{ marginTop: "20px", marginBottom: "30px" }}>
|
||||
{listitems[2]}
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
marginTop: "20px",
|
||||
marginBottom: "30px",
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
<a href="/contact" style={hrefStyle}>
|
||||
<Button
|
||||
style={{ width: "220px", height: "60px", borderRadius: "0px" }}
|
||||
variant="contained"
|
||||
color="primary"
|
||||
>
|
||||
Contact
|
||||
</Button>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
// Reroute if the user is logged in
|
||||
// const landingSite = isLoggedIn ? <Workflows globalUrl={globalUrl}{...props} /> : <div style={bodyDivStyle}>{landingpageData}</div>
|
||||
const landingSite = <div style={bodyDivStyle}>{landingpageDataBrowser}</div>;
|
||||
|
||||
const loadedCheck = isLoaded ? (
|
||||
<div>
|
||||
<BrowserView>{landingSite}</BrowserView>
|
||||
<MobileView>{landingpageDataMobile}</MobileView>
|
||||
</div>
|
||||
) : (
|
||||
<div></div>
|
||||
);
|
||||
|
||||
return <div>{loadedCheck}</div>;
|
||||
};
|
||||
export default LandingPage;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,93 +0,0 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
import Paper from "@material-ui/core/Paper";
|
||||
|
||||
const bodyDivStyle = {
|
||||
margin: "auto",
|
||||
textAlign: "center",
|
||||
width: "768px",
|
||||
};
|
||||
|
||||
//const tmpdata = {
|
||||
// "username": "frikky",
|
||||
// "firstname": "fred",
|
||||
// "lastname": "ode",
|
||||
// "title": "topkek",
|
||||
// "companyname": "company here",
|
||||
// "email": "your email pls",
|
||||
// "phone": "PHONE!!",
|
||||
//}
|
||||
|
||||
// FIXME - add fetch for data fields
|
||||
// FIXME - remove tmpdata
|
||||
// FIXME: Use isLoggedIn :)
|
||||
const Settings = (defaultprops) => {
|
||||
const { globalUrl, isLoaded, surfaceColor } = defaultprops;
|
||||
|
||||
const params = useParams();
|
||||
var props = JSON.parse(JSON.stringify(defaultprops))
|
||||
props.match = {}
|
||||
props.match.params = params
|
||||
|
||||
const [firstRequest, setFirstRequest] = useState(true);
|
||||
const boxStyle = {
|
||||
flex: "1",
|
||||
marginLeft: "10px",
|
||||
marginRight: "10px",
|
||||
paddingLeft: "30px",
|
||||
paddingRight: "30px",
|
||||
paddingBottom: "30px",
|
||||
paddingTop: "30px",
|
||||
backgroundColor: surfaceColor,
|
||||
color: "white",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
};
|
||||
|
||||
const registerCall = () => {
|
||||
const url = globalUrl + "/api/v1/register/" + props.match.params.key;
|
||||
fetch(url, {
|
||||
method: "GET",
|
||||
credentials: "include",
|
||||
headers: {
|
||||
"Content-Type": "application/json; charset=utf-8",
|
||||
},
|
||||
})
|
||||
.then((response) =>
|
||||
response.json().then((responseJson) => {
|
||||
console.log(responseJson);
|
||||
})
|
||||
)
|
||||
.catch((error) => {
|
||||
console.log("SOMETHING WRONG");
|
||||
});
|
||||
};
|
||||
|
||||
// This should "always" have data
|
||||
useEffect(() => {
|
||||
if (firstRequest) {
|
||||
setFirstRequest(false);
|
||||
registerCall();
|
||||
}
|
||||
});
|
||||
|
||||
// Random names for type & autoComplete. Didn't research :^)
|
||||
const landingpageData = (
|
||||
<div style={{ display: "flex", marginTop: "80px" }}>
|
||||
<Paper style={boxStyle}>
|
||||
<h2>Registration verification</h2>
|
||||
<p>Thanks for verifying, redirecting you to our login!</p>
|
||||
</Paper>
|
||||
</div>
|
||||
);
|
||||
|
||||
const loadedCheck = isLoaded ? (
|
||||
<div style={bodyDivStyle}>{landingpageData}</div>
|
||||
) : (
|
||||
<div></div>
|
||||
);
|
||||
|
||||
return <div>{loadedCheck}</div>;
|
||||
};
|
||||
export default Settings;
|
||||
@@ -1,176 +0,0 @@
|
||||
/* eslint-disable react/no-multi-comp */
|
||||
import React, { useState } from "react";
|
||||
|
||||
import DialogTitle from "@material-ui/core/DialogTitle";
|
||||
import Dialog from "@material-ui/core/Dialog";
|
||||
import TextField from "@material-ui/core/TextField";
|
||||
import Button from "@material-ui/core/Button";
|
||||
|
||||
const LoginDialog = (props) => {
|
||||
const {
|
||||
classes,
|
||||
onClose,
|
||||
open,
|
||||
globalUrl,
|
||||
isLoggedIn,
|
||||
setIsLoggedIn,
|
||||
...other
|
||||
} = props;
|
||||
|
||||
const [username, setUsername] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
//const [selectedValue, setSelectedValue] = useState(false);
|
||||
|
||||
// Used to swap from login to register. True = login, false = register
|
||||
const [loginCheck, setLoginCheck] = useState(true);
|
||||
|
||||
// Error messages etc
|
||||
const [loginInfo, setLoginInfo] = useState("");
|
||||
|
||||
const handleValidateForm = () => {
|
||||
return username.length > 1 && password.length > 8;
|
||||
};
|
||||
|
||||
const onSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
// Just use this one?
|
||||
var data =
|
||||
'{"username": "' + username + '", "password": "' + password + '"}';
|
||||
var baseurl = globalUrl;
|
||||
if (loginCheck) {
|
||||
var url = baseurl + "/login";
|
||||
fetch(url, {
|
||||
method: "POST",
|
||||
body: data,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
})
|
||||
.then((response) =>
|
||||
response.json().then((responseJson) => {
|
||||
console.log(responseJson);
|
||||
//console.log(e)
|
||||
if (responseJson["success"] === false) {
|
||||
setLoginInfo(responseJson["reason"]);
|
||||
} else {
|
||||
setLoginInfo("Successful login :)");
|
||||
onClose();
|
||||
setIsLoggedIn(true);
|
||||
}
|
||||
})
|
||||
)
|
||||
.catch((error) => {
|
||||
setLoginInfo("Error in userdata");
|
||||
});
|
||||
} else {
|
||||
url = baseurl + "/register";
|
||||
fetch(url, {
|
||||
method: "POST",
|
||||
body: data,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
})
|
||||
.then((response) =>
|
||||
response.json().then((responseJson) => {
|
||||
if (responseJson["success"] === false) {
|
||||
setLoginInfo(responseJson["reason"]);
|
||||
} else {
|
||||
setLoginInfo("Successful register. Please check your mail :)");
|
||||
onClose();
|
||||
setIsLoggedIn(true);
|
||||
}
|
||||
})
|
||||
)
|
||||
.catch((error) => {
|
||||
setLoginInfo("Error in userdata");
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const onChangeUser = (e) => {
|
||||
setUsername(e.target.value);
|
||||
};
|
||||
|
||||
const onChangePass = (e) => {
|
||||
setPassword(e.target.value);
|
||||
};
|
||||
|
||||
const onClickRegister = () => {
|
||||
setLoginCheck(!loginCheck);
|
||||
};
|
||||
|
||||
//var loginChange = loginCheck ? (<div><p onClick={setLoginCheck(false)}>Want to register? Click here.</p></div>) : (<div><p onClick={setLoginCheck(true)}>Go back to login? Click here.</p></div>);
|
||||
var formtitle = loginCheck ? <div>Login</div> : <div>Register</div>;
|
||||
var formButton = loginCheck ? (
|
||||
<div>Click to Register</div>
|
||||
) : (
|
||||
<div>Click to Login</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<Dialog modal open={open} onClose={onClose} {...other}>
|
||||
<DialogTitle>{formtitle}</DialogTitle>
|
||||
<form onSubmit={onSubmit} style={{ margin: "15px 15px 15px 15px" }}>
|
||||
Username
|
||||
<div>
|
||||
<TextField
|
||||
required
|
||||
id="standard-required"
|
||||
autoComplete="username"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
onChange={onChangeUser}
|
||||
/>
|
||||
</div>
|
||||
Password
|
||||
<div>
|
||||
<TextField
|
||||
id="outlined-password-input"
|
||||
type="password"
|
||||
autoComplete="current-password"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
onChange={onChangePass}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ display: "flex", marginTop: "15px" }}>
|
||||
<Button
|
||||
color="secondary"
|
||||
variant="contained"
|
||||
type="submit"
|
||||
style={{ flex: "1", marginRight: "5px" }}
|
||||
disabled={!handleValidateForm()}
|
||||
>
|
||||
SUBMIT
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
color="primary"
|
||||
variant="contained"
|
||||
type="button"
|
||||
style={{ flex: "1" }}
|
||||
onClick={onClose}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
{loginInfo}
|
||||
</form>
|
||||
<div style={{ display: "flex" }}>
|
||||
<Button
|
||||
color="secondary"
|
||||
variant="contained"
|
||||
onClick={onClickRegister}
|
||||
type="button"
|
||||
style={{ flex: "1" }}
|
||||
>
|
||||
{formButton}
|
||||
</Button>
|
||||
</div>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default LoginDialog;
|
||||
@@ -1,232 +0,0 @@
|
||||
import React, { useEffect } from "react";
|
||||
|
||||
import Paper from "@material-ui/core/Paper";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import ButtonBase from "@material-ui/core/ButtonBase";
|
||||
import List from "@material-ui/core/List";
|
||||
import ListItem from "@material-ui/core/ListItem";
|
||||
import Button from "@material-ui/core/Button";
|
||||
//import Breadcrumbs from '@material-ui/core/Breadcrumbs';
|
||||
|
||||
const Schedules = (props) => {
|
||||
const { globalUrl } = props;
|
||||
|
||||
//const [schedules, setSchedules] = React.useState(scheduledata);
|
||||
const [schedules, setSchedules] = React.useState({});
|
||||
|
||||
const getAvailableSchedules = () => {
|
||||
fetch(globalUrl + "/api/v1/schedules", {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then((responseJson) => {
|
||||
setSchedules(responseJson);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
});
|
||||
};
|
||||
|
||||
// FIXME - add automated redirection, as empty apps look horrible currently
|
||||
const newSchedule = () => {
|
||||
fetch(globalUrl + "/api/v1/schedules/new", {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify(),
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then((responseJson) => {
|
||||
console.log(responseJson);
|
||||
setSchedules({});
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
});
|
||||
};
|
||||
|
||||
const deleteSchedule = (id) => {
|
||||
if (id === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
fetch(globalUrl + "/api/v1/schedules/" + id + "/delete", {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then((responseJson) => {
|
||||
setSchedules({});
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
});
|
||||
};
|
||||
|
||||
// FIXME - use this?
|
||||
//const getNewScheduleInfo = () => {
|
||||
// fetch(globalUrl+"/api/v1/schedules", {
|
||||
// method: 'GET',
|
||||
// headers: {
|
||||
// 'Content-Type': 'application/json',
|
||||
// 'Accept': 'application/json',
|
||||
// },
|
||||
// })
|
||||
// .then((response) => response.json())
|
||||
// .then((responseJson) => {
|
||||
// setSchedules(responseJson)
|
||||
// })
|
||||
// .catch(error => {
|
||||
// console.log(error)
|
||||
// });
|
||||
//}
|
||||
|
||||
useEffect(() => {
|
||||
if (Object.getOwnPropertyNames(schedules).length <= 0) {
|
||||
getAvailableSchedules();
|
||||
}
|
||||
});
|
||||
|
||||
const bodyDivStyle = {
|
||||
marginLeft: "20px",
|
||||
marginRight: "20px",
|
||||
width: "1350px",
|
||||
minWidth: "1350px",
|
||||
maxWidth: "1350px",
|
||||
};
|
||||
|
||||
const scheduleApp = (app) => {
|
||||
console.log(app);
|
||||
return (
|
||||
<Grid container spacing={2} style={{ margin: "10px 10px 10px 10px" }}>
|
||||
<Grid item>
|
||||
<ButtonBase>
|
||||
<img alt="" style={{ width: "100px", height: "100px" }} />
|
||||
</ButtonBase>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm container>
|
||||
<Grid item xs container direction="column" spacing={2}>
|
||||
<Grid item xs>
|
||||
<div>
|
||||
<h2>{app.name}</h2>
|
||||
</div>
|
||||
<div>{app.description}</div>
|
||||
</Grid>
|
||||
<Grid item>{app.action}</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
|
||||
const splitter = (
|
||||
<div
|
||||
style={{
|
||||
width: "1px",
|
||||
backgroundColor: "grey",
|
||||
margin: "5px 5px 5px 5px",
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
const hrefStyle = {
|
||||
color: "#385f71",
|
||||
textDecoration: "none",
|
||||
};
|
||||
|
||||
// FIXME - add Schedule modal
|
||||
const schedulePaper = (schedule) => {
|
||||
return (
|
||||
<div>
|
||||
<Paper
|
||||
style={{
|
||||
maxWidth: "1000px",
|
||||
display: "flex",
|
||||
padding: "10px 10px 10px 10px",
|
||||
}}
|
||||
>
|
||||
<div style={{ flex: "5" }}>
|
||||
{scheduleApp(schedule.appinfo.sourceapp)}
|
||||
</div>
|
||||
<div style={{ flex: "1", alignItems: "center" }}>ARROW</div>
|
||||
<div style={{ flex: "5" }}>
|
||||
{scheduleApp(schedule.appinfo.destinationapp)}
|
||||
</div>
|
||||
{splitter}
|
||||
<div style={{ flex: "1" }}>
|
||||
<List style={{ backgroundColor: "#ffffff" }}>
|
||||
<ListItem style={{ flex: "1", textAlign: "center" }}>
|
||||
<a href={"/schedules/" + schedule.id} style={hrefStyle}>
|
||||
<Button disabled={false} color="primary">
|
||||
Edit
|
||||
</Button>
|
||||
</a>
|
||||
</ListItem>
|
||||
<ListItem style={{ flex: "1", textAlign: "center" }}>
|
||||
<Button
|
||||
disabled={false}
|
||||
onClick={() => {
|
||||
deleteSchedule(schedule.id);
|
||||
}}
|
||||
color="primary"
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
</ListItem>
|
||||
</List>
|
||||
</div>
|
||||
</Paper>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
console.log(schedules);
|
||||
console.log(schedules);
|
||||
console.log(schedules.schedules);
|
||||
const schedulemap =
|
||||
Object.getOwnPropertyNames(schedules).length > 0 &&
|
||||
schedules.schedules &&
|
||||
schedules.schedules.length > 0 ? (
|
||||
<div>{schedules.schedules.map((data) => schedulePaper(data))}</div>
|
||||
) : (
|
||||
<div style={{ marginTop: "10%", marginLeft: "50%" }}>
|
||||
<Button
|
||||
disabled={false}
|
||||
onClick={() => {
|
||||
newSchedule();
|
||||
}}
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
>
|
||||
CREATE NEW SCHEDULE
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
|
||||
const scheduleView =
|
||||
Object.getOwnPropertyNames(schedules).length > 0 ? (
|
||||
<div style={bodyDivStyle}>
|
||||
<Button
|
||||
disabled={false}
|
||||
onClick={() => {
|
||||
newSchedule();
|
||||
}}
|
||||
color="primary"
|
||||
>
|
||||
New
|
||||
</Button>
|
||||
{schedulemap}
|
||||
</div>
|
||||
) : null;
|
||||
|
||||
// Maybe use gridview or something, idk
|
||||
return <div>{scheduleView}</div>;
|
||||
};
|
||||
|
||||
export default Schedules;
|
||||
@@ -10,11 +10,12 @@ import {
|
||||
Divider,
|
||||
TextField,
|
||||
} from "@mui/material";
|
||||
import { useAlert } from "react-alert";
|
||||
//import { useAlert
|
||||
import { ToastContainer, toast } from "react-toastify"
|
||||
|
||||
const Settings = (props) => {
|
||||
const { globalUrl, isLoaded, userdata, setUserData } = props;
|
||||
const alert = useAlert();
|
||||
//const alert = useAlert();
|
||||
let navigate = useNavigate();
|
||||
|
||||
const [username, setUsername] = useState("");
|
||||
@@ -142,7 +143,7 @@ const Settings = (props) => {
|
||||
if (responseJson["success"] === false) {
|
||||
setPasswordFormMessage(responseJson["reason"]);
|
||||
} else {
|
||||
alert.success("Changed password!");
|
||||
toast("Changed password!");
|
||||
setPasswordFormMessage("");
|
||||
}
|
||||
})
|
||||
@@ -294,22 +295,22 @@ const Settings = (props) => {
|
||||
// detectEthereumProvider().then((provider) => {
|
||||
// if (provider) {
|
||||
// if (!provider.isMetaMask) {
|
||||
// alert.error("Only MetaMask is supported as of now.");
|
||||
// toast("Only MetaMask is supported as of now.");
|
||||
// return;
|
||||
// }
|
||||
|
||||
// // Find the ethereum network
|
||||
// // Get the users' account(s)
|
||||
// //alert.info("Connecting to MetaMask")
|
||||
// //toast("Connecting to MetaMask")
|
||||
// //console.log("Connected: ", provider.isConnected())
|
||||
|
||||
// if (!provider.isConnected()) {
|
||||
// alert.error("Metamask is not connected.");
|
||||
// toast("Metamask is not connected.");
|
||||
// return;
|
||||
// }
|
||||
|
||||
// provider.on("message", (event) => {
|
||||
// alert.info("Ethereum message: ", event);
|
||||
// toast("Ethereum message: ", event);
|
||||
// });
|
||||
|
||||
// provider.on("chainChanged", (chainId) => {
|
||||
@@ -330,12 +331,12 @@ const Settings = (props) => {
|
||||
// console.log("INFO: ", userdata);
|
||||
// setUserData(userdata);
|
||||
// } else {
|
||||
// alert.error("Couldn't find balance: ", result);
|
||||
// toast("Couldn't find balance: ", result);
|
||||
// }
|
||||
// })
|
||||
// .catch((error) => {
|
||||
// // If the request fails, the Promise will reject with an error.
|
||||
// alert.error("Failed getting info from ethereum API: " + error);
|
||||
// toast("Failed getting info from ethereum API: " + error);
|
||||
// });
|
||||
// });
|
||||
// }
|
||||
@@ -858,7 +859,7 @@ const Settings = (props) => {
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (!responseJson.success && responseJson.reason !== undefined) {
|
||||
alert.error("Failed updating user: " + responseJson.reason);
|
||||
toast("Failed updating user: " + responseJson.reason);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
|
||||
@@ -1,298 +0,0 @@
|
||||
import React from "react";
|
||||
import { Grid, Container, Divider } from "@mui/material";
|
||||
|
||||
import { makeStyles } from "@mui/material/styles";
|
||||
import Card from "@material-ui/core/Card";
|
||||
import CardContent from "@material-ui/core/CardContent";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
|
||||
import Table from "@material-ui/core/Table";
|
||||
import TableBody from "@material-ui/core/TableBody";
|
||||
import TableCell from "@material-ui/core/TableCell";
|
||||
import TableContainer from "@material-ui/core/TableContainer";
|
||||
import TableHead from "@material-ui/core/TableHead";
|
||||
import TableRow from "@material-ui/core/TableRow";
|
||||
import Paper from "@material-ui/core/Paper";
|
||||
|
||||
import { LineChart, LineSeries, BarChart } from "reaviz";
|
||||
import { GridStripe } from "reaviz";
|
||||
//import { GridlineSeries } from "reaviz";
|
||||
|
||||
import InputLabel from '@material-ui/core/InputLabel';
|
||||
import FormControl from '@material-ui/core/FormControl';
|
||||
import Select from '@material-ui/core/Select';
|
||||
import MenuItem from '@material-ui/core/MenuItem';
|
||||
|
||||
const data = [
|
||||
{
|
||||
key: new Date("11/29/2019"),
|
||||
data: 10,
|
||||
},
|
||||
{
|
||||
key: new Date("11/30/2019"),
|
||||
data: 14,
|
||||
},
|
||||
{
|
||||
key: new Date("12/01/2019"),
|
||||
data: 5,
|
||||
},
|
||||
{
|
||||
key: new Date("12/02/2019"),
|
||||
data: 18,
|
||||
},
|
||||
];
|
||||
|
||||
const useStyles1 = makeStyles((theme) => ({
|
||||
formControl: {
|
||||
margin: theme.spacing(1),
|
||||
minWidth: 120,
|
||||
},
|
||||
selectEmpty: {
|
||||
marginTop: theme.spacing(2),
|
||||
},
|
||||
}));
|
||||
|
||||
|
||||
const useStyles = makeStyles({
|
||||
table: {
|
||||
minWidth: 650,
|
||||
},
|
||||
root: {
|
||||
minWidth: 275,
|
||||
},
|
||||
bullet: {
|
||||
display: "inline-block",
|
||||
margin: "0 2px",
|
||||
transform: "scale(0.8)",
|
||||
},
|
||||
title: {
|
||||
fontSize: 14,
|
||||
},
|
||||
pos: {
|
||||
marginBottom: 12,
|
||||
},
|
||||
});
|
||||
|
||||
function createData(name, calories, fat, carbs, protein) {
|
||||
return { name, calories, fat, carbs, protein };
|
||||
}
|
||||
|
||||
const rows = [
|
||||
createData("Frozen yoghurt", 159, 6.0, 24, 4.0),
|
||||
createData("Ice cream sandwich", 237, 9.0, 37, 4.3),
|
||||
createData("Eclair", 262, 16.0, 24, 6.0),
|
||||
createData("Cupcake", 305, 3.7, 67, 4.3),
|
||||
createData("Gingerbread", 356, 16.0, 49, 3.9),
|
||||
];
|
||||
|
||||
const DashboardPage = () => {
|
||||
const classes = useStyles();
|
||||
const classes1 = useStyles1();
|
||||
|
||||
const [age, setAge] = React.useState(0);
|
||||
|
||||
const handleChange = (event) => {
|
||||
setAge(event.target.value);
|
||||
|
||||
};
|
||||
|
||||
return (
|
||||
<Container maxWidth="xl">
|
||||
<Grid>
|
||||
<Grid item xl={8} style={{"border":"20px"}}>
|
||||
<center>
|
||||
<Typography type="title" variant="h1" color="inherit">
|
||||
Dashboard
|
||||
</Typography>
|
||||
<div style={{
|
||||
"paddingLeft": "50px"
|
||||
}}>
|
||||
<FormControl className={classes1.formControl}>
|
||||
<InputLabel id="demo-simple-select-label">Organization</InputLabel>
|
||||
<Select
|
||||
labelId="demo-simple-select-label"
|
||||
onChange={handleChange}
|
||||
>
|
||||
<MenuItem value={10}>Ten</MenuItem>
|
||||
<MenuItem value={20}>Twenty</MenuItem>
|
||||
<MenuItem value={30}>Thirty</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
</div>
|
||||
</center>
|
||||
</Grid>
|
||||
<Divider />
|
||||
</Grid>
|
||||
<Grid
|
||||
container
|
||||
spacing={2}
|
||||
style={{
|
||||
maxWidth: "1250px",
|
||||
margin: "auto auto 10px",
|
||||
padding: "10px",
|
||||
}}
|
||||
>
|
||||
<Grid item xs={4}>
|
||||
<Card
|
||||
className={classes.root}
|
||||
style={{
|
||||
color: "white",
|
||||
backgroundColor: "RGB(31, 32, 36)",
|
||||
height: "100px",
|
||||
border: "2px solid rgb(197, 17, 82)",
|
||||
}}
|
||||
>
|
||||
<CardContent>
|
||||
<Typography
|
||||
className={classes.title}
|
||||
color="textSecondary"
|
||||
gutterBottom
|
||||
>
|
||||
Total workflows executions
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="h1"
|
||||
className={classes.pos}
|
||||
color="textSecondary"
|
||||
>
|
||||
456
|
||||
</Typography>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Grid>
|
||||
<Grid item xs={4}>
|
||||
<Card
|
||||
className={classes.root}
|
||||
style={{
|
||||
color: "white",
|
||||
backgroundColor: "RGB(31, 32, 36)",
|
||||
height: "100px",
|
||||
border: "2px solid rgb(244, 194, 13)",
|
||||
}}
|
||||
>
|
||||
<CardContent>
|
||||
<Typography
|
||||
className={classes.title}
|
||||
color="textSecondary"
|
||||
gutterBottom
|
||||
>
|
||||
Total Apps executions
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="h1"
|
||||
className={classes.pos}
|
||||
color="textSecondary"
|
||||
>
|
||||
587
|
||||
</Typography>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Grid>
|
||||
<Grid item xs={4}>
|
||||
<Card
|
||||
className={classes.root}
|
||||
style={{
|
||||
color: "white",
|
||||
backgroundColor: "RGB(31, 32, 36)",
|
||||
height: "100px",
|
||||
border: "2px solid rgb(72, 133, 237)",
|
||||
}}
|
||||
>
|
||||
<CardContent>
|
||||
<Typography
|
||||
className={classes.title}
|
||||
color="textSecondary"
|
||||
gutterBottom
|
||||
>
|
||||
Total failed executions
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="h1"
|
||||
className={classes.pos}
|
||||
color="textSecondary"
|
||||
>
|
||||
999
|
||||
</Typography>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid
|
||||
container
|
||||
spacing={3}
|
||||
style={{
|
||||
maxWidth: "1250px",
|
||||
margin: "auto auto 10px",
|
||||
color: "white",
|
||||
backgroundColor: "rgb(39, 41, 45)",
|
||||
padding: "20px",
|
||||
}}
|
||||
>
|
||||
<Grid item md={6}>
|
||||
<BarChart width={600} height={400} data={data} />
|
||||
</Grid>
|
||||
<Grid item md={6}>
|
||||
<LineChart
|
||||
width={600}
|
||||
height={400}
|
||||
data={data}
|
||||
line={<GridStripe fill={"a#393c3e"} />}
|
||||
series={<LineSeries symbols={null} />}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid
|
||||
container
|
||||
spacing={1}
|
||||
style={{
|
||||
maxWidth: "1250px",
|
||||
margin: "auto auto 10px",
|
||||
color: "white",
|
||||
backgroundColor: "rgb(39, 41, 45)",
|
||||
padding: "20px",
|
||||
}}
|
||||
>
|
||||
<Grid item md={12}>
|
||||
<TableContainer
|
||||
component={Paper}
|
||||
style={{
|
||||
color: "white",
|
||||
backgroundColor: "rgb(39, 41, 45) ",
|
||||
}}
|
||||
>
|
||||
<Table
|
||||
className={classes.table}
|
||||
size="small"
|
||||
aria-label="a dense table"
|
||||
>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell>Dessert (100g serving)</TableCell>
|
||||
<TableCell align="right">Calories</TableCell>
|
||||
<TableCell align="right">Fat (g)</TableCell>
|
||||
<TableCell align="right">Carbs (g)</TableCell>
|
||||
<TableCell align="right">Protein (g)</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{rows.map((row) => (
|
||||
<TableRow key={row.name}>
|
||||
<TableCell component="th" scope="row">
|
||||
{row.name}
|
||||
</TableCell>
|
||||
<TableCell align="right">{row.calories}</TableCell>
|
||||
<TableCell align="right">{row.fat}</TableCell>
|
||||
<TableCell align="right">{row.carbs}</TableCell>
|
||||
<TableCell align="right">{row.protein}</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardPage;
|
||||
@@ -7,7 +7,8 @@ import {
|
||||
Button,
|
||||
} from "@mui/material";
|
||||
import theme from '../theme.jsx';
|
||||
import { useAlert } from "react-alert";
|
||||
//import { useAlert
|
||||
import { ToastContainer, toast } from "react-toastify"
|
||||
|
||||
import AuthenticationOauth2 from "../components/Oauth2Auth.jsx";
|
||||
import AuthenticationWindow from "../components/AuthenticationWindow.jsx";
|
||||
@@ -22,7 +23,7 @@ const SetAuthentication = (props) => {
|
||||
const [appAuthentication, setAppAuthentication] = React.useState([]);
|
||||
|
||||
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
|
||||
const alert = useAlert();
|
||||
//const alert = useAlert();
|
||||
|
||||
const parseIncomingOpenapiData = (data) => {
|
||||
if (data.app === undefined || data.app === null) {
|
||||
@@ -84,7 +85,7 @@ const SetAuthentication = (props) => {
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success === false || responseJson.success === undefined) {
|
||||
alert.error("Failed to get the app. Does it exist?")
|
||||
toast("Failed to get the app. Does it exist?")
|
||||
setIsAppLoaded(true)
|
||||
return;
|
||||
}
|
||||
@@ -92,7 +93,7 @@ const SetAuthentication = (props) => {
|
||||
parseIncomingOpenapiData(responseJson);
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error("Error in app fetch: " + error.toString());
|
||||
toast("Error in app fetch: " + error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -1,316 +0,0 @@
|
||||
import React, { useEffect } from "react";
|
||||
|
||||
import Paper from "@material-ui/core/Paper";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import ButtonBase from "@material-ui/core/ButtonBase";
|
||||
import Button from "@material-ui/core/Button";
|
||||
import List from "@material-ui/core/List";
|
||||
import ListItem from "@material-ui/core/ListItem";
|
||||
import TextField from "@material-ui/core/TextField";
|
||||
import Select from "@material-ui/core/Select";
|
||||
import MenuItem from "@material-ui/core/MenuItem";
|
||||
|
||||
import Dialog from "@material-ui/core/Dialog";
|
||||
import DialogTitle from "@material-ui/core/DialogTitle";
|
||||
import DialogActions from "@material-ui/core/DialogActions";
|
||||
import DialogContent from "@material-ui/core/DialogContent";
|
||||
|
||||
import WebhookImage from "../assets/img/webhook.png";
|
||||
import KafkaImage from "../assets/img/kafka.png";
|
||||
|
||||
const Webhooks = (props) => {
|
||||
const { globalUrl, isLoaded } = props;
|
||||
const validtypes = ["webhook"];
|
||||
|
||||
//const [hooks, setSchedules] = React.useState(hookdata);
|
||||
const [hooks, setHooks] = React.useState([]);
|
||||
const [modalOpen, setModalOpen] = React.useState(false);
|
||||
const [newHookName, setNewHookName] = React.useState("");
|
||||
const [newHookDescription, setNewHookDescription] = React.useState("");
|
||||
const [newHookType, setNewHookType] = React.useState("");
|
||||
const [firstrequest, setFirstrequest] = React.useState(true);
|
||||
const [, setModalError] = React.useState("");
|
||||
|
||||
useEffect(() => {
|
||||
if (firstrequest) {
|
||||
setFirstrequest(false);
|
||||
getAvailableHooks();
|
||||
}
|
||||
});
|
||||
|
||||
const newHook = () => {
|
||||
if (newHookName.length === 0) {
|
||||
setModalError("Missing name in modal");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!validtypes.includes(newHookType)) {
|
||||
setModalError(
|
||||
newHookType + " is not a valid type. Try this: " + validtypes
|
||||
);
|
||||
}
|
||||
|
||||
fetch(globalUrl + "/api/v1/hooks/new", {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
name: newHookName,
|
||||
description: newHookDescription,
|
||||
type: newHookType,
|
||||
}),
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then((responseJson) => {
|
||||
console.log(responseJson);
|
||||
setHooks([]);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
});
|
||||
};
|
||||
|
||||
const getAvailableHooks = () => {
|
||||
fetch(globalUrl + "/api/v1/hooks", {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then((responseJson) => {
|
||||
setHooks(responseJson);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
// window.location.pathname = "/"
|
||||
});
|
||||
};
|
||||
|
||||
const deleteHook = (id) => {
|
||||
if (id === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
fetch(globalUrl + "/api/v1/hooks/" + id + "/delete", {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then((responseJson) => {
|
||||
setHooks([]);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
});
|
||||
};
|
||||
|
||||
const bodyDivStyle = {
|
||||
marginLeft: "20px",
|
||||
marginRight: "20px",
|
||||
width: "1350px",
|
||||
minWidth: "1350px",
|
||||
maxWidth: "1350px",
|
||||
};
|
||||
|
||||
const hookApp = (app) => {
|
||||
// Might be more options, but should be webhook or MQ
|
||||
const appPicture =
|
||||
app.type === "webhook" ? (
|
||||
<img src={WebhookImage} alt="webhook" width="100px" height="100px" />
|
||||
) : (
|
||||
<img src={KafkaImage} alt="MQ" width="100px" height="100px" />
|
||||
);
|
||||
|
||||
return (
|
||||
<Grid container spacing={2} style={{ margin: "10px 10px 10px 10px" }}>
|
||||
<Grid item style={{ marginRight: "10px" }}>
|
||||
<ButtonBase>{appPicture}</ButtonBase>
|
||||
</Grid>
|
||||
{splitter}
|
||||
<Grid item xs={12} sm container style={{ marginLeft: "10px" }}>
|
||||
<Grid item xs container direction="column" spacing={2}>
|
||||
<Grid item xs>
|
||||
<div>
|
||||
<h2>{app.info.name}</h2>
|
||||
</div>
|
||||
<div>Desc: {app.info.description}</div>
|
||||
<div>Status: {app.status}</div>
|
||||
</Grid>
|
||||
<Grid item>{app.action}</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
|
||||
const splitter = (
|
||||
<div
|
||||
style={{
|
||||
width: "1px",
|
||||
backgroundColor: "grey",
|
||||
margin: "5px 5px 5px 5px",
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
const hrefStyle = {
|
||||
color: "#385f71",
|
||||
textDecoration: "none",
|
||||
};
|
||||
|
||||
// FIXME - add Schedule modal
|
||||
const hookPaper = (hook) => {
|
||||
return (
|
||||
<div>
|
||||
<Paper
|
||||
style={{
|
||||
maxWidth: "500px",
|
||||
display: "flex",
|
||||
padding: "10px 10px 10px 10px",
|
||||
marginTop: "10px",
|
||||
}}
|
||||
>
|
||||
<div style={{ flex: "5" }}>{hookApp(hook)}</div>
|
||||
{splitter}
|
||||
<div style={{ flex: "1" }}>
|
||||
<List style={{ backgroundColor: "#ffffff" }}>
|
||||
<ListItem style={{ flex: "1", textAlign: "center" }}>
|
||||
<a href={"/webhooks/" + hook.id} style={hrefStyle}>
|
||||
<Button disabled={false} color="primary">
|
||||
Edit
|
||||
</Button>
|
||||
</a>
|
||||
</ListItem>
|
||||
<ListItem style={{ flex: "1", textAlign: "center" }}>
|
||||
<Button
|
||||
disabled={false}
|
||||
onClick={() => {
|
||||
deleteHook(hook.id);
|
||||
}}
|
||||
color="primary"
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
</ListItem>
|
||||
</List>
|
||||
</div>
|
||||
</Paper>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const modalView = modalOpen ? (
|
||||
<Dialog
|
||||
modal
|
||||
open={modalOpen}
|
||||
onClose={() => {
|
||||
setModalOpen(false);
|
||||
}}
|
||||
>
|
||||
<DialogTitle>Hook configuration</DialogTitle>
|
||||
<DialogContent>
|
||||
<TextField
|
||||
onChange={(event) => {
|
||||
setNewHookName(event.target.value);
|
||||
}}
|
||||
color="primary"
|
||||
placeholder="Name"
|
||||
margin="dense"
|
||||
fullWidth
|
||||
/>
|
||||
<TextField
|
||||
onChange={(event) => {
|
||||
setNewHookDescription(event.target.value);
|
||||
}}
|
||||
color="primary"
|
||||
placeholder="Description"
|
||||
margin="dense"
|
||||
fullWidth
|
||||
/>
|
||||
|
||||
<Select
|
||||
value={newHookType}
|
||||
onChange={(event) => {
|
||||
setNewHookType(event.target.value);
|
||||
}}
|
||||
fullWidth="true"
|
||||
>
|
||||
{validtypes.map((data) => (
|
||||
<MenuItem value={data}>{data}</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={() => setModalOpen(false)} color="primary">
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
disabled={
|
||||
newHookName.length === 0 || !validtypes.includes(newHookType)
|
||||
}
|
||||
onClick={() => {
|
||||
newHook();
|
||||
setModalOpen(false);
|
||||
}}
|
||||
color="primary"
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
) : null;
|
||||
|
||||
const hookmap =
|
||||
hooks.length > 0 ? (
|
||||
<div>{hooks.map((data) => hookPaper(data))}</div>
|
||||
) : (
|
||||
<div style={{ marginTop: "10%", marginLeft: "50%" }}>
|
||||
<Button
|
||||
disabled={false}
|
||||
onClick={() => {
|
||||
setModalOpen(true);
|
||||
}}
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
>
|
||||
CREATE NEW HOOK
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
|
||||
const hookView = (
|
||||
<div style={bodyDivStyle}>
|
||||
<Button
|
||||
disabled={false}
|
||||
onClick={() => {
|
||||
setModalOpen(true);
|
||||
}}
|
||||
color="primary"
|
||||
>
|
||||
New
|
||||
</Button>
|
||||
{hookmap}
|
||||
</div>
|
||||
);
|
||||
|
||||
const loadedCheck = isLoaded ? (
|
||||
<div>
|
||||
{modalView}
|
||||
{hookView}
|
||||
</div>
|
||||
) : (
|
||||
<div></div>
|
||||
);
|
||||
|
||||
// Maybe use gridview or something, idk
|
||||
return <div>{loadedCheck}</div>;
|
||||
};
|
||||
|
||||
export default Webhooks;
|
||||
@@ -1,23 +1,25 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import ReactGA from 'react-ga4';
|
||||
import WelcomeForm2 from "../components/WelcomeForm2.jsx";
|
||||
import {
|
||||
Stepper,
|
||||
Step,
|
||||
StepLabel,
|
||||
} from '@mui/material';
|
||||
import AppFramework from "../components/AppFramework.jsx";
|
||||
import ArrowBackIosIcon from '@mui/icons-material/ArrowBackIos';
|
||||
|
||||
import {
|
||||
ArrorForwardIos as ArrowForwardIosIcon,
|
||||
} from '@mui/icons-material';
|
||||
|
||||
import {
|
||||
Grid,
|
||||
Container,
|
||||
Collapse,
|
||||
Fade,
|
||||
Typography,
|
||||
Paper,
|
||||
Button,
|
||||
Card,
|
||||
CardContent,
|
||||
CardActionArea,
|
||||
Stepper,
|
||||
Step,
|
||||
StepLabel,
|
||||
} from '@mui/material';
|
||||
import theme from '../theme.jsx';
|
||||
import { useNavigate, Link } from "react-router-dom";
|
||||
@@ -193,9 +195,9 @@ const Welcome = (props) => {
|
||||
setFrameworkData({})
|
||||
|
||||
if (responseJson.reason !== undefined) {
|
||||
//alert.error("Failed loading: " + responseJson.reason)
|
||||
//toast("Failed loading: " + responseJson.reason)
|
||||
} else {
|
||||
//alert.error("Failed to load framework for your org.")
|
||||
//toast("Failed to load framework for your org.")
|
||||
}
|
||||
} else {
|
||||
setFrameworkData(responseJson)
|
||||
@@ -417,7 +419,7 @@ const Welcome = (props) => {
|
||||
<Typography variant="h6" style={{textAlign: "center", marginBottom: 25, }}>
|
||||
App Framework
|
||||
</Typography>
|
||||
<Collapse>
|
||||
<Fade>
|
||||
<AppFramework
|
||||
inputUsecase={inputUsecase}
|
||||
frameworkData={frameworkData}
|
||||
@@ -428,20 +430,20 @@ const Welcome = (props) => {
|
||||
isLoggedIn={true}
|
||||
globalUrl={globalUrl}
|
||||
size={0.78}
|
||||
color={theme.palette.platformColor}
|
||||
color={theme.palette.backgroundColor}
|
||||
discoveryWrapper={discoveryWrapper}
|
||||
setDiscoveryWrapper={setDiscoveryWrapper}
|
||||
apps={apps}
|
||||
inputUsecases={usecases}
|
||||
setInputUsecases={setUsecases}
|
||||
/>
|
||||
</Collapse>
|
||||
</Fade>
|
||||
</div>
|
||||
}
|
||||
</Grid>
|
||||
</div>
|
||||
:
|
||||
<Collapse in={true}>
|
||||
<Fade in={true}>
|
||||
<div style={{maxWidth: 700, margin: "auto", marginTop: 50, }}>
|
||||
{/*
|
||||
<div style={{display:"flex"}}>
|
||||
@@ -537,7 +539,7 @@ const Welcome = (props) => {
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
</Collapse>
|
||||
</Fade>
|
||||
}
|
||||
</div>
|
||||
)
|
||||
|
||||
+340
-149
@@ -3,12 +3,8 @@ import ReactDOM from "react-dom"
|
||||
|
||||
import { makeStyles } from "@mui/styles";
|
||||
import { Navigate } from "react-router-dom";
|
||||
//import { Redirect } from "react-router-dom";
|
||||
|
||||
|
||||
import SecurityFramework from '../components/SecurityFramework.jsx';
|
||||
import EditWorkflow from "../components/EditWorkflow.jsx"
|
||||
//import { ShepherdTour, ShepherdTourContext } from 'react-shepherd'
|
||||
import Priority from "../components/Priority.jsx";
|
||||
|
||||
import { isMobile } from "react-device-detect"
|
||||
@@ -32,7 +28,6 @@ import {
|
||||
MenuItem,
|
||||
Chip,
|
||||
Typography,
|
||||
Zoom,
|
||||
CircularProgress,
|
||||
Dialog,
|
||||
DialogTitle,
|
||||
@@ -41,11 +36,10 @@ import {
|
||||
Checkbox,
|
||||
LinearProgress,
|
||||
ListItemText,
|
||||
} from "@mui/material"
|
||||
|
||||
import {
|
||||
AvatarGroup,
|
||||
} from "@mui/material"
|
||||
|
||||
Zoom,
|
||||
} from "@mui/material";
|
||||
|
||||
import {
|
||||
GridOn as GridOnIcon,
|
||||
@@ -81,24 +75,17 @@ import {
|
||||
ArrowRight as ArrowRightIcon,
|
||||
} from "@mui/icons-material";
|
||||
|
||||
//import NestedMenuItem from "material-ui-nested-menu-item";
|
||||
//import {Search as SearchIcon, ArrowUpward as ArrowUpwardIcon, Visibility as VisibilityIcon, Close as CloseIcon, Error as ErrorIcon, FindReplace as FindreplaceIcon, ArrowLeft as ArrowLeftIcon, Cached as CachedIcon, DirectionsRun as DirectionsRunIcon, Add as AddIcon, Polymer as PolymerIcon, FormatListNumbered as FormatListNumberedIcon, Create as CreateIcon, PlayArrow as PlayArrowIcon, AspectRatio as AspectRatioIcon, MoreVert as MoreVertIcon, Apps as AppsIcon, Schedule as ScheduleIcon, FavoriteBorder as FavoriteBorderIcon, Pause as PauseIcon, Delete as DeleteIcon, AddCircleOutline as AddCircleOutlineIcon, Save as SaveIcon, KeyboardArrowLeft as KeyboardArrowLeftIcon, KeyboardArrowRight as KeyboardArrowRightIcon, ArrowBack as ArrowBackIcon, Settings as SettingsIcon, LockOpen as LockOpenIcon, ExpandMore as ExpandMoreIcon, VpnKey as VpnKeyIcon} from '@material-ui/icons';
|
||||
|
||||
//https://next.material-ui.com/components/material-icons/
|
||||
import { DataGrid, GridToolbar } from "@mui/x-data-grid";
|
||||
|
||||
//import JSONPretty from 'react-json-pretty';
|
||||
//import JSONPrettyMon from 'react-json-pretty/dist/monikai'
|
||||
import Dropzone from "../components/Dropzone.jsx";
|
||||
|
||||
import { useNavigate, Link } from "react-router-dom";
|
||||
import { useAlert } from "react-alert";
|
||||
import ChipInput from "material-ui-chip-input";
|
||||
//import { useAlert
|
||||
import { ToastContainer, toast } from "react-toastify"
|
||||
import { MuiChipsInput } from "mui-chips-input";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import theme from "../theme.jsx";
|
||||
|
||||
const inputColor = "#383B40";
|
||||
const surfaceColor = "#27292D";
|
||||
const svgSize = 24;
|
||||
const imagesize = 22;
|
||||
|
||||
@@ -409,16 +396,16 @@ export const validateJson = (showResult) => {
|
||||
}
|
||||
|
||||
if (typeof showResult === "object" || typeof showResult === "array") {
|
||||
return {
|
||||
valid: true,
|
||||
result: showResult,
|
||||
}
|
||||
return {
|
||||
valid: true,
|
||||
result: showResult,
|
||||
}
|
||||
}
|
||||
|
||||
if (showResult[0] === "\"") {
|
||||
return {
|
||||
valid: false,
|
||||
result: showResult,
|
||||
return {
|
||||
valid: false,
|
||||
result: showResult,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -427,10 +414,10 @@ export const validateJson = (showResult) => {
|
||||
if (!showResult.includes("{") && !showResult.includes("[")) {
|
||||
jsonvalid = false
|
||||
|
||||
return {
|
||||
valid: jsonvalid,
|
||||
result: showResult,
|
||||
};
|
||||
return {
|
||||
valid: jsonvalid,
|
||||
result: showResult,
|
||||
};
|
||||
}
|
||||
} catch (e) {
|
||||
showResult = showResult.split("'").join('"');
|
||||
@@ -543,7 +530,7 @@ const Workflows = (props) => {
|
||||
document.title = "Shuffle - Workflows";
|
||||
let navigate = useNavigate();
|
||||
|
||||
const alert = useAlert();
|
||||
//const alert = useAlert();
|
||||
const classes = useStyles(theme);
|
||||
const imgSize = 60;
|
||||
|
||||
@@ -780,7 +767,7 @@ const Workflows = (props) => {
|
||||
}}
|
||||
PaperProps={{
|
||||
style: {
|
||||
backgroundColor: surfaceColor,
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
color: "white",
|
||||
minWidth: 500,
|
||||
padding: 30,
|
||||
@@ -835,7 +822,7 @@ const Workflows = (props) => {
|
||||
}}
|
||||
PaperProps={{
|
||||
style: {
|
||||
backgroundColor: surfaceColor,
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
color: "white",
|
||||
minWidth: 500,
|
||||
padding: 50,
|
||||
@@ -892,7 +879,7 @@ const Workflows = (props) => {
|
||||
}}
|
||||
PaperProps={{
|
||||
style: {
|
||||
backgroundColor: surfaceColor,
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
color: "white",
|
||||
minWidth: 500,
|
||||
},
|
||||
@@ -943,7 +930,7 @@ const Workflows = (props) => {
|
||||
const files = isDropzone ? e.dataTransfer.files : e.target.files;
|
||||
|
||||
const reader = new FileReader();
|
||||
alert.info("Starting upload. Please wait while we validate the workflows");
|
||||
toast("Starting upload. Please wait while we validate the workflows");
|
||||
|
||||
try {
|
||||
reader.addEventListener("load", (e) => {
|
||||
@@ -952,7 +939,7 @@ const Workflows = (props) => {
|
||||
try {
|
||||
data = JSON.parse(reader.result);
|
||||
} catch (e) {
|
||||
alert.error("Invalid JSON: " + e);
|
||||
toast("Invalid JSON: " + e);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -986,13 +973,13 @@ const Workflows = (props) => {
|
||||
data.status
|
||||
).then((response) => {
|
||||
if (response !== undefined) {
|
||||
alert.success(`Successfully imported ${data.name}`);
|
||||
toast(`Successfully imported ${data.name}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error("Import error: " + error.toString());
|
||||
toast("Import error: " + error.toString());
|
||||
});
|
||||
});
|
||||
} catch (e) {
|
||||
@@ -1030,9 +1017,9 @@ const Workflows = (props) => {
|
||||
if (responseJson.success === false) {
|
||||
setAppFramework({})
|
||||
if (responseJson.reason !== undefined) {
|
||||
//alert.error("Failed loading: " + responseJson.reason)
|
||||
//toast("Failed loading: " + responseJson.reason)
|
||||
} else {
|
||||
//alert.error("Failed to load framework for your org.")
|
||||
//toast("Failed to load framework for your org.")
|
||||
}
|
||||
} else {
|
||||
setAppFramework(responseJson)
|
||||
@@ -1060,7 +1047,7 @@ const Workflows = (props) => {
|
||||
// navigate("/search?tab=workflows")
|
||||
//}
|
||||
|
||||
alert.info("Failed getting workflows. Are you logged in?");
|
||||
toast("Failed getting workflows. Are you logged in?");
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -1125,14 +1112,14 @@ const Workflows = (props) => {
|
||||
|
||||
} else {
|
||||
if (isLoggedIn) {
|
||||
alert.error("An error occurred while loading workflows");
|
||||
toast("An error occurred while loading workflows");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1182,7 +1169,7 @@ const Workflows = (props) => {
|
||||
|
||||
setUsecases(newcategories)
|
||||
} else {
|
||||
setUsecases(categorydata)
|
||||
setUsecases(categorydata)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1212,7 +1199,7 @@ const Workflows = (props) => {
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
//alert.error("ERROR: " + error.toString());
|
||||
//toast("ERROR: " + error.toString());
|
||||
console.log("ERROR: " + error.toString());
|
||||
setWorkflows(workflows);
|
||||
setWorkflowDone(true);
|
||||
@@ -1253,7 +1240,7 @@ const Workflows = (props) => {
|
||||
width: "100%",
|
||||
height: "250px",
|
||||
color: "white",
|
||||
backgroundColor: surfaceColor,
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
};
|
||||
@@ -1271,19 +1258,19 @@ const Workflows = (props) => {
|
||||
overflow: "hidden",
|
||||
width: "100%",
|
||||
color: "white",
|
||||
backgroundColor: surfaceColor,
|
||||
padding: "12px 12px 0px 15px",
|
||||
borderRadius: 5,
|
||||
display: "flex",
|
||||
boxSizing: "border-box",
|
||||
position: "relative",
|
||||
padding: "12px 12px 0px 15px",
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
};
|
||||
|
||||
const gridContainer = {
|
||||
height: "auto",
|
||||
color: "white",
|
||||
margin: "10px",
|
||||
backgroundColor: surfaceColor,
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
};
|
||||
|
||||
const workflowActionStyle = {
|
||||
@@ -1302,7 +1289,7 @@ const Workflows = (props) => {
|
||||
}, i * 200);
|
||||
}
|
||||
|
||||
alert.info(`exporting and keeping original for all ${allWorkflows.length} workflows`);
|
||||
toast(`exporting and keeping original for all ${allWorkflows.length} workflows`);
|
||||
};
|
||||
|
||||
const deduplicateIds = (data, skip_sanitize) => {
|
||||
@@ -1343,7 +1330,7 @@ const Workflows = (props) => {
|
||||
trigger.parameters[1].value = "webhook_" + trigger.id;
|
||||
// FIXME: Add auth here?
|
||||
} else {
|
||||
alert.info("Something is wrong with the webhook in the copy");
|
||||
toast("Something is wrong with the webhook in the copy");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1463,7 +1450,7 @@ const Workflows = (props) => {
|
||||
data = sanitizeWorkflow(data);
|
||||
|
||||
if (data.subflows !== null && data.subflows !== undefined) {
|
||||
alert.info(
|
||||
toast(
|
||||
"Not exporting with subflows when sanitizing. Please manually export them."
|
||||
);
|
||||
data.subflows = [];
|
||||
@@ -1491,7 +1478,7 @@ const Workflows = (props) => {
|
||||
const publishWorkflow = (data) => {
|
||||
data = JSON.parse(JSON.stringify(data));
|
||||
data = sanitizeWorkflow(data);
|
||||
alert.info("Sanitizing and publishing " + data.name);
|
||||
toast("Sanitizing and publishing " + data.name);
|
||||
|
||||
// This ALWAYS talks to Shuffle cloud
|
||||
fetch(globalUrl + "/api/v1/workflows/" + data.id + "/publish", {
|
||||
@@ -1508,9 +1495,9 @@ const Workflows = (props) => {
|
||||
console.log("Status not 200 for workflow publish :O!");
|
||||
} else {
|
||||
if (isCloud) {
|
||||
alert.success("Successfully published workflow");
|
||||
toast("Successfully published workflow");
|
||||
} else {
|
||||
alert.success(
|
||||
toast(
|
||||
"Successfully published workflow to https://shuffler.io"
|
||||
);
|
||||
}
|
||||
@@ -1520,20 +1507,20 @@ const Workflows = (props) => {
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson.reason !== undefined) {
|
||||
alert.error("Failed publishing: ", responseJson.reason);
|
||||
toast("Failed publishing: ", responseJson.reason);
|
||||
}
|
||||
|
||||
getAvailableWorkflows();
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error("Failed publishing: is the workflow valid? Remember to save the workflow first.")
|
||||
toast("Failed publishing: is the workflow valid? Remember to save the workflow first.")
|
||||
console.log(error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
const copyWorkflow = (data) => {
|
||||
data = JSON.parse(JSON.stringify(data));
|
||||
alert.success("Copying workflow " + data.name);
|
||||
toast("Copying workflow " + data.name);
|
||||
data.id = "";
|
||||
data.name = data.name + "_copy";
|
||||
data = deduplicateIds(data, true);
|
||||
@@ -1560,7 +1547,7 @@ const Workflows = (props) => {
|
||||
}, 1000);
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1576,9 +1563,9 @@ const Workflows = (props) => {
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
console.log("Status not 200 for setting workflows :O!");
|
||||
alert.error("Failed deleting workflow. Do you have access?");
|
||||
toast("Failed deleting workflow. Do you have access?");
|
||||
} else {
|
||||
alert.success("Deleted workflow " + id);
|
||||
toast("Deleted workflow " + id);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
@@ -1589,7 +1576,7 @@ const Workflows = (props) => {
|
||||
}, 1000);
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1606,6 +1593,7 @@ const Workflows = (props) => {
|
||||
maxWidth: "100%",
|
||||
minWidth: paperAppStyle.width,
|
||||
color: innerColor,
|
||||
padding: paperAppStyle.padding,
|
||||
borderRadius: paperAppStyle.borderRadius,
|
||||
display: "flex",
|
||||
boxSizing: "border-box",
|
||||
@@ -1613,12 +1601,10 @@ const Workflows = (props) => {
|
||||
border: `2px solid ${innerColor}`,
|
||||
cursor: "pointer",
|
||||
backgroundColor: hover ? "rgba(39,41,45,0.5)" : "rgba(39,41,45,1)",
|
||||
|
||||
padding: paperAppStyle.padding,
|
||||
};
|
||||
|
||||
return (
|
||||
<Grid item xs={isMobile ? 12 : 4} style={{ }}>
|
||||
<Grid item xs={isMobile ? 12 : 4} style={{ padding: "12px 10px 12px 10px" }}>
|
||||
<Paper
|
||||
square
|
||||
style={setupPaperStyle}
|
||||
@@ -1711,7 +1697,7 @@ const Workflows = (props) => {
|
||||
}}
|
||||
>
|
||||
<MenuItem
|
||||
style={{ backgroundColor: inputColor, color: "white" }}
|
||||
style={{ backgroundColor: theme.palette.inputColor, color: "white" }}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation()
|
||||
ReactDOM.unstable_batchedUpdates(() => {
|
||||
@@ -1735,7 +1721,7 @@ const Workflows = (props) => {
|
||||
{"Edit details"}
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
style={{ backgroundColor: inputColor, color: "white" }}
|
||||
style={{ backgroundColor: theme.palette.inputColor, color: "white" }}
|
||||
onClick={() => {
|
||||
setSelectedWorkflow(data);
|
||||
setPublishModalOpen(true);
|
||||
@@ -1746,7 +1732,7 @@ const Workflows = (props) => {
|
||||
{"Publish Workflow"}
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
style={{ backgroundColor: inputColor, color: "white" }}
|
||||
style={{ backgroundColor: theme.palette.inputColor, color: "white" }}
|
||||
onClick={() => {
|
||||
copyWorkflow(data);
|
||||
setOpen(false);
|
||||
@@ -1756,15 +1742,8 @@ const Workflows = (props) => {
|
||||
<FileCopyIcon style={{ marginLeft: 0, marginRight: 8 }} />
|
||||
{"Duplicate Workflow"}
|
||||
</MenuItem>
|
||||
{/*<NestedMenuItem disabled={userdata.orgs === undefined || userdata.orgs === null || userdata.orgs.length === 1 || userdata.orgs.length >= 0} style={{backgroundColor: inputColor, color: "white"}} onClick={() => {
|
||||
//copyWorkflow(data)
|
||||
//setOpen(false)
|
||||
}} key={"duplicate"}>
|
||||
<FileCopyIcon style={{marginLeft: 0, marginRight: 8}}/>
|
||||
{"Copy to Child Org"}
|
||||
</NestedMenuItem>*/}
|
||||
<MenuItem
|
||||
style={{ backgroundColor: inputColor, color: "white" }}
|
||||
style={{ backgroundColor: theme.palette.inputColor, color: "white" }}
|
||||
onClick={() => {
|
||||
setExportModalOpen(true);
|
||||
|
||||
@@ -1818,7 +1797,7 @@ const Workflows = (props) => {
|
||||
{"Export Workflow"}
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
style={{ backgroundColor: inputColor, color: "white" }}
|
||||
style={{ backgroundColor: theme.palette.inputColor, color: "white" }}
|
||||
onClick={() => {
|
||||
setDeleteModalOpen(true);
|
||||
setSelectedWorkflowId(data.id);
|
||||
@@ -1910,7 +1889,7 @@ const Workflows = (props) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{width: "100%", position: "relative",}}>
|
||||
<div style={{width: "100%", position: "relative",}}>
|
||||
<Paper square style={paperAppStyle}>
|
||||
{selectedCategory !== "" ?
|
||||
<Tooltip title={`Usecase Category: ${selectedCategory}`} placement="bottom">
|
||||
@@ -2054,7 +2033,7 @@ const Workflows = (props) => {
|
||||
}}
|
||||
onClick={() => {
|
||||
if (subflows === 0) {
|
||||
alert.info("No subflows for " + data.name);
|
||||
toast("No subflows for " + data.name);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2229,9 +2208,9 @@ const Workflows = (props) => {
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success === false) {
|
||||
if (responseJson.reason !== undefined) {
|
||||
alert.error("Error setting workflow: ", responseJson.reason)
|
||||
toast("Error setting workflow: ", responseJson.reason)
|
||||
} else {
|
||||
alert.error("Error setting workflow.")
|
||||
toast("Error setting workflow.")
|
||||
}
|
||||
|
||||
return
|
||||
@@ -2249,14 +2228,14 @@ const Workflows = (props) => {
|
||||
setSubmitLoading(false)
|
||||
setModalOpen(false);
|
||||
} else {
|
||||
//alert.info("Successfully changed basic info for workflow");
|
||||
//toast("Successfully changed basic info for workflow");
|
||||
setModalOpen(false);
|
||||
}
|
||||
|
||||
return responseJson;
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
setSubmitLoading(false)
|
||||
setModalOpen(false);
|
||||
setSubmitLoading(false);
|
||||
@@ -2273,7 +2252,7 @@ const Workflows = (props) => {
|
||||
const file = event.target.files[key];
|
||||
if (file.type !== "application/json") {
|
||||
if (file.type !== undefined) {
|
||||
alert.error("File has to contain valid json");
|
||||
toast("File has to contain valid json");
|
||||
setSubmitLoading(false)
|
||||
}
|
||||
|
||||
@@ -2287,7 +2266,7 @@ const Workflows = (props) => {
|
||||
try {
|
||||
data = JSON.parse(reader.result);
|
||||
} catch (e) {
|
||||
alert.error("Invalid JSON: " + e);
|
||||
toast("Invalid JSON: " + e);
|
||||
setSubmitLoading(false)
|
||||
return;
|
||||
}
|
||||
@@ -2327,13 +2306,13 @@ const Workflows = (props) => {
|
||||
data.status,
|
||||
).then((response) => {
|
||||
if (response !== undefined) {
|
||||
alert.success("Successfully imported " + data.name);
|
||||
toast("Successfully imported " + data.name);
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error("Import error: " + error.toString());
|
||||
toast("Import error: " + error.toString());
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2557,7 +2536,7 @@ const Workflows = (props) => {
|
||||
}}
|
||||
onClick={() => {
|
||||
if (subflows === 0) {
|
||||
alert.info("No subflows for " + data.name);
|
||||
toast("No subflows for " + data.name);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2705,7 +2684,7 @@ const Workflows = (props) => {
|
||||
}}
|
||||
PaperProps={{
|
||||
style: {
|
||||
backgroundColor: surfaceColor,
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
color: "white",
|
||||
minWidth: isMobile ? "90%" : "800px",
|
||||
maxWidth: isMobile ? "90%" : "800px",
|
||||
@@ -2761,7 +2740,7 @@ const Workflows = (props) => {
|
||||
fullWidth
|
||||
/>
|
||||
<div style={{display: "flex", marginTop: 10, }}>
|
||||
<ChipInput
|
||||
<MuiChipsInput
|
||||
style={{ flex: 1}}
|
||||
InputProps={{
|
||||
style: {
|
||||
@@ -2823,7 +2802,7 @@ const Workflows = (props) => {
|
||||
selectedUsecases.push(subcase.name)
|
||||
}
|
||||
|
||||
setUpdate(Math.random());
|
||||
setUpdate(Math.random());
|
||||
setSelectedUsecases(selectedUsecases)
|
||||
}}>
|
||||
<Checkbox style={{color: selectedUsecases.includes(subcase.name) ? usecase.color : theme.palette.inputColor}} checked={selectedUsecases.includes(subcase.name)} />
|
||||
@@ -3067,12 +3046,153 @@ const Workflows = (props) => {
|
||||
</span>
|
||||
);
|
||||
|
||||
// const tourOptions = {
|
||||
// defaultStepOptions: {
|
||||
// classes: "shadow-md bg-purple-dark",
|
||||
// scrollTo: true
|
||||
// },
|
||||
// useModalOverlay: true,
|
||||
// tourName: workflows,
|
||||
// exitOnEsc: true,
|
||||
// }
|
||||
|
||||
// //classes: "custom-class-name-1 custom-class-name-2",
|
||||
// const newSteps = [
|
||||
// {
|
||||
// id: "intro",
|
||||
// scrollTo: true,
|
||||
// beforeShowPromise: function() {
|
||||
// return new Promise(function(resolve) {
|
||||
// setTimeout(function() {
|
||||
// window.scrollTo(0, 0);
|
||||
// resolve();
|
||||
// }, 500);
|
||||
// });
|
||||
// },
|
||||
// buttons: [
|
||||
// {
|
||||
// classes: "shepherd-button-primary",
|
||||
// style: {
|
||||
// backgroundColor: "red",
|
||||
// color: "white",
|
||||
// },
|
||||
// text: "Next",
|
||||
// type: "next"
|
||||
// }
|
||||
// ],
|
||||
// highlightClass: "highlight",
|
||||
// showCancelLink: true,
|
||||
// text: [
|
||||
// "React-Shepherd is a JavaScript library for guiding users through your React app."
|
||||
// ],
|
||||
// when: {
|
||||
// show: () => {
|
||||
// console.log("show step 1");
|
||||
// },
|
||||
// hide: () => {
|
||||
// console.log("hide step 1");
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// id: "second",
|
||||
// attachTo: {
|
||||
// element: "second-step",
|
||||
// on: "top"
|
||||
// },
|
||||
// text: [
|
||||
// "Yuk eksplorasi hasil Tes Minat Bakat-mu dan rekomendasi <b>Jurusan</b> dan Karier."
|
||||
// ],
|
||||
// buttons: [
|
||||
// {
|
||||
// classes: "btn btn-info",
|
||||
// text: "Kembali",
|
||||
// type: "back"
|
||||
// },
|
||||
// {
|
||||
// classes: "btn btn-success",
|
||||
// text: "Saya Mengerti",
|
||||
// type: "cancel"
|
||||
// }
|
||||
// ],
|
||||
// when: {
|
||||
// show: () => {
|
||||
// console.log("show stepp");
|
||||
// },
|
||||
// hide: () => {
|
||||
// console.log("complete step");
|
||||
// }
|
||||
// },
|
||||
// showCancelLink: false,
|
||||
// scrollTo: true,
|
||||
// modalOverlayOpeningPadding: 4,
|
||||
// useModalOverlay: false,
|
||||
// canClickTarget: false
|
||||
// }
|
||||
// ]
|
||||
|
||||
// function TourButton() {
|
||||
// const tour = useContext(ShepherdTourContext);
|
||||
|
||||
// return (
|
||||
// <Button variant="contained" color="primary" onClick={tour.start}>
|
||||
// Start Tour
|
||||
// </Button>
|
||||
// );
|
||||
// }
|
||||
|
||||
const WorkflowView = () => {
|
||||
if (workflows.length === 0) {
|
||||
// Not going there yet
|
||||
//if ((userdata.tutorials !== undefined && userdata.tutorials !== null && !userdata.tutorials.includes("getting-started")) || userdata.tutorials === null) {
|
||||
// return <Navigate to="/getting-started" replace />;
|
||||
//}
|
||||
// Not going there yet
|
||||
//if ((userdata.tutorials !== undefined && userdata.tutorials !== null && !userdata.tutorials.includes("getting-started")) || userdata.tutorials === null) {
|
||||
// return <Navigate to="/getting-started" replace />;
|
||||
//}
|
||||
//return (
|
||||
// <div style={emptyWorkflowStyle}>
|
||||
// <Paper style={boxStyle}>
|
||||
// <div>
|
||||
// <h2>Welcome to Shuffle</h2>
|
||||
// </div>
|
||||
// <div>
|
||||
// <p>
|
||||
// <b>Shuffle</b> is a flexible, easy to use, automation platform
|
||||
// allowing users to integrate their services and devices freely.
|
||||
// It's made to significantly reduce the amount of manual labor,
|
||||
// and is focused on security applications.{" "}
|
||||
// <a
|
||||
// href="/docs/about"
|
||||
// style={{ textDecoration: "none", color: "#f85a3e" }}
|
||||
// >
|
||||
// Click here to learn more.
|
||||
// </a>
|
||||
// </p>
|
||||
// </div>
|
||||
// <div>
|
||||
// If you want to jump straight into it, click here to create your
|
||||
// first workflow:
|
||||
// </div>
|
||||
// <div style={{ display: "flex" }}>
|
||||
// <Button
|
||||
// id="second-step"
|
||||
// color="primary"
|
||||
// style={{ marginTop: "20px" }}
|
||||
// variant="outlined"
|
||||
// onClick={() => setModalOpen(true)}
|
||||
// >
|
||||
// New workflow
|
||||
// </Button>
|
||||
// <span style={{ paddingTop: 20, display: "flex" }}>
|
||||
// <Typography
|
||||
// style={{ marginTop: 5, marginLeft: 30, marginRight: 15 }}
|
||||
// >
|
||||
// ..OR
|
||||
// </Typography>
|
||||
// {workflowButtons}
|
||||
// </span>
|
||||
// </div>
|
||||
// </Paper>
|
||||
// </div>
|
||||
//)
|
||||
}
|
||||
|
||||
var workflowDelay = -150
|
||||
@@ -3084,40 +3204,104 @@ const Workflows = (props) => {
|
||||
<div style={workflowViewStyle}>
|
||||
<div style={{ display: "flex", marginTop: 25, }}>
|
||||
<div style={{ flex: 1 }}>
|
||||
<Typography variant="h1" style={{fontSize: 30}}>
|
||||
Workflows
|
||||
</Typography>
|
||||
<Typography variant="h1" style={{fontSize: 30}}>
|
||||
Workflows
|
||||
</Typography>
|
||||
</div>
|
||||
{isMobile ? null :
|
||||
<div style={{ display: "flex", margin: "0px 0px 20px 0px" }}>
|
||||
<div style={{ flex: 1, float: "right" }}>
|
||||
<ChipInput
|
||||
style={{}}
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
maxWidth: 275,
|
||||
minWidth: 275,
|
||||
},
|
||||
}}
|
||||
placeholder="Add Filter"
|
||||
color="primary"
|
||||
fullWidth
|
||||
value={filters}
|
||||
onAdd={(chip) => {
|
||||
addFilter(chip);
|
||||
}}
|
||||
onDelete={(_, index) => {
|
||||
removeFilter(index);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
{/*
|
||||
<div style={{ flex: 1 }}>
|
||||
<Typography style={{ marginTop: 7, marginBottom: "auto" }}>
|
||||
<a
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
href="https://shuffler.io/docs/workflows"
|
||||
style={{ textDecoration: "none", color: "#f85a3e" }}
|
||||
>
|
||||
Learn more about Workflows
|
||||
</a>
|
||||
</Typography>
|
||||
</div>
|
||||
*/}
|
||||
{isMobile ? null :
|
||||
<div style={{ display: "flex", margin: "0px 0px 20px 0px" }}>
|
||||
<div style={{ flex: 1, float: "right" }}>
|
||||
<MuiChipsInput
|
||||
style={{}}
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
maxWidth: 275,
|
||||
minWidth: 275,
|
||||
},
|
||||
}}
|
||||
placeholder="Filter Workflows"
|
||||
color="primary"
|
||||
fullWidth
|
||||
value={filters}
|
||||
onChange={(chips) => {
|
||||
setFilters(chips);
|
||||
}}
|
||||
onAdd={(chip) => {
|
||||
addFilter(chip);
|
||||
}}
|
||||
onDelete={(_, index) => {
|
||||
removeFilter(index);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div style={{ flex: 1, textAlign: "right", }}>
|
||||
{workflowButtons}
|
||||
</div>
|
||||
</div>
|
||||
{/*
|
||||
<div style={flexContainerStyle}>
|
||||
<div style={{...flexBoxStyle, ...activeWorkflowStyle}}>
|
||||
<div style={flexContentStyle}>
|
||||
<div><img src={mobileImage} style={iconStyle} /></div>
|
||||
<div style={ blockRightStyle }>
|
||||
<div style={counterStyle}>{workflows.length}</div>
|
||||
<div style={fontSize_16}>ACTIVE WORKFLOWS</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{...flexBoxStyle, ...availableWorkflowStyle}}>
|
||||
<div style={flexContentStyle}>
|
||||
<div><img src={bookImage} style={iconStyle} /></div>
|
||||
<div style={ blockRightStyle }>
|
||||
<div style={counterStyle}>{workflows.length}</div>
|
||||
<div style={fontSize_16}>AVAILABE WORKFLOWS</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{...flexBoxStyle, ...notificationStyle}}>
|
||||
<div style={flexContentStyle}>
|
||||
<div><img src={bagImage} style={iconStyle} /></div>
|
||||
<div style={ blockRightStyle }>
|
||||
<div style={counterStyle}>{workflows.length}</div>
|
||||
<div style={fontSize_16}>NOTIFICATIONS</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
*/}
|
||||
|
||||
{/*
|
||||
chipRenderer={({ value, isFocused, isDisabled, handleClick, handleRequestDelete }, key) => {
|
||||
console.log("VALUE: ", value)
|
||||
|
||||
return (
|
||||
<Chip
|
||||
key={key}
|
||||
style={chipStyle}
|
||||
|
||||
>
|
||||
{value}
|
||||
</Chip>
|
||||
)
|
||||
}}
|
||||
*/}
|
||||
|
||||
<div style={{width: "100%", minHeight: isMobile ? 0 : 51, maxHeight: isMobile ? 0 : 51, marginTop: 10, }}>
|
||||
{!isMobile && usecases !== null && usecases !== undefined && usecases.length > 0 ?
|
||||
@@ -3125,6 +3309,7 @@ const Workflows = (props) => {
|
||||
{usecases.map((usecase, index) => {
|
||||
//console.log(usecase)
|
||||
const percentDone = usecase.matches.length > 0 ? parseInt(usecase.matches.length/usecase.list.length*100) : 0
|
||||
//console.log("Usecase Matches: ", usecase.matches, ", Percent: ", percentDone)
|
||||
|
||||
return (
|
||||
<Paper
|
||||
@@ -3166,7 +3351,10 @@ const Workflows = (props) => {
|
||||
</div>
|
||||
|
||||
<div style={{ marginTop: 10, marginBottom: 10, }} />
|
||||
{!isMobile && actionImageList !== undefined && actionImageList !== null && actionImageList.length > 0 ? (
|
||||
{!isMobile &&
|
||||
actionImageList !== undefined &&
|
||||
actionImageList !== null &&
|
||||
actionImageList.length > 0 ? (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
@@ -3255,10 +3443,11 @@ const Workflows = (props) => {
|
||||
return returnData
|
||||
}
|
||||
|
||||
/*<Zoom key={index} in={true} style={{ transitionDelay: `${appDelay}ms` }}>*/
|
||||
return (
|
||||
<span>
|
||||
{/*<Zoom key={index} in={true} style={{ transitionDelay: `${appDelay}ms` }}>*/}
|
||||
{returnData}
|
||||
{/*</Zoom>*/}
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
@@ -3296,11 +3485,13 @@ const Workflows = (props) => {
|
||||
/>
|
||||
: null}
|
||||
|
||||
{/*<Zoom in={true} style={{ transitionDelay: `${workflowDelay}ms` }}>*/}
|
||||
<div style={{marginTop: 15, marginBottom: 50, }}>
|
||||
<div style={{marginTop: 30, marginBottom: 50, marginLeft: 30, }}>
|
||||
{view === "grid" ? (
|
||||
<Grid container spacing={filteredWorkflows.length === 0 ? 12 : filteredWorkflows.length === 1 ? 6 : 4} style={paperAppContainer}>
|
||||
<NewWorkflowPaper />
|
||||
{/*<Zoom in={true} style={{ transitionDelay: `${workflowDelay}ms` }}>*/}
|
||||
<NewWorkflowPaper />
|
||||
{/*</Zoom>*/}
|
||||
|
||||
{filteredWorkflows.map((data, index) => {
|
||||
// Shouldn't be a part of this list
|
||||
if (data.public === true) {
|
||||
@@ -3311,20 +3502,20 @@ const Workflows = (props) => {
|
||||
workflowDelay += 75
|
||||
} else {
|
||||
return (
|
||||
<Zoom key={index} in={true} style={{ transitionDelay: `${workflowDelay}ms` }}>
|
||||
<Grid key={index} item xs={isMobile ? 12 : 4} style={{}}>
|
||||
<WorkflowPaper key={index} data={data} />
|
||||
</Grid>
|
||||
</Zoom>
|
||||
<Grid key={index} item xs={isMobile ? 12 : 4} style={{ padding: "12px 10px 12px 10px" }}>
|
||||
<WorkflowPaper key={index} data={data} />
|
||||
</Grid>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Zoom key={index} in={true} style={{ transitionDelay: `${workflowDelay}ms` }}>
|
||||
<Grid item xs={isMobile ? 12 : 4} style={{}}>
|
||||
<span>
|
||||
{/*<Zoom key={index} in={true} style={{ transitionDelay: `${workflowDelay}ms` }}>*/}
|
||||
<Grid item xs={isMobile ? 12 : 4} style={{ padding: "12px 10px 12px 10px" }}>
|
||||
<WorkflowPaper key={index} data={data} />
|
||||
</Grid>
|
||||
</Zoom>
|
||||
{/*</Zoom>*/}
|
||||
</span>
|
||||
)
|
||||
})}
|
||||
</Grid>
|
||||
@@ -3364,7 +3555,7 @@ const Workflows = (props) => {
|
||||
parsedData["field_2"] = field2;
|
||||
}
|
||||
|
||||
alert.success("Getting specific workflows from your URL.");
|
||||
toast("Getting specific workflows from your URL.");
|
||||
fetch(globalUrl + "/api/v1/workflows/download_remote", {
|
||||
method: "POST",
|
||||
mode: "cors",
|
||||
@@ -3376,7 +3567,7 @@ const Workflows = (props) => {
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status === 200) {
|
||||
alert.success("Successfully loaded workflows from " + downloadUrl);
|
||||
toast("Successfully loaded workflows from " + downloadUrl);
|
||||
setTimeout(() => {
|
||||
getAvailableWorkflows();
|
||||
}, 1000);
|
||||
@@ -3387,14 +3578,14 @@ const Workflows = (props) => {
|
||||
.then((responseJson) => {
|
||||
if (!responseJson.success) {
|
||||
if (responseJson.reason !== undefined) {
|
||||
alert.error("Failed loading: " + responseJson.reason);
|
||||
toast("Failed loading: " + responseJson.reason);
|
||||
} else {
|
||||
alert.error("Failed loading");
|
||||
toast("Failed loading");
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
@@ -3409,7 +3600,7 @@ const Workflows = (props) => {
|
||||
onClose={() => {}}
|
||||
PaperProps={{
|
||||
style: {
|
||||
backgroundColor: surfaceColor,
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
color: "white",
|
||||
minWidth: "800px",
|
||||
minHeight: "320px",
|
||||
@@ -3436,7 +3627,7 @@ const Workflows = (props) => {
|
||||
<DialogContent style={{ color: "rgba(255,255,255,0.65)" }}>
|
||||
Repository (supported: github, gitlab, bitbucket)
|
||||
<TextField
|
||||
style={{ backgroundColor: inputColor }}
|
||||
style={{ backgroundColor: theme.palette.inputColor }}
|
||||
variant="outlined"
|
||||
margin="normal"
|
||||
defaultValue={downloadUrl}
|
||||
@@ -3456,7 +3647,7 @@ const Workflows = (props) => {
|
||||
</span>
|
||||
<div style={{ display: "flex" }}>
|
||||
<TextField
|
||||
style={{ backgroundColor: inputColor }}
|
||||
style={{ backgroundColor: theme.palette.inputColor }}
|
||||
variant="outlined"
|
||||
margin="normal"
|
||||
defaultValue={downloadBranch}
|
||||
@@ -3477,7 +3668,7 @@ const Workflows = (props) => {
|
||||
</span>
|
||||
<div style={{ display: "flex" }}>
|
||||
<TextField
|
||||
style={{ flex: 1, backgroundColor: inputColor }}
|
||||
style={{ flex: 1, backgroundColor: theme.palette.inputColor }}
|
||||
variant="outlined"
|
||||
margin="normal"
|
||||
InputProps={{
|
||||
@@ -3493,7 +3684,7 @@ const Workflows = (props) => {
|
||||
fullWidth
|
||||
/>
|
||||
<TextField
|
||||
style={{ flex: 1, backgroundColor: inputColor }}
|
||||
style={{ flex: 1, backgroundColor: theme.palette.inputColor }}
|
||||
variant="outlined"
|
||||
margin="normal"
|
||||
InputProps={{
|
||||
@@ -3643,7 +3834,7 @@ const Workflows = (props) => {
|
||||
}}
|
||||
PaperProps={{
|
||||
style: {
|
||||
backgroundColor: surfaceColor,
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
color: "white",
|
||||
minWidth: 560,
|
||||
minHeight: 415,
|
||||
@@ -3728,7 +3919,7 @@ const Workflows = (props) => {
|
||||
setWorkflow={setEditingWorkflow}
|
||||
modalOpen={modalOpen}
|
||||
setModalOpen={setModalOpen}
|
||||
usecases={usecases}
|
||||
usecases={usecases}
|
||||
setNewWorkflow={setNewWorkflow}
|
||||
appFramework={appFramework}
|
||||
isEditing={isEditing}
|
||||
|
||||
Reference in New Issue
Block a user