Loads of frontend fixes related to React & MUI

This commit is contained in:
Frikky
2023-09-19 14:37:41 +02:00
parent 45a0583728
commit c1c34ff72a
17 changed files with 1961 additions and 971 deletions
+17 -13
View File
@@ -3,6 +3,7 @@ import React, {useEffect, useState} from 'react';
import theme from '../theme.jsx'; import theme from '../theme.jsx';
import ReactGA from 'react-ga4'; import ReactGA from 'react-ga4';
import {Link} from 'react-router-dom'; import {Link} from 'react-router-dom';
import { removeQuery } from '../components/ScrollToTop.jsx';
import { import {
Search as SearchIcon, Search as SearchIcon,
@@ -89,21 +90,24 @@ const AppGrid = props => {
} }
const SearchBox = ({currentRefinement, refine, isSearchStalled} ) => { const SearchBox = ({currentRefinement, refine, isSearchStalled} ) => {
useEffect(() => { var defaultSearch = ""
if (window !== undefined && window.location !== undefined && window.location.search !== undefined && window.location.search !== null) { //useEffect(() => {
const urlSearchParams = new URLSearchParams(window.location.search) if (window !== undefined && window.location !== undefined && window.location.search !== undefined && window.location.search !== null) {
const params = Object.fromEntries(urlSearchParams.entries()) const urlSearchParams = new URLSearchParams(window.location.search)
const foundQuery = params["q"] const params = Object.fromEntries(urlSearchParams.entries())
if (foundQuery !== null && foundQuery !== undefined) { const foundQuery = params["q"]
console.log("Got query: ", foundQuery) if (foundQuery !== null && foundQuery !== undefined) {
refine(foundQuery) console.log("Got query: ", foundQuery)
} refine(foundQuery)
defaultSearch = foundQuery
} }
}, []) }
//}, [])
return ( return (
<form noValidate action="" role="search"> <form noValidate action="" role="search">
<TextField <TextField
defaultValue={defaultSearch}
fullWidth fullWidth
style={{backgroundColor: theme.palette.inputColor, borderRadius: borderRadius, margin: 10, width: "100%",}} style={{backgroundColor: theme.palette.inputColor, borderRadius: borderRadius, margin: 10, width: "100%",}}
InputProps={{ InputProps={{
@@ -121,10 +125,12 @@ const AppGrid = props => {
autoComplete='off' autoComplete='off'
type="search" type="search"
color="primary" color="primary"
defaultValue={currentRefinement}
placeholder="Find Apps..." placeholder="Find Apps..."
id="shuffle_search_field" id="shuffle_search_field"
onChange={(event) => { onChange={(event) => {
// Remove "q" from URL
removeQuery("q")
refine(event.currentTarget.value) refine(event.currentTarget.value)
}} }}
limit={5} limit={5}
@@ -151,8 +157,6 @@ const AppGrid = props => {
// setInnerHits(hits) // setInnerHits(hits)
//} //}
console.log("In appgrid")
return ( return (
<Grid container spacing={2}> <Grid container spacing={2}>
{hits.map((data, index) => { {hits.map((data, index) => {
+12 -10
View File
@@ -3,6 +3,7 @@ import React, { useEffect, useState } from 'react';
import ReactGA from 'react-ga4'; import ReactGA from 'react-ga4';
import {Link} from 'react-router-dom'; import {Link} from 'react-router-dom';
import theme from '../theme.jsx'; import theme from '../theme.jsx';
import { removeQuery } from '../components/ScrollToTop.jsx';
import { import {
SkipNext as SkipNextIcon, SkipNext as SkipNextIcon,
@@ -97,20 +98,21 @@ const CreatorGrid = props => {
// value={currentRefinement} // value={currentRefinement}
const SearchBox = ({currentRefinement, refine, isSearchStalled} ) => { const SearchBox = ({currentRefinement, refine, isSearchStalled} ) => {
useEffect(() => { var defaultSearch = ""
if (window !== undefined && window.location !== undefined && window.location.search !== undefined && window.location.search !== null) { if (window !== undefined && window.location !== undefined && window.location.search !== undefined && window.location.search !== null) {
const urlSearchParams = new URLSearchParams(window.location.search) const urlSearchParams = new URLSearchParams(window.location.search)
const params = Object.fromEntries(urlSearchParams.entries()) const params = Object.fromEntries(urlSearchParams.entries())
const foundQuery = params["q"] const foundQuery = params["q"]
if (foundQuery !== null && foundQuery !== undefined) { if (foundQuery !== null && foundQuery !== undefined) {
refine(foundQuery) refine(foundQuery)
} defaultSearch = foundQuery
} }
}, []) }
return ( return (
<form noValidate action="" role="search"> <form noValidate action="" role="search">
<TextField <TextField
defaultValue={defaultSearch}
fullWidth fullWidth
style={{backgroundColor: theme.palette.inputColor, borderRadius: borderRadius, margin: 10, width: "100%",}} style={{backgroundColor: theme.palette.inputColor, borderRadius: borderRadius, margin: 10, width: "100%",}}
InputProps={{ InputProps={{
@@ -128,10 +130,10 @@ const CreatorGrid = props => {
autoComplete='off' autoComplete='off'
type="search" type="search"
color="primary" color="primary"
value={currentRefinement}
placeholder="Find Creators..." placeholder="Find Creators..."
id="shuffle_search_field" id="shuffle_search_field"
onChange={(event) => { onChange={(event) => {
removeQuery("q")
refine(event.currentTarget.value) refine(event.currentTarget.value)
}} }}
/> />
+13 -11
View File
@@ -3,6 +3,7 @@ import React, {useEffect, useState} from 'react';
import theme from '../theme.jsx'; import theme from '../theme.jsx';
import ReactGA from 'react-ga4'; import ReactGA from 'react-ga4';
import {Link} from 'react-router-dom'; import {Link} from 'react-router-dom';
import { removeQuery } from '../components/ScrollToTop.jsx';
import { Search as SearchIcon, CloudQueue as CloudQueueIcon, Code as CodeIcon, Close as CloseIcon, Folder as FolderIcon, LibraryBooks as LibraryBooksIcon } from '@mui/icons-material'; import { Search as SearchIcon, CloudQueue as CloudQueueIcon, Code as CodeIcon, Close as CloseIcon, Folder as FolderIcon, LibraryBooks as LibraryBooksIcon } from '@mui/icons-material';
import aa from 'search-insights' import aa from 'search-insights'
@@ -84,21 +85,22 @@ const DocsGrid = props => {
} }
const SearchBox = ({currentRefinement, refine, isSearchStalled} ) => { const SearchBox = ({currentRefinement, refine, isSearchStalled} ) => {
useEffect(() => { var defaultSearch = ""
if (window !== undefined && window.location !== undefined && window.location.search !== undefined && window.location.search !== null) { if (window !== undefined && window.location !== undefined && window.location.search !== undefined && window.location.search !== null) {
const urlSearchParams = new URLSearchParams(window.location.search) const urlSearchParams = new URLSearchParams(window.location.search)
const params = Object.fromEntries(urlSearchParams.entries()) const params = Object.fromEntries(urlSearchParams.entries())
const foundQuery = params["q"] const foundQuery = params["q"]
if (foundQuery !== null && foundQuery !== undefined) { if (foundQuery !== null && foundQuery !== undefined) {
console.log("Got query: ", foundQuery) console.log("Got query: ", foundQuery)
refine(foundQuery) refine(foundQuery)
} defaultSearch = foundQuery
} }
}, []) }
return ( return (
<form noValidate action="" role="search"> <form noValidate action="" role="search">
<TextField <TextField
defaultValue={defaultSearch}
fullWidth fullWidth
style={{backgroundColor: theme.palette.inputColor, borderRadius: borderRadius, margin: 10, width: "100%",}} style={{backgroundColor: theme.palette.inputColor, borderRadius: borderRadius, margin: 10, width: "100%",}}
InputProps={{ InputProps={{
@@ -116,10 +118,10 @@ const DocsGrid = props => {
autoComplete='off' autoComplete='off'
type="search" type="search"
color="primary" color="primary"
defaultValue={currentRefinement}
placeholder="Search our Documentation..." placeholder="Search our Documentation..."
id="shuffle_search_field" id="shuffle_search_field"
onChange={(event) => { onChange={(event) => {
removeQuery("q")
refine(event.currentTarget.value) refine(event.currentTarget.value)
}} }}
limit={5} limit={5}
+1 -1
View File
@@ -521,7 +521,7 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho
} }
// Handle top bar or something // Handle top bar or something
const defaultTop = 7 const defaultTop = 0
const loginTextBrowser = !isLoggedIn ? const loginTextBrowser = !isLoggedIn ?
<div style={{display: "flex", minWidth: 1250, maxWidth: 1250, margin: "auto", textAlign: "center",}}> <div style={{display: "flex", minWidth: 1250, maxWidth: 1250, margin: "auto", textAlign: "center",}}>
<div style={{display: "flex", flex: 1, }}> <div style={{display: "flex", flex: 1, }}>
+3 -22
View File
@@ -218,7 +218,7 @@ const AuthenticationOauth2 = (props) => {
"31cb4c84-658e-43d5-ae84-22c9142e967a", "31cb4c84-658e-43d5-ae84-22c9142e967a",
"", "",
"https://graph.microsoft.com", "https://graph.microsoft.com",
["ChannelMessage.Edit", "ChannelMessage.Read.All", "ChannelMessage.Send", "Chat.Create", "Chat.ReadWrite", "Chat.Read", "offline_access"], ["ChannelMessage.Edit", "ChannelMessage.Read.All", "ChannelMessage.Send", "Chat.Create", "Chat.ReadWrite", "Chat.Read", "offline_access", "Team.ReadBasic.All"],
admin_consent, admin_consent,
) )
} else if (selectedApp.name.toLowerCase().includes("todoist")) { } else if (selectedApp.name.toLowerCase().includes("todoist")) {
@@ -607,7 +607,7 @@ const AuthenticationOauth2 = (props) => {
<div> <div>
<DialogTitle> <DialogTitle>
<div style={{ color: "white" }}> <div style={{ color: "white" }}>
Authentication for {selectedApp.name} Authenticate {selectedApp.name.replaceAll("_", " ")}
</div> </div>
</DialogTitle> </DialogTitle>
<DialogContent> <DialogContent>
@@ -674,11 +674,6 @@ const AuthenticationOauth2 = (props) => {
style={{backgroundColor: theme.palette.inputColor, borderRadius: theme.palette.borderRadius,}} style={{backgroundColor: theme.palette.inputColor, borderRadius: theme.palette.borderRadius,}}
InputProps={{ InputProps={{
style:{ style:{
color: "white",
marginLeft: "5px",
maxWidth: "95%",
height: 50,
fontSize: "1em",
}, },
}} }}
fullWidth fullWidth
@@ -763,11 +758,6 @@ const AuthenticationOauth2 = (props) => {
}} }}
InputProps={{ InputProps={{
style: { style: {
color: "white",
marginLeft: "5px",
maxWidth: "95%",
height: 50,
fontSize: "1em",
}, },
}} }}
fullWidth fullWidth
@@ -804,11 +794,6 @@ const AuthenticationOauth2 = (props) => {
}} }}
InputProps={{ InputProps={{
style: { style: {
color: "white",
marginLeft: "5px",
maxWidth: "95%",
fontSize: "1em",
height: "50px",
}, },
}} }}
fullWidth fullWidth
@@ -827,11 +812,6 @@ const AuthenticationOauth2 = (props) => {
}} }}
InputProps={{ InputProps={{
style: { style: {
color: "white",
marginLeft: "5px",
maxWidth: "95%",
fontSize: "1em",
height: "50px",
}, },
}} }}
fullWidth fullWidth
@@ -848,6 +828,7 @@ const AuthenticationOauth2 = (props) => {
Scopes Scopes
<Select <Select
multiple multiple
underline={false}
value={selectedScopes} value={selectedScopes}
style={{ style={{
backgroundColor: theme.palette.inputColor, backgroundColor: theme.palette.inputColor,
+124 -93
View File
@@ -11,7 +11,7 @@ import { NestedMenuItem } from "mui-nested-menu";
//import { useAlert //import { useAlert
import { import {
ButtonGroup, ButtonGroup,
Popper, Popper,
TextField, TextField,
TextareaAutosize, TextareaAutosize,
@@ -376,66 +376,57 @@ const ParsedAction = (props) => {
const [menuPosition, setMenuPosition] = useState(null); const [menuPosition, setMenuPosition] = useState(null);
useEffect(() => { useEffect(() => {
if ( if (selectedActionParameters !== undefined && selectedActionParameters !== null && selectedActionParameters.length === 0
selectedActionParameters !== null &&
selectedActionParameters.length === 0
) { ) {
if ( if (selectedAction.parameters !== undefined && selectedAction.parameters !== null && selectedAction.parameters.length > 0) {
selectedAction.parameters !== null &&
selectedAction.parameters.length > 0
) {
setSelectedActionParameters(selectedAction.parameters); setSelectedActionParameters(selectedAction.parameters);
} }
} }
if ( if ((selectedVariableParameter === null || selectedVariableParameter === undefined) && workflow.workflow_variables !== null && workflow.workflow_variables.length > 0) {
(selectedVariableParameter === null ||
selectedVariableParameter === undefined) &&
workflow.workflow_variables !== null &&
workflow.workflow_variables.length > 0
) {
// FIXME - this is the bad thing // FIXME - this is the bad thing
setSelectedVariableParameter(workflow.workflow_variables[0].name); setSelectedVariableParameter(workflow.workflow_variables[0].name);
} }
if (actionlist.length === 0) { if (actionlist.length === 0) {
// FIXME: Have previous execution values in here // FIXME: Have previous execution values in here
if (workflowExecutions.length > 0) { if (workflowExecutions.length > 0) {
for (let [key,keyval] in Object.entries(workflowExecutions)) { for (let [key,keyval] in Object.entries(workflowExecutions)) {
if ( if (
workflowExecutions[key].execution_argument === undefined || workflowExecutions[key].execution_argument === undefined ||
workflowExecutions[key].execution_argument === null || workflowExecutions[key].execution_argument === null ||
workflowExecutions[key].execution_argument.length === 0 workflowExecutions[key].execution_argument.length === 0
) { ) {
continue; continue;
}
const valid = validateJson(workflowExecutions[key].execution_argument)
if (valid.valid) {
actionlist.push({
type: "Execution Argument",
name: "Execution Argument",
value: "$exec",
highlight: "exec",
autocomplete: "exec",
example: valid.result,
})
break
}
} }
const valid = validateJson(workflowExecutions[key].execution_argument)
if (valid.valid) {
actionlist.push({
type: "Execution Argument",
name: "Execution Argument",
value: "$exec",
highlight: "exec",
autocomplete: "exec",
example: valid.result,
})
break
}
} }
if (actionlist.length === 0) { }
actionlist.push({
type: "Execution Argument", if (actionlist.length === 0) {
name: "Execution Argument", actionlist.push({
value: "$exec", type: "Execution Argument",
highlight: "exec", name: "Execution Argument",
autocomplete: "exec", value: "$exec",
example: "", highlight: "exec",
}) autocomplete: "exec",
} example: "",
})
}
actionlist.push({ actionlist.push({
type: "Shuffle DB", type: "Shuffle DB",
@@ -1313,26 +1304,26 @@ const ParsedAction = (props) => {
data.value = data.example; data.value = data.example;
} }
// In case of data.example // In case of data.example
if (data.value === undefined || data.value === null) { if (data.value === undefined || data.value === null) {
data.value = ""
}
if (data.value.length === 0) {
if (data.name.toLowerCase() === "headers") {
console.log("Should show headers field instead with + and -!")
// Check if file ID exists
//
const fileFound = selectedActionParameters.find(param => param.name === "file_id")
if (fileFound === undefined || fileFound === null) {
data.value = data.example
} else {
// Purposely unset it if set by default when using files
data.value = "" data.value = ""
} }
}
if (data.value.length === 0) { }
if (data.name.toLowerCase() === "headers") {
console.log("Should show headers field instead with + and -!")
// Check if file ID exists
//
const fileFound = selectedActionParameters.find(param => param.name === "file_id")
if (fileFound === undefined || fileFound === null) {
data.value = data.example
} else {
// Purposely unset it if set by default when using files
data.value = ""
}
}
}
/* /*
if (data.name !== "queries" && data.name !== "key" && data.name !== "value" ) { if (data.name !== "queries" && data.name !== "key" && data.name !== "value" ) {
@@ -1391,7 +1382,7 @@ const ParsedAction = (props) => {
var hideBodyButton = ""; var hideBodyButton = "";
const hideBodyButtonValue = ( const hideBodyButtonValue = (
<div <div
key={data.name} key={data.name}
style={{ style={{
marginTop: 25, marginTop: 25,
border: "1px solid rgba(255,255,255,0.7)", border: "1px solid rgba(255,255,255,0.7)",
@@ -1403,7 +1394,7 @@ const ParsedAction = (props) => {
> >
<Tooltip <Tooltip
color="secondary" color="secondary"
title={"Automatically change body"} title={"Show all body fields"}
placement="top" placement="top"
> >
<FormControlLabel <FormControlLabel
@@ -1424,15 +1415,15 @@ const ParsedAction = (props) => {
for (let paramkey in Object.entries(selectedActionParameters)) { for (let paramkey in Object.entries(selectedActionParameters)) {
var currentItem = selectedActionParameters[paramkey]; var currentItem = selectedActionParameters[paramkey];
if (currentItem.name === "ssl_verify") { if (currentItem.name === "ssl_verify") {
} }
if (currentItem.name === "body") { if (currentItem.name === "body") {
// FIXME: Workaround for toggling, as actions don't have IDs. // FIXME: Workaround for toggling, as actions don't have IDs.
// May screw up something in the future. // May screw up something in the future.
currentItem.id = tag currentItem.id = tag
} }
if (currentItem.description === openApiFieldDesc) { if (currentItem.description === openApiFieldDesc) {
currentItem.field_active = !hideBody; currentItem.field_active = !hideBody;
@@ -1458,8 +1449,8 @@ const ParsedAction = (props) => {
if (found === null) { if (found === null) {
setActivateHidingBodyButton(true); setActivateHidingBodyButton(true);
} else { } else {
//console.log("In found: ", found, hideBody) //console.log("In found: ", found, hideBody)
} }
} else { } else {
//console.log("SHOW BUTTON"); //console.log("SHOW BUTTON");
@@ -1485,6 +1476,17 @@ const ParsedAction = (props) => {
} }
changed = true; changed = true;
var isRequired = false
// Check if original field name is in the selectedAction.required_body_fields
if (selectedAction.required_body_fields !== undefined && selectedAction.required_body_fields !== null) {
for (let innerkey in selectedAction.required_body_fields) {
if (selectedAction.required_body_fields[innerkey] === tmpitem) {
isRequired = true
break
}
}
}
selectedActionParameters.push({ selectedActionParameters.push({
action_field: "", action_field: "",
configuration: false, configuration: false,
@@ -1494,7 +1496,7 @@ const ParsedAction = (props) => {
multiline: true, multiline: true,
name: tmpitem, name: tmpitem,
options: null, options: null,
required: false, required: isRequired,
schema: { type: "string" }, schema: { type: "string" },
skip_multicheck: false, skip_multicheck: false,
tags: null, tags: null,
@@ -2443,8 +2445,8 @@ const ParsedAction = (props) => {
//console.log(data.configuration) //console.log(data.configuration)
const buttonTitle = `Authenticate ${selectedApp.name.replaceAll("_", " ")}` const buttonTitle = `Authenticate ${selectedApp.name.replaceAll("_", " ")}`
const hasAutocomplete = data.autocompleted === true const hasAutocomplete = data.autocompleted === true
return ( return (
<div key={data.name}> <div key={data.name}>
{hideBodyButton} {hideBodyButton}
@@ -2669,8 +2671,6 @@ const ParsedAction = (props) => {
const { data, newActionname, newActiondescription, useIcon, extraDescription, } = actionprops; const { data, newActionname, newActiondescription, useIcon, extraDescription, } = actionprops;
const [hover, setHover] = React.useState(false); const [hover, setHover] = React.useState(false);
console.log("Extra desc: ", extraDescription)
return ( return (
<Tooltip <Tooltip
color="secondary" color="secondary"
@@ -2893,15 +2893,35 @@ const ParsedAction = (props) => {
> >
<Tooltip <Tooltip
color="primary" color="primary"
title={"Find related workflows"} title={"Find related tworkflows"}
placement="top" placement="top"
> >
<a href={`https://shuffler.io/search?tab=workflows&q=${selectedAction.app_name}`} target="_blank"> <a href={`https://shuffler.io/search?tab=workflows&q=${selectedAction.app_name}`} target="_blank">
<SearchIcon style={{ color: "rgba(255,255,255,0.7)"}} /> <SearchIcon style={{ color: "rgba(255,255,255,0.7)"}} />
</a> </a>
</Tooltip>
</IconButton>
<IconButton
style={{
marginTop: "auto",
marginBottom: "auto",
height: 30,
marginLeft: 15,
paddingRight: 0,
}}
onClick={() => {
// aiSubmit(aiMsg, undefined, undefined, newSelectedAction)
aiSubmit("Fill based on previous values", undefined, undefined, selectedAction)
}}
>
<Tooltip
color="primary"
title={"Autocompletes fields. Uses NAME of the action and previous values' results."}
placement="top"
>
<AutoFixHighIcon style={{ color: "rgba(255,255,255,0.7)", height: 24, }} />
</Tooltip> </Tooltip>
</IconButton> </IconButton>
</div> </div>
</div> </div>
<div style={{ display: "flex", flexDirection: "column" }}> <div style={{ display: "flex", flexDirection: "column" }}>
@@ -2992,14 +3012,21 @@ const ParsedAction = (props) => {
defaultValue={selectedAction.label} defaultValue={selectedAction.label}
onChange={selectedNameChange} onChange={selectedNameChange}
onBlur={(e) => { onBlur={(e) => {
const name = e.target.value; // Copy the name value
const name = e.target.value
const parsedBaseLabel = "$"+baselabel.toLowerCase().replaceAll(" ", "_") const parsedBaseLabel = "$"+baselabel.toLowerCase().replaceAll(" ", "_")
const newname = "$"+name.toLowerCase().replaceAll(" ", "_") const newname = "$"+name.toLowerCase().replaceAll(" ", "_")
console.log("NAME: ", name)
// Check if it's the same as the current name in use
//if (name === selectedAction.label) {
// console.log("Returning from name thing")
// return
//}
// Change in actions, triggers & conditions // Change in actions, triggers & conditions
// Highlight the changes somehow with a glow? // Highlight the changes somehow with a glow?
//
// Should make it a function lol
if (workflow.branches !== undefined && workflow.branches !== null) { if (workflow.branches !== undefined && workflow.branches !== null) {
for (let [key,keyval] in Object.entries(workflow.branches)) { for (let [key,keyval] in Object.entries(workflow.branches)) {
if (workflow.branches[key].conditions !== undefined && workflow.branches[key].conditions !== null) { if (workflow.branches[key].conditions !== undefined && workflow.branches[key].conditions !== null) {
@@ -3117,7 +3144,13 @@ const ParsedAction = (props) => {
continue continue
} }
for (let [subkey, subkeyval] in Object.entries(workflow.actions[key].parameters)) { const params = workflow.actions[key].parameters
console.log(params)
if (params === null || params === undefined) {
continue
}
for (let [subkey, subkeyval] in Object.entries(params)) {
const param = workflow.actions[key].parameters[subkey]; const param = workflow.actions[key].parameters[subkey];
if (!param.value.includes("$")) { if (!param.value.includes("$")) {
continue continue
@@ -3179,7 +3212,7 @@ const ParsedAction = (props) => {
console.log("DID NAME REPLACE ACTUALLY WORK? - may be missing it in certain triggers"); console.log("DID NAME REPLACE ACTUALLY WORK? - may be missing it in certain triggers");
setWorkflow(workflow); setWorkflow(workflow);
setUpdate(Math.random()); setUpdate(Math.random());
baselabel = name baselabel = name
}} }}
/> />
@@ -3240,7 +3273,7 @@ const ParsedAction = (props) => {
}} }}
> >
<AddIcon style={{ marginRight: 10 }} /> Authenticate{" "} <AddIcon style={{ marginRight: 10 }} /> Authenticate{" "}
{selectedApp.name} {selectedApp.name.replaceAll("_", " ")}
</Button> </Button>
</span> </span>
</Tooltip> </Tooltip>
@@ -3573,8 +3606,6 @@ const ParsedAction = (props) => {
data.label = "No name" data.label = "No name"
} }
console.log("Name: ", newActionname)
newActionname = (newActionname.charAt(0).toUpperCase() + newActionname.substring(1)).replaceAll("_", " "); newActionname = (newActionname.charAt(0).toUpperCase() + newActionname.substring(1)).replaceAll("_", " ");
var method = "" var method = ""
+9 -1
View File
@@ -43,6 +43,7 @@ const SearchField = props => {
const node = useRef() const node = useRef()
const [searchOpen, setSearchOpen] = useState(false) const [searchOpen, setSearchOpen] = useState(false)
const [oldPath, setOldPath] = useState("") const [oldPath, setOldPath] = useState("")
const [value, setValue] = useState("");
if (serverside === true) { if (serverside === true) {
return null return null
@@ -67,7 +68,13 @@ const SearchField = props => {
//}, searchOpen) //}, searchOpen)
const SearchBox = ({currentRefinement, refine, isSearchStalled, } ) => { const SearchBox = ({currentRefinement, refine, isSearchStalled, } ) => {
const keyPressHandler = (e) => {
// e.preventDefault();
if (e.which === 13) {
// alert("You pressed enter!");
navigate("/search?q=" + currentRefinement, { state: value, replace: true });
}
};
/* /*
endAdornment: ( endAdornment: (
<InputAdornment position="end" style={{textAlign: "right", zIndex: 5001, cursor: "pointer", width: 100, }} onMouseOver={(event) => { <InputAdornment position="end" style={{textAlign: "right", zIndex: 5001, cursor: "pointer", width: 100, }} onMouseOver={(event) => {
@@ -108,6 +115,7 @@ const SearchField = props => {
color="primary" color="primary"
placeholder="Find Public Apps, Workflows, Documentation..." placeholder="Find Public Apps, Workflows, Documentation..."
value={currentRefinement} value={currentRefinement}
onKeyDown={keyPressHandler}
id="shuffle_search_field" id="shuffle_search_field"
onClick={(event) => { onClick={(event) => {
if (!searchOpen) { if (!searchOpen) {
@@ -205,6 +205,9 @@ const CodeEditor = (props) => {
setAvailableVariables(allVariables) setAvailableVariables(allVariables)
setMainVariables(tmpVariables) setMainVariables(tmpVariables)
console.log("Checking local codedata: ", localcodedata)
expectedOutput(localcodedata)
}, []) }, [])
const aiSubmit = (value, inputAction) => { const aiSubmit = (value, inputAction) => {
+6
View File
@@ -2,6 +2,7 @@ import React, { useEffect, useState } from 'react';
import {Link} from 'react-router-dom'; import {Link} from 'react-router-dom';
import theme from '../theme.jsx'; import theme from '../theme.jsx';
import { removeQuery } from '../components/ScrollToTop.jsx';
import { Search as SearchIcon, CloudQueue as CloudQueueIcon, Code as CodeIcon } from '@mui/icons-material'; import { Search as SearchIcon, CloudQueue as CloudQueueIcon, Code as CodeIcon } from '@mui/icons-material';
@@ -171,6 +172,7 @@ const AppGrid = props => {
// value={currentRefinement} // value={currentRefinement}
const SearchBox = ({currentRefinement, refine, isSearchStalled} ) => { const SearchBox = ({currentRefinement, refine, isSearchStalled} ) => {
var defaultSearch = ""
useEffect(() => { useEffect(() => {
if (window !== undefined && window.location !== undefined && window.location.search !== undefined && window.location.search !== null) { if (window !== undefined && window.location !== undefined && window.location.search !== undefined && window.location.search !== null) {
const urlSearchParams = new URLSearchParams(window.location.search) const urlSearchParams = new URLSearchParams(window.location.search)
@@ -179,6 +181,7 @@ const AppGrid = props => {
if (foundQuery !== null && foundQuery !== undefined) { if (foundQuery !== null && foundQuery !== undefined) {
console.log("Got query: ", foundQuery) console.log("Got query: ", foundQuery)
refine(foundQuery) refine(foundQuery)
defaultSearch = foundQuery
} }
} }
}, []) }, [])
@@ -187,6 +190,7 @@ const AppGrid = props => {
console.log("In refinement: ", inputsearch) console.log("In refinement: ", inputsearch)
//setLocalMessage(inputsearch) //setLocalMessage(inputsearch)
refine(inputsearch) refine(inputsearch)
defaultSearch = inputsearch
} else if (onlyResults === true) { } else if (onlyResults === true) {
// Don't return anything unless refinement works // Don't return anything unless refinement works
return null return null
@@ -196,6 +200,7 @@ const AppGrid = props => {
<form noValidate action="" role="search"> <form noValidate action="" role="search">
{onlyResults !== true ? {onlyResults !== true ?
<TextField <TextField
defaultValue={defaultSearch}
fullWidth fullWidth
style={{backgroundColor: theme.palette.inputColor, borderRadius: borderRadius, margin: 10, width: "100%",}} style={{backgroundColor: theme.palette.inputColor, borderRadius: borderRadius, margin: 10, width: "100%",}}
InputProps={{ InputProps={{
@@ -214,6 +219,7 @@ const AppGrid = props => {
placeholder="Find Workflows..." placeholder="Find Workflows..."
id="shuffle_search_field" id="shuffle_search_field"
onChange={(event) => { onChange={(event) => {
removeQuery("q")
refine(event.currentTarget.value) refine(event.currentTarget.value)
}} }}
limit={5} limit={5}
+13 -12
View File
@@ -63,7 +63,7 @@ const data = [
"z-index": 4999, "z-index": 4999,
"border-radius": "5px", "border-radius": "5px",
"background-opacity": "0.5", "background-opacity": "0.5",
"text-wrap": "wrap", "text-wrap": "wrap",
}, },
}, },
{ {
@@ -125,8 +125,8 @@ const data = [
"border-radius": "5px", "border-radius": "5px",
"border-color": "orange", "border-color": "orange",
"background-color": "#213243", "background-color": "#213243",
"background-width": "100%", "background-width": "100px",
"background-height": "100%", "background-height": "100px",
}, },
}, },
{ {
@@ -163,15 +163,16 @@ const data = [
{ {
selector: "node[?isSuggestion]", selector: "node[?isSuggestion]",
css: { css: {
shape: "ellipse", shape: "roundrectangle",
width: "50px", width: "30px",
height: "50px", height: "30px",
"z-index": "5002", "z-index": "5002",
"font-size": "0px", filter: "grayscale(100%)",
border: "1px solid rgba(255,255,255,0.9)", border: "1px solid rgba(255,255,255,0.9)",
"background-image": "data(large_image)", "background-image": "data(large_image)",
"background-color": "data(iconBackground)", "background-fit": "cover",
label: "data(label)", "font-size": "20px",
label: "data(label_replaced)",
}, },
}, },
{ {
@@ -199,7 +200,7 @@ const data = [
"text-margin-x": "0px", "text-margin-x": "0px",
"background-color": "data(imageColor)", "background-color": "data(imageColor)",
"background-image": "data(image)", "background-image": "data(image)",
label: "data(label)", label: "data(label)",
}, },
}, },
{ {
@@ -337,9 +338,9 @@ const data = [
width: "1px", width: "1px",
"line-style": "dashed", "line-style": "dashed",
"line-fill": "linear-gradient", "line-fill": "linear-gradient",
"target-arrow-color": "#f34079", "target-arrow-color": "#555555",
"line-gradient-stop-positions": ["0.0", "100"], "line-gradient-stop-positions": ["0.0", "100"],
"line-gradient-stop-colors": ["#f86a3e", "#f34079"], "line-gradient-stop-colors": ["#555555", "#555555"],
}, },
}, },
{ {
+11 -6
View File
@@ -24,12 +24,12 @@ const theme = createTheme(adaptV4Theme({
backgroundColor: "#1a1a1a", backgroundColor: "#1a1a1a",
borderRadius: 5, borderRadius: 5,
defaultBorder: "1px solid rgba(255,255,255,0.3)", defaultBorder: "1px solid rgba(255,255,255,0.3)",
jsonTheme: "brewer", jsonTheme: "brewer",
reactJsonStyle: { reactJsonStyle: {
borderRadius: 5, borderRadius: 5,
border: "1px solid rgba(255,255,255,0.7)", border: "1px solid rgba(255,255,255,0.7)",
padding: 5, padding: 5,
}, },
textFieldStyle: { textFieldStyle: {
backgroundColor: "#383B40", backgroundColor: "#383B40",
borderRadius: 5, borderRadius: 5,
@@ -70,6 +70,11 @@ const theme = createTheme(adaptV4Theme({
}, },
}, },
overrides: { overrides: {
MuiPaper: {
root: {
backgroundColor: "#1c1c1d",
},
},
MuiMenu: { MuiMenu: {
list: { list: {
backgroundColor: "#27292d", backgroundColor: "#27292d",
+4 -5
View File
@@ -3381,11 +3381,10 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user
<ListItemText <ListItemText
style={{ maxWidth: 200, minWidth: 200 }} style={{ maxWidth: 200, minWidth: 200 }}
primary={ primary={
schedule.environment === "cloud" ? ( schedule.environment === "cloud" || schedule.environment === "" || schedule.frequency.length > 0 ?
schedule.frequency schedule.frequency
) : ( :
<span>{schedule.seconds} seconds</span> <span>{schedule.seconds} seconds</span>
)
} }
/> />
<ListItemText <ListItemText
@@ -3399,14 +3398,14 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user
style={{ textDecoration: "none", color: "#f85a3e" }} style={{ textDecoration: "none", color: "#f85a3e" }}
href={`/workflows/${schedule.workflow_id}`} href={`/workflows/${schedule.workflow_id}`}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
> >
{schedule.workflow_id} {schedule.workflow_id}
</a> </a>
} }
/> />
<ListItemText <ListItemText
primary={schedule.argument} primary={schedule.argument.replaceAll('\\\"', '\"')}
style={{ style={{
minWidth: 300, minWidth: 300,
maxWidth: 300, maxWidth: 300,
File diff suppressed because it is too large Load Diff
+107 -115
View File
@@ -196,8 +196,9 @@ const parseCurl = (s) => {
case "data": case "data":
if (out.method === "GET" || out.method === "HEAD") if (out.method === "GET" || out.method === "HEAD")
out.method = "POST"; out.method = "POST";
out.header["Content-Type"] =
out.header["Content-Type"] || "application/x-www-form-urlencoded"; out.header["Content-Type"] = out.header["Content-Type"] || "application/x-www-form-urlencoded";
out.body = out.body ? out.body + "&" + arg : arg; out.body = out.body ? out.body + "&" + arg : arg;
state = ""; state = "";
break; break;
@@ -223,52 +224,52 @@ const parseCurl = (s) => {
// Basically CRUD for each category + special // Basically CRUD for each category + special
export const appCategories = [ export const appCategories = [
{ {
"name": "Communication", "name": "Communication",
"color": "#FFC107", "color": "#FFC107",
"icon": "communication", "icon": "communication",
"action_labels": ["List Messages", "Send Message", "Get Message", "Search messages"], "action_labels": ["List Messages", "Send Message", "Get Message", "Search messages", "List Attachments", "Get Attachment", "Get Contact"],
}, { }, {
"name": "SIEM", "name": "SIEM",
"color": "#FFC107", "color": "#FFC107",
"icon": "siem", "icon": "siem",
"action_labels": ["Search", "List Alerts", "Close Alert", "Create detection", "Add to lookup list",], "action_labels": ["Search", "List Alerts", "Close Alert", "Get Alert", "Create detection", "Add to lookup list",],
}, { }, {
"name": "Eradication", "name": "Eradication",
"color": "#FFC107", "color": "#FFC107",
"icon": "eradication", "icon": "eradication",
"action_labels": ["List Alerts", "Close Alert", "Create detection", "Block hash", "Search Hosts", "Isolate host", "Unisolate host"], "action_labels": ["List Alerts", "Close Alert", "Get Alert", "Create detection", "Block hash", "Search Hosts", "Isolate host", "Unisolate host"],
}, { }, {
"name": "Cases", "name": "Cases",
"color": "#FFC107", "color": "#FFC107",
"icon": "cases", "icon": "cases",
"action_labels": ["List tickets", "Get ticket", "Create ticket", "Close ticket", "Add comment", "Update ticket",], "action_labels": ["List tickets", "Get ticket", "Create ticket", "Close ticket", "Add comment", "Update ticket", "Search tickets"],
}, { }, {
"name": "Assets", "name": "Assets",
"color": "#FFC107", "color": "#FFC107",
"icon": "assets", "icon": "assets",
"action_labels": ["List Assets", "Get Asset", "Search Assets", "Search Users", "Search endpoints", "Search vulnerabilities"], "action_labels": ["List Assets", "Get Asset", "Search Assets", "Search Users", "Search endpoints", "Search vulnerabilities"],
}, { }, {
"name": "Intel", "name": "Intel",
"color": "#FFC107", "color": "#FFC107",
"icon": "intel", "icon": "intel",
"action_labels": ["Get IOC", "Search IOC", "Create IOC", "Update IOC", "Delete IOC",], "action_labels": ["Get IOC", "Search IOC", "Create IOC", "Update IOC", "Delete IOC",],
}, { }, {
"name": "IAM", "name": "IAM",
"color": "#FFC107", "color": "#FFC107",
"icon": "iam", "icon": "iam",
"action_labels": ["Reset Password", "Enable user", "Disable user", "Get Identity", "Get Asset", "Search Identity", ], "action_labels": ["Reset Password", "Enable user", "Disable user", "Get Identity", "Get Asset", "Search Identity", ],
}, { }, {
"name": "Network", "name": "Network",
"color": "#FFC107", "color": "#FFC107",
"icon": "network", "icon": "network",
"action_labels": ["Get Rules", "Allow IP", "Block IP",], "action_labels": ["Get Rules", "Allow IP", "Block IP",],
}, { }, {
"name": "Other", "name": "Other",
"color": "#FFC107", "color": "#FFC107",
"icon": "other", "icon": "other",
"action_labels": ["Update Info", "Get Info", "Get Status", "Get Version", "Get Health", "Get Config", "Get Configs", "Get Configs by type", "Get Configs by name", "Run script"], "action_labels": ["Update Info", "Get Info", "Get Status", "Get Version", "Get Health", "Get Config", "Get Configs", "Get Configs by type", "Get Configs by name", "Run script"],
}, },
] ]
export const base64_decode = (str) => { export const base64_decode = (str) => {
@@ -570,11 +571,11 @@ const AppCreator = (defaultprops) => {
if (data.openapi === null) { if (data.openapi === null) {
toast("Failed to load OpenAPI for app. Please contact support if this persists.") toast("Failed to load OpenAPI for app. Please contact support if this persists.")
setIsAppLoaded(true); setIsAppLoaded(true);
return return
} }
console.log("Decoded: ", parsedDecoded) //console.log("Decoded: ", parsedDecoded)
const parsedapp = const parsedapp =
data.openapi === undefined || data.openapi === null data.openapi === undefined || data.openapi === null
? data ? data
@@ -719,9 +720,9 @@ const AppCreator = (defaultprops) => {
var newActions = []; var newActions = [];
var wordlist = {}; var wordlist = {};
var all_categories = []; var all_categories = [];
var parentUrl = "" var parentUrl = ""
console.log("Paths: ", data.paths) console.log("Paths: ", data.paths)
if (data.paths !== null && data.paths !== undefined) { if (data.paths !== null && data.paths !== undefined) {
for (let [path, pathvalue] of Object.entries(data.paths)) { for (let [path, pathvalue] of Object.entries(data.paths)) {
@@ -751,45 +752,44 @@ const AppCreator = (defaultprops) => {
tmpname = methodvalue.operationId; tmpname = methodvalue.operationId;
} }
if (tmpname !== undefined && tmpname !== null) { if (tmpname !== undefined && tmpname !== null) {
tmpname = tmpname.replaceAll(".", " "); tmpname = tmpname.replaceAll(".", " ");
} }
if ((tmpname === undefined || tmpname === null) && methodvalue.description !== undefined && methodvalue.description !== null && methodvalue.description.length > 0) { if ((tmpname === undefined || tmpname === null) && methodvalue.description !== undefined && methodvalue.description !== null && methodvalue.description.length > 0) {
tmpname = methodvalue.description.replaceAll(".", " ").replaceAll("_", " ") tmpname = methodvalue.description.replaceAll(".", " ").replaceAll("_", " ")
} }
var newaction = { var newaction = {
name: tmpname, name: tmpname,
description: methodvalue.description, description: methodvalue.description,
url: path, url: path,
file_field: "", file_field: "",
method: method.toUpperCase(), method: method.toUpperCase(),
headers: "", headers: "",
queries: [], queries: [],
paths: [], paths: [],
body: "", body: "",
errors: [], errors: [],
example_response: "", example_response: "",
action_label: "No Label", action_label: "No Label",
required_bodyfields: [], required_bodyfields: [],
}; };
if (methodvalue["x-label"] !== undefined && methodvalue["x-label"] !== null) { if (methodvalue["x-label"] !== undefined && methodvalue["x-label"] !== null) {
// FIX: Map labels only if they're actually in the category list // FIX: Map labels only if they're actually in the category list
newaction.action_label = methodvalue["x-label"] newaction.action_label = methodvalue["x-label"]
} }
if (methodvalue["x-required-fields"] !== undefined && methodvalue["x-required-fields"] !== null) { if (methodvalue["x-required-fields"] !== undefined && methodvalue["x-required-fields"] !== null) {
newaction.required_bodyfields = methodvalue["x-required-fields"] newaction.required_bodyfields = methodvalue["x-required-fields"]
} }
if (newaction.url !== undefined && newaction.url !== null && newaction.url.includes("_shuffle_replace_")) { if (newaction.url !== undefined && newaction.url !== null && newaction.url.includes("_shuffle_replace_")) {
const regex = /_shuffle_replace_\d/i; const regex = /_shuffle_replace_\d/i;
//console.log("NEW: ", //console.log("NEW: ",
newaction.url = newaction.url.replaceAll(new RegExp(regex, 'g'), "") newaction.url = newaction.url.replaceAll(new RegExp(regex, 'g'), "")
console.log("Replaced: ", newaction.url) }
}
// Finding category // Finding category
if (path.includes("/")) { if (path.includes("/")) {
@@ -1110,19 +1110,11 @@ const AppCreator = (defaultprops) => {
undefined undefined
) { ) {
if ( if (
methodvalue.responses.default.content["text/plain"][ methodvalue.responses.default.content["text/plain"]["schema"] !== undefined) {
"schema" if (methodvalue.responses.default.content["text/plain"]["schema"]["example"] !== undefined) {
] !== undefined newaction.example_response = methodvalue.responses.default.content["text/plain"]["schema"]["example"]
) {
if (
methodvalue.responses.default.content["text/plain"][
"schema"
]["example"] !== undefined
) {
newaction.example_response =
methodvalue.responses.default.content["text/plain"][
"schema"
]["example"]
} }
@@ -1560,12 +1552,12 @@ const AppCreator = (defaultprops) => {
if (securitySchemes !== undefined) { if (securitySchemes !== undefined) {
console.log("SECURITY: ", securitySchemes) //console.log("SECURITY: ", securitySchemes)
var newauth = []; var newauth = [];
try { try {
var optionset = false var optionset = false
for (const [key, value] of Object.entries(securitySchemes)) { for (const [key, value] of Object.entries(securitySchemes)) {
console.log("AUTH: ", key, value); //console.log("AUTH: ", key, value);
if (key === "jwt") { if (key === "jwt") {
setAuthenticationOption("JWT"); setAuthenticationOption("JWT");
@@ -1776,12 +1768,12 @@ const AppCreator = (defaultprops) => {
if (!found) { if (!found) {
newActions2.push(action) newActions2.push(action)
} else { } else {
console.log("NOT skipping duplicate action: ", action.url, ". Should merge contents") //console.log("NOT skipping duplicate action: ", action.url, ". Should merge contents")
newActions2.push(action) newActions2.push(action)
} }
} }
console.log("Actions: ", newActions.length, " Actions2: ", newActions2.length) //console.log("Actions: ", newActions.length, " Actions2: ", newActions2.length)
newActions = newActions2 newActions = newActions2
if (newActions.length > increaseAmount - 1) { if (newActions.length > increaseAmount - 1) {
setActionAmount(increaseAmount); setActionAmount(increaseAmount);
@@ -3880,7 +3872,7 @@ const AppCreator = (defaultprops) => {
const datasplit = parsedurlsplit[1].split("&") const datasplit = parsedurlsplit[1].split("&")
for (var key in datasplit) { for (var key in datasplit) {
console.log("Data: ", datasplit[key]) //console.log("Data: ", datasplit[key])
var actualkey = datasplit[key] var actualkey = datasplit[key]
var example = "" var example = ""
if (datasplit[key].includes("=")) { if (datasplit[key].includes("=")) {
@@ -4143,7 +4135,7 @@ const AppCreator = (defaultprops) => {
overflowX: "hidden", overflowX: "hidden",
}} }}
onClick={() => { onClick={() => {
console.log("Data: ", data) //console.log("Data: ", data)
if (hasFile) { if (hasFile) {
//setActionField("headers", "") //setActionField("headers", "")
//console.log("It has a file: ", data["file_field"]) //console.log("It has a file: ", data["file_field"])
@@ -4321,9 +4313,9 @@ const AppCreator = (defaultprops) => {
} }
const LoopActions = (props) => { const LoopActions = (props) => {
const { filteredActions } = props; const { filteredActions } = props;
console.log("Actions: ", filteredActions) //console.log("Actions: ", filteredActions)
if (filteredActions === null || filteredActions === undefined || filteredActions.length === 0) { if (filteredActions === null || filteredActions === undefined || filteredActions.length === 0) {
return null return null
} }
@@ -4745,7 +4737,7 @@ const AppCreator = (defaultprops) => {
} }
const handleSubmitCheck = () => { const handleSubmitCheck = () => {
console.log("NEW AUTH: ", authenticationOption); //console.log("NEW AUTH: ", authenticationOption);
if (authenticationOption.label.length === 0) { if (authenticationOption.label.length === 0) {
authenticationOption.label = `Auth for ${selectedApp.name}`; authenticationOption.label = `Auth for ${selectedApp.name}`;
//toast("Label can't be empty") //toast("Label can't be empty")
@@ -4968,10 +4960,10 @@ const AppCreator = (defaultprops) => {
{projectCategories.map((tag, index) => { {projectCategories.map((tag, index) => {
const newname = tag.charAt(0).toUpperCase() + tag.slice(1); const newname = tag.charAt(0).toUpperCase() + tag.slice(1);
//var regex = /_shuffle_replace_\d/i; //var regex = /_shuffle_replace_\d/i;
////console.log("NEW: ", ////console.log("NEW: ",
//newname = newname.replaceAll(regex, "") //newname = newname.replaceAll(regex, "")
//console.log("Replaced: ", newname) //console.log("Replaced: ", newname)
return ( return (
<Chip <Chip
+32 -14
View File
@@ -391,19 +391,28 @@ const Docs = (defaultprops) => {
} }
function CodeHandler(props) { function CodeHandler(props) {
console.log("PROPS: ", props)
const propvalue = props.value !== undefined && props.value !== null ? props.value : props.children !== undefined && props.children !== null && props.children.length > 0 ? props.children[0] : ""
return ( return (
<pre <div
style={{ style={{
padding: 15, padding: 15,
minWidth: "50%", minWidth: "50%",
maxWidth: "100%", maxWidth: "100%",
backgroundColor: theme.palette.inputColor, backgroundColor: theme.palette.inputColor,
overflowX: "auto", overflowY: "auto",
overflowY: "hidden",
}} }}
> >
<code>{props.value}</code> <code
</pre> style={{
// Wrap if larger than X
whiteSpace: "pre-wrap",
overflow: "auto",
}}
>{propvalue}</code>
</div>
); );
} }
@@ -436,7 +445,7 @@ const Docs = (defaultprops) => {
href={selectedMeta.link} href={selectedMeta.link}
style={{ textDecoration: "none", color: "#f85a3e" }} style={{ textDecoration: "none", color: "#f85a3e" }}
> >
<Button style={{}} variant="outlined" color="secondary"> <Button style={{color: "white", }} variant="outlined" color="secondary">
<EditIcon /> &nbsp;&nbsp;Edit <EditIcon /> &nbsp;&nbsp;Edit
</Button> </Button>
</a> </a>
@@ -638,7 +647,7 @@ const Docs = (defaultprops) => {
<Typography variant="h6" style={headerStyle}>Why Shuffle?</Typography> <Typography variant="h6" style={headerStyle}>Why Shuffle?</Typography>
<Typography variant="body1"> <Typography variant="body1">
<b>Security first.</b> We incentivize trying before buying, and give you the full set of tools you need to automate your operations. What's more is we also help you <a href="https://shuffler.io/pricing?tag=docs" target="_blank" style={hrefStyle2}>find usecases</a> that fit your your unique needs. Accessibility is key, and we intend to help every SOC globally use and share their usecases. <b>Security first.</b> We incentivize trying before buying, and give you the full set of tools you need to automate your operations. What's more is we also help you <a href="https://shuffler.io/pricing?tag=docs" target="_blank" style={hrefStyle2}>find usecases</a> that fit your unique needs. Accessibility is key, and we intend to help every SOC globally use and share their usecases.
</Typography> </Typography>
<Typography variant="h6" style={headerStyle}>Get help</Typography> <Typography variant="h6" style={headerStyle}>Get help</Typography>
@@ -878,16 +887,25 @@ const Docs = (defaultprops) => {
: :
<div id="markdown_wrapper_outer" style={markdownStyle}> <div id="markdown_wrapper_outer" style={markdownStyle}>
<ReactMarkdown <ReactMarkdown
components={{
img: Img,
code: CodeHandler,
h1: Heading,
h2: Heading,
h3: Heading,
h4: Heading,
h5: Heading,
h6: Heading,
a: OuterLink,
}}
id="markdown_wrapper" id="markdown_wrapper"
escapeHtml={false} escapeHtml={false}
source={data} style={{
renderers={{ maxWidth: "100%", minWidth: "100%",
link: OuterLink,
image: Img,
code: CodeHandler,
heading: Heading,
}} }}
/> >
{data}
</ReactMarkdown>
</div> </div>
} }
<Divider <Divider
+78 -57
View File
@@ -66,10 +66,10 @@ const RunWorkflow = (defaultprops) => {
marginBottom: 150, marginBottom: 150,
} }
const params = useParams(); const params = useParams();
var props = JSON.parse(JSON.stringify(defaultprops)) var props = JSON.parse(JSON.stringify(defaultprops))
props.match = {} props.match = {}
props.match.params = params props.match.params = params
const defaultTitle = "Run Workflow" const defaultTitle = "Run Workflow"
if (document != undefined && document.title != defaultTitle) { if (document != undefined && document.title != defaultTitle) {
@@ -126,12 +126,14 @@ const RunWorkflow = (defaultprops) => {
} }
const executionMargin = 20 const executionMargin = 20
const defaultReturn = const defaultReturn = null
/*
<div style={{marginTop: executionMargin, }}> <div style={{marginTop: executionMargin, }}>
<Typography variant="h6" style={{color: theme.palette.primaryColor}}> <Typography variant="h6" style={{color: theme.palette.primaryColor}}>
No results yet No results yet
</Typography> </Typography>
</div> </div>
*/
if (executionData.results === undefined || executionData.results === null) { if (executionData.results === undefined || executionData.results === null) {
return defaultReturn return defaultReturn
@@ -271,7 +273,7 @@ const RunWorkflow = (defaultprops) => {
width: 30, width: 30,
}} }}
onClick={() => { onClick={() => {
navigate(`?execution_highlight=${parsed_url}`) //navigate(`?execution_highlight=${parsed_url}`)
}} }}
> >
<PreviewIcon style={{ color: "rgba(255,255,255,0.5)" }} /> <PreviewIcon style={{ color: "rgba(255,255,255,0.5)" }} />
@@ -314,10 +316,16 @@ const RunWorkflow = (defaultprops) => {
) )
} }
const onSubmit = (execution_id, authorization, answer) => { const onSubmit = (event, execution_id, authorization, answer) => {
if (event !== null) {
event.preventDefault()
}
console.log("In submit!")
stop() stop()
setMessage("") setMessage("")
setExecutionLoading(true) setExecutionLoading(true)
setExecutionData({}) setExecutionData({})
setExecutionInfo("") setExecutionInfo("")
@@ -327,13 +335,13 @@ const RunWorkflow = (defaultprops) => {
var url = `${globalUrl}/api/v1/workflows/${props.match.params.key}/execute` var url = `${globalUrl}/api/v1/workflows/${props.match.params.key}/execute`
var fetchBody = { var fetchBody = {
headers: {
'Content-Type': 'application/json; charset=utf-8',
},
mode: 'cors', mode: 'cors',
credentials: 'include', credentials: 'include',
crossDomain: true, crossDomain: true,
withCredentials: true, withCredentials: true,
headers: {
'Content-Type': 'application/json; charset=utf-8',
},
} }
if (answer !== undefined && execution_id !== undefined && authorization !== undefined) { if (answer !== undefined && execution_id !== undefined && authorization !== undefined) {
@@ -345,8 +353,10 @@ const RunWorkflow = (defaultprops) => {
fetchBody.body = JSON.stringify(data) fetchBody.body = JSON.stringify(data)
} }
console.log("Pre request: ", url, fetchBody)
fetch(url, fetchBody) fetch(url, fetchBody)
.then((response) => { .then((response) => {
console.log("Got answer 1")
if (response.status !== 200 && response.status !== 201) { if (response.status !== 200 && response.status !== 201) {
if (answer !== undefined && execution_id !== undefined && authorization !== undefined) { if (answer !== undefined && execution_id !== undefined && authorization !== undefined) {
@@ -362,9 +372,11 @@ const RunWorkflow = (defaultprops) => {
} }
} }
console.log("Got answer 2")
return response.json(); return response.json();
}) })
.then(responseJson => { .then(responseJson => {
console.log("Got answer 3")
setExecutionLoading(false) setExecutionLoading(false)
if (responseJson["success"] === false) { if (responseJson["success"] === false) {
console.log("Failed sending execution request") console.log("Failed sending execution request")
@@ -379,6 +391,7 @@ const RunWorkflow = (defaultprops) => {
start(); start();
} }
} }
console.log("Got answer 4")
}) })
.catch(error => { .catch(error => {
//setExecutionInfo("Error in workflow startup: " + error) //setExecutionInfo("Error in workflow startup: " + error)
@@ -387,14 +400,14 @@ const RunWorkflow = (defaultprops) => {
} }
const getWorkflow = (workflow_id) => { const getWorkflow = (workflow_id) => {
fetch(globalUrl + "/api/v1/workflows/" + workflow_id, { fetch(globalUrl + "/api/v1/workflows/" + workflow_id, {
method: "GET", method: "GET",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
Accept: "application/json", Accept: "application/json",
}, },
credentials: "include", credentials: "include",
}) })
.then((response) => { .then((response) => {
if (response.status !== 200) { if (response.status !== 200) {
console.log("Status not 200 for workflows :O!"); console.log("Status not 200 for workflows :O!");
@@ -441,35 +454,35 @@ const RunWorkflow = (defaultprops) => {
return return
} }
console.log("Got response: ", responseJson) //console.log("Got response: ", responseJson)
ReactDOM.unstable_batchedUpdates(() => { ReactDOM.unstable_batchedUpdates(() => {
if (JSON.stringify(responseJson) !== JSON.stringify(executionData)) { if (JSON.stringify(responseJson) !== JSON.stringify(executionData)) {
// FIXME: If another is selected, don't edit.. // FIXME: If another is selected, don't edit..
// Doesn't work because this is some async garbage // Doesn't work because this is some async garbage
if (executionData.execution_id === undefined || (responseJson.execution_id === executionData.execution_id && responseJson.results !== undefined && responseJson.results !== null)) { if (executionData.execution_id === undefined || (responseJson.execution_id === executionData.execution_id && responseJson.results !== undefined && responseJson.results !== null)) {
if (executionData.status !== responseJson.status || executionData.result !== responseJson.result || (executionData.results !== undefined && responseJson.results !== null && executionData.results.length !== responseJson.results.length)) { if (executionData.status !== responseJson.status || executionData.result !== responseJson.result || (executionData.results !== undefined && responseJson.results !== null && executionData.results.length !== responseJson.results.length)) {
console.log("Updating data!") //console.log("Updating data!")
setExecutionData(responseJson) setExecutionData(responseJson)
for (var key in responseJson.results) { for (var key in responseJson.results) {
if (responseJson.results[key].status === "WAITING") { if (responseJson.results[key].status === "WAITING") {
console.log("Found: ", responseJson.results[key]) console.log("Found: ", responseJson.results[key])
const validate = validateJson(responseJson.results[key].result) const validate = validateJson(responseJson.results[key].result)
console.log("Validate: ", validate) console.log("Validate: ", validate)
if (validate.valid && typeof validate.result === "string") { if (validate.valid && typeof validate.result === "string") {
validate.result = JSON.parse(validate.result) validate.result = JSON.parse(validate.result)
}
console.log("Newresult: ", validate.result)
if (validate.result["information"] !== undefined && validate.result["information"] !== null) {
setWorkflowQuestion(validate.result["information"])
}
break
}
} }
console.log("Newresult: ", validate.result)
if (validate.result["information"] !== undefined && validate.result["information"] !== null) {
setWorkflowQuestion(validate.result["information"])
}
break
}
}
} else { } else {
console.log("NOT updating executiondata state."); console.log("NOT updating executiondata state.");
} }
@@ -518,9 +531,9 @@ const RunWorkflow = (defaultprops) => {
if (responseJson.sync_features === undefined || responseJson.sync_features === null) { if (responseJson.sync_features === undefined || responseJson.sync_features === null) {
} }
if (document != undefined && document.title != defaultTitle) { if (document != undefined && document.title != defaultTitle) {
document.title = responseJson.name + " - " + defaultTitle document.title = responseJson.name + " - " + defaultTitle
} }
setSelectedOrganization(responseJson) setSelectedOrganization(responseJson)
} }
}) })
@@ -530,6 +543,11 @@ const RunWorkflow = (defaultprops) => {
}; };
const fetchUpdates = (execution_id, authorization, getorg) => { const fetchUpdates = (execution_id, authorization, getorg) => {
if (execution_id === undefined || execution_id === null || execution_id === "") {
stop()
return
}
const innerRequest = { const innerRequest = {
"execution_id": execution_id, "execution_id": execution_id,
"authorization": authorization "authorization": authorization
@@ -594,7 +612,7 @@ const RunWorkflow = (defaultprops) => {
const buttonBackground = "linear-gradient(to right, #f86a3e, #f34079)" const buttonBackground = "linear-gradient(to right, #f86a3e, #f34079)"
const buttonStyle = {borderRadius: 25, height: 50, fontSize: 18, backgroundImage: handleValidateForm(executionArgument) || executionLoading ? buttonBackground : "grey", color: "white"} const buttonStyle = {borderRadius: 25, height: 50, fontSize: 18, backgroundImage: handleValidateForm(executionArgument) || executionLoading ? buttonBackground : "grey", color: "white"}
console.log("execdata: ", executionData) //console.log("execdata: ", executionData)
const disabledButtons = message.length > 0 || executionData.status === "FINISHED" || executionData.status === "ABORTED" const disabledButtons = message.length > 0 || executionData.status === "FINISHED" || executionData.status === "ABORTED"
const organization = selectedOrganization !== undefined && selectedOrganization !== null ? selectedOrganization.name : "Unknown" const organization = selectedOrganization !== undefined && selectedOrganization !== null ? selectedOrganization.name : "Unknown"
@@ -603,7 +621,7 @@ const RunWorkflow = (defaultprops) => {
const image = selectedOrganization !== undefined && selectedOrganization !== null && selectedOrganization.image !== undefined && selectedOrganization.image !== null && selectedOrganization.image !== "" ? selectedOrganization.image : theme.palette.defaultImage const image = selectedOrganization !== undefined && selectedOrganization !== null && selectedOrganization.image !== undefined && selectedOrganization.image !== null && selectedOrganization.image !== "" ? selectedOrganization.image : theme.palette.defaultImage
console.log("IMG: ", image, "ORG: ", selectedOrganization) //console.log("IMG: ", image, "ORG: ", selectedOrganization)
if (!disabledButtons && answer !== undefined && answer !== null && organization !== "Unknown" && buttonClicked.length === 0) { if (!disabledButtons && answer !== undefined && answer !== null && organization !== "Unknown" && buttonClicked.length === 0) {
console.log("Finding button!") console.log("Finding button!")
@@ -627,7 +645,7 @@ const RunWorkflow = (defaultprops) => {
const basedata = const basedata =
<div style={bodyDivStyle}> <div style={bodyDivStyle}>
<Paper style={boxStyle}> <Paper style={boxStyle}>
<form onSubmit={() => {onSubmit()}} style={{margin: "15px 15px 15px 15px"}}> <form onSubmit={(e) => {onSubmit(e)}} style={{margin: "15px 15px 15px 15px"}}>
<img <img
alt={workflow.name} alt={workflow.name}
@@ -676,7 +694,7 @@ const RunWorkflow = (defaultprops) => {
{answer !== undefined && answer !== null ? null : {answer !== undefined && answer !== null ? null :
<span> <span>
Execution Argument Runtime Argument
<div style={{marginBottom: 5}}> <div style={{marginBottom: 5}}>
<TextField <TextField
color="primary" color="primary"
@@ -705,9 +723,12 @@ const RunWorkflow = (defaultprops) => {
{executionRunning ? {executionRunning ?
<span style={{width: 50, height: 50, margin: "auto", alignItems: "center", justifyContent: "center", textAlign: "center", }}> <span style={{width: 50, height: 50, margin: "auto", alignItems: "center", justifyContent: "center", textAlign: "center", }}>
<CircularProgress style={{marginTop: 20, marginBottom: 20, marginLeft: 185, }}/> <CircularProgress style={{marginTop: 20, marginBottom: 20, marginLeft: 185, }}/>
<Typography variant="body2" style={{margin: "auto", marginTop: 20, marginBottom: 20, textAlign: "center", alignItem: "center", }} color="textSecondary">
Status: {executionData.status} {executionData.status !== undefined && executionData.status !== null && executionData.status !== "" ?
</Typography> <Typography variant="body2" style={{margin: "auto", marginTop: 20, marginBottom: 20, textAlign: "center", alignItem: "center", }} color="textSecondary">
Status: {executionData.status}
</Typography>
: null}
</span> </span>
: :
@@ -723,7 +744,7 @@ const RunWorkflow = (defaultprops) => {
} }
<div fullWidth style={{width: "100%", marginTop: 10, marginBottom: 10, display: "flex", }}> <div fullWidth style={{width: "100%", marginTop: 10, marginBottom: 10, display: "flex", }}>
<Button fullWidth id="continue_execution" variant="contained" disabled={disabledButtons} color="primary" style={{border: answer === "true" ? "2px solid rgba(255,255,255,0.6)" : null, flex: 1,}} onClick={() => { <Button fullWidth id="continue_execution" variant="contained" disabled={disabledButtons} color="primary" style={{border: answer === "true" ? "2px solid rgba(255,255,255,0.6)" : null, flex: 1,}} onClick={() => {
onSubmit(execution_id, authorization, true) onSubmit(null, execution_id, authorization, true)
setButtonClicked("FINISHED") setButtonClicked("FINISHED")
setExecutionData({ setExecutionData({
@@ -734,7 +755,7 @@ const RunWorkflow = (defaultprops) => {
&nbsp;or&nbsp; &nbsp;or&nbsp;
</Typography> </Typography>
<Button fullWidth id="abort_execution" variant="contained" color="primary" disabled={disabledButtons} style={{border: answer !== "true" ? "2px solid rgba(255,255,255,0.6)" : null, flex: 1, }} onClick={() => { <Button fullWidth id="abort_execution" variant="contained" color="primary" disabled={disabledButtons} style={{border: answer !== "true" ? "2px solid rgba(255,255,255,0.6)" : null, flex: 1, }} onClick={() => {
onSubmit(execution_id, authorization, false) onSubmit(null, execution_id, authorization, false)
setButtonClicked("ABORTED") setButtonClicked("ABORTED")
setExecutionData({ setExecutionData({
+73 -66
View File
@@ -393,6 +393,9 @@ export const validateJson = (showResult) => {
if (typeof showResult === 'string') { if (typeof showResult === 'string') {
showResult = showResult.split(" False").join(" false") showResult = showResult.split(" False").join(" false")
showResult = showResult.split(" True").join(" true") showResult = showResult.split(" True").join(" true")
showResult.replaceAll("False,", "false,")
showResult.replaceAll("True,", "true,")
} }
if (typeof showResult === "object" || typeof showResult === "array") { if (typeof showResult === "object" || typeof showResult === "array") {
@@ -1247,9 +1250,9 @@ const Workflows = (props) => {
//flexDirection: !isMobile ? "column" : "row", //flexDirection: !isMobile ? "column" : "row",
const paperAppContainer = { const paperAppContainer = {
display: "flex", //display: "flex",
flexWrap: "wrap", //flexWrap: "wrap",
alignContent: "space-between", //alignContent: "space-between",
}; };
const paperAppStyle = { const paperAppStyle = {
@@ -1587,7 +1590,7 @@ const Workflows = (props) => {
const NewWorkflowPaper = () => { const NewWorkflowPaper = () => {
const [hover, setHover] = React.useState(false); const [hover, setHover] = React.useState(false);
const innerColor = "rgba(255,255,255,0.3)"; const innerColor = "rgba(255,255,255,0.3)"
const setupPaperStyle = { const setupPaperStyle = {
minHeight: paperAppStyle.minHeight, minHeight: paperAppStyle.minHeight,
maxWidth: "100%", maxWidth: "100%",
@@ -1609,9 +1612,9 @@ const Workflows = (props) => {
square square
style={setupPaperStyle} style={setupPaperStyle}
onClick={() => { onClick={() => {
setModalOpen(true) setModalOpen(true)
setIsEditing(false) setIsEditing(false)
}} }}
onMouseOver={() => { onMouseOver={() => {
setHover(true); setHover(true);
}} }}
@@ -1682,7 +1685,7 @@ const Workflows = (props) => {
} }
const actions = data.actions !== null ? data.actions.length : 0; const actions = data.actions !== null ? data.actions.length : 0;
const appGroup = getWorkflowAppgroup(data) const appGroup = getWorkflowAppgroup(data)
const [triggers, subflows] = getWorkflowMeta(data); const [triggers, subflows] = getWorkflowMeta(data);
const workflowMenuButtons = ( const workflowMenuButtons = (
@@ -1699,22 +1702,22 @@ const Workflows = (props) => {
<MenuItem <MenuItem
style={{ backgroundColor: theme.palette.inputColor, color: "white" }} style={{ backgroundColor: theme.palette.inputColor, color: "white" }}
onClick={(event) => { onClick={(event) => {
event.stopPropagation() event.stopPropagation()
ReactDOM.unstable_batchedUpdates(() => { ReactDOM.unstable_batchedUpdates(() => {
setModalOpen(true); setModalOpen(true);
setEditingWorkflow(JSON.parse(JSON.stringify(data))); setEditingWorkflow(JSON.parse(JSON.stringify(data)));
setNewWorkflowName(data.name); setNewWorkflowName(data.name);
setNewWorkflowDescription(data.description); setNewWorkflowDescription(data.description);
setDefaultReturnValue(data.default_return_value); setDefaultReturnValue(data.default_return_value);
if (data.tags !== undefined && data.tags !== null) { if (data.tags !== undefined && data.tags !== null) {
setNewWorkflowTags(JSON.parse(JSON.stringify(data.tags))); setNewWorkflowTags(JSON.parse(JSON.stringify(data.tags)));
} }
if (data.usecase_ids !== undefined && data.usecase_ids !== null && data.usecase_ids.length > 0) { if (data.usecase_ids !== undefined && data.usecase_ids !== null && data.usecase_ids.length > 0) {
setSelectedUsecases(data.usecase_ids) setSelectedUsecases(data.usecase_ids)
} }
}) })
}} }}
key={"change"} key={"change"}
> >
<EditIcon style={{ marginLeft: 0, marginRight: 8 }} /> <EditIcon style={{ marginLeft: 0, marginRight: 8 }} />
@@ -2122,18 +2125,18 @@ const Workflows = (props) => {
: null} : null}
</Grid> </Grid>
{data.actions !== undefined && data.actions !== null ? ( {data.actions !== undefined && data.actions !== null ? (
<div style={{position: "absolute", top: 10, right: 10, }}> <div style={{position: "absolute", top: 10, right: 10, }}>
<IconButton <IconButton
aria-label="more" aria-label="more"
aria-controls="long-menu" aria-controls="long-menu"
aria-haspopup="true" aria-haspopup="true"
onClick={menuClick} onClick={menuClick}
style={{ padding: "0px", color: "#979797" }} style={{ padding: "0px", color: "#979797" }}
> >
<MoreVertIcon /> <MoreVertIcon />
</IconButton> </IconButton>
{workflowMenuButtons} {workflowMenuButtons}
</div> </div>
) : null} ) : null}
</Grid> </Grid>
</Paper> </Paper>
@@ -2180,13 +2183,13 @@ const Workflows = (props) => {
workflowdata["default_return_value"] = defaultReturnValue; workflowdata["default_return_value"] = defaultReturnValue;
} }
if (currentUsecases !== undefined && currentUsecases !== null) { if (currentUsecases !== undefined && currentUsecases !== null) {
workflowdata["usecase_ids"] = currentUsecases workflowdata["usecase_ids"] = currentUsecases
//workflows[0].category = ["detect"] //workflows[0].category = ["detect"]
//workflows[0].usecase_ids = ["Correlate tickets"] //workflows[0].usecase_ids = ["Correlate tickets"]
} }
const new_url = `${globalUrl}/api/v1/workflows${extraData}` const new_url = `${globalUrl}/api/v1/workflows${extraData}`
return fetch(new_url, { return fetch(new_url, {
method: method, method: method,
headers: { headers: {
@@ -2206,16 +2209,16 @@ const Workflows = (props) => {
return response.json(); return response.json();
}) })
.then((responseJson) => { .then((responseJson) => {
if (responseJson.success === false) { if (responseJson.success === false) {
if (responseJson.reason !== undefined) { if (responseJson.reason !== undefined) {
toast("Error setting workflow: ", responseJson.reason) toast("Error setting workflow: ", responseJson.reason)
} else { } else {
toast("Error setting workflow.") toast("Error setting workflow.")
}
return
} }
return
}
if (redirect) { if (redirect) {
//window.location.pathname = "/workflows/" + responseJson["id"]; //window.location.pathname = "/workflows/" + responseJson["id"];
navigate("/workflows/" + responseJson["id"]) navigate("/workflows/" + responseJson["id"])
@@ -2224,7 +2227,7 @@ const Workflows = (props) => {
// Update :) // Update :)
setTimeout(() => { setTimeout(() => {
getAvailableWorkflows(); getAvailableWorkflows();
}, 2500); }, 4000);
setSubmitLoading(false) setSubmitLoading(false)
setModalOpen(false); setModalOpen(false);
} else { } else {
@@ -2244,7 +2247,7 @@ const Workflows = (props) => {
const importFiles = (event) => { const importFiles = (event) => {
console.log("Importing!"); console.log("Importing!");
setSubmitLoading(true) setSubmitLoading(true)
if (event.target.files.length > 0) { if (event.target.files.length > 0) {
console.log("Files: !", event.target.files.length); console.log("Files: !", event.target.files.length);
@@ -2281,9 +2284,9 @@ const Workflows = (props) => {
data.default_return_value, data.default_return_value,
{}, {},
false, false,
[], [],
"", "",
data.status, data.status,
) )
.then((response) => { .then((response) => {
if (response !== undefined) { if (response !== undefined) {
@@ -2301,9 +2304,9 @@ const Workflows = (props) => {
data.default_return_value, data.default_return_value,
data, data,
false, false,
[], [],
"", "",
data.status, data.status,
).then((response) => { ).then((response) => {
if (response !== undefined) { if (response !== undefined) {
toast("Successfully imported " + data.name); toast("Successfully imported " + data.name);
@@ -3239,14 +3242,18 @@ const Workflows = (props) => {
fullWidth fullWidth
value={filters} value={filters}
onChange={(chips) => { onChange={(chips) => {
console.log("CHANGE: ", chips);
setFilters(chips); setFilters(chips);
findWorkflow(chips);
}} }}
onAdd={(chip) => { //onAdd={(chip) => {
addFilter(chip); // console.log("ADd: ", chip);
}} // addFilter(chip);
onDelete={(_, index) => { //}}
removeFilter(index); //onDelete={(_, index) => {
}} // console.log("Remove: ", index);
// removeFilter(index);
//}}
/> />
</div> </div>
</div> </div>
@@ -3485,11 +3492,11 @@ const Workflows = (props) => {
/> />
: null} : null}
<div style={{marginTop: 30, marginBottom: 50, marginLeft: 30, }}> <div style={{}}>
{view === "grid" ? ( {view === "grid" ? (
<Grid container spacing={filteredWorkflows.length === 0 ? 12 : filteredWorkflows.length === 1 ? 6 : 4} style={paperAppContainer}> <Grid container spacing={0/*filteredWorkflows.length === 0 ? 12 : filteredWorkflows.length === 1 ? 6 : 4*/} style={paperAppContainer}>
{/*<Zoom in={true} style={{ transitionDelay: `${workflowDelay}ms` }}>*/} {/*<Zoom in={true} style={{ transitionDelay: `${workflowDelay}ms` }}>*/}
<NewWorkflowPaper /> <NewWorkflowPaper />
{/*</Zoom>*/} {/*</Zoom>*/}
{filteredWorkflows.map((data, index) => { {filteredWorkflows.map((data, index) => {