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
|
||||
Reference in New Issue
Block a user