Fixed last material-ui references
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import ReactGA from 'react-ga4';
|
||||
import theme from '../theme';
|
||||
//import { useTheme } from '@material-ui/core/styles';
|
||||
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';
|
||||
|
||||
@@ -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,7 +1,6 @@
|
||||
import React, {useState} from 'react';
|
||||
import theme from '../theme.jsx';
|
||||
import {BrowserView, MobileView} from "react-device-detect";
|
||||
//import { useTheme } from '@material-ui/core/styles';
|
||||
|
||||
import { useNavigate, Link } from "react-router-dom";
|
||||
import ReactGA from 'react-ga4';
|
||||
|
||||
@@ -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,6 +1,5 @@
|
||||
import React, { useRef, useState, useEffect, useLayoutEffect } from "react";
|
||||
import { useParams, useNavigate, Link } from "react-router-dom";
|
||||
//import { useTheme } from "@material-ui/core/styles";
|
||||
import theme from '../theme.jsx';
|
||||
import { useAlert } from "react-alert";
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ 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";
|
||||
|
||||
@@ -86,7 +85,6 @@ import {
|
||||
Circle as CircleIcon,
|
||||
SquareFoot as SquareFootIcon,
|
||||
} from '@mui/icons-material';
|
||||
//} from "@material-ui/icons";
|
||||
|
||||
|
||||
//import CodeMirror from "@uiw/react-codemirror";
|
||||
@@ -113,7 +111,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",
|
||||
},
|
||||
|
||||
@@ -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,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
|
||||
@@ -369,7 +369,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",
|
||||
},
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -83,7 +83,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",
|
||||
},
|
||||
|
||||
@@ -93,7 +93,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",
|
||||
},
|
||||
|
||||
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;
|
||||
@@ -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';
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -75,14 +75,8 @@ 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";
|
||||
@@ -1747,13 +1741,6 @@ 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: theme.palette.inputColor, color: "white"}} onClick={() => {
|
||||
//copyWorkflow(data)
|
||||
//setOpen(false)
|
||||
}} key={"duplicate"}>
|
||||
<FileCopyIcon style={{marginLeft: 0, marginRight: 8}}/>
|
||||
{"Copy to Child Org"}
|
||||
</NestedMenuItem>*/}
|
||||
<MenuItem
|
||||
style={{ backgroundColor: theme.palette.inputColor, color: "white" }}
|
||||
onClick={() => {
|
||||
|
||||
Reference in New Issue
Block a user