Merge branch '1.4.0'
This commit is contained in:
@@ -3515,7 +3515,7 @@ class AppBase:
|
|||||||
timeout = 30
|
timeout = 30
|
||||||
|
|
||||||
# Check if current app is Shuffle Tools, then set to 55 due to certain actions being slow (ioc parser..)
|
# Check if current app is Shuffle Tools, then set to 55 due to certain actions being slow (ioc parser..)
|
||||||
#uu In general, this should be disabled for onprem
|
# In general, this should be disabled for onprem
|
||||||
if self.action["app_name"].lower() == "shuffle tools":
|
if self.action["app_name"].lower() == "shuffle tools":
|
||||||
timeout = 55
|
timeout = 55
|
||||||
|
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ require (
|
|||||||
github.com/gorilla/mux v1.8.0
|
github.com/gorilla/mux v1.8.0
|
||||||
github.com/h2non/filetype v1.1.3
|
github.com/h2non/filetype v1.1.3
|
||||||
github.com/satori/go.uuid v1.2.0
|
github.com/satori/go.uuid v1.2.0
|
||||||
github.com/shuffle/shuffle-shared v0.4.66
|
github.com/shuffle/shuffle-shared v0.4.79
|
||||||
golang.org/x/crypto v0.14.0
|
golang.org/x/crypto v0.9.0
|
||||||
google.golang.org/api v0.125.0
|
google.golang.org/api v0.125.0
|
||||||
google.golang.org/grpc v1.55.0
|
google.golang.org/grpc v1.55.0
|
||||||
gopkg.in/src-d/go-git.v4 v4.13.1
|
gopkg.in/src-d/go-git.v4 v4.13.1
|
||||||
|
|||||||
@@ -1049,15 +1049,25 @@ func handleExecution(id string, workflow shuffle.Workflow, request *http.Request
|
|||||||
|
|
||||||
workflowExecution, execInfo, _, err := shuffle.PrepareWorkflowExecution(ctx, workflow, request, 10)
|
workflowExecution, execInfo, _, err := shuffle.PrepareWorkflowExecution(ctx, workflow, request, 10)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
err = shuffle.SetWorkflowExecution(ctx, workflowExecution, true)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[ERROR] Failed setting workflow execution during init (2): %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
if strings.Contains(fmt.Sprintf("%s", err), "User Input") {
|
if strings.Contains(fmt.Sprintf("%s", err), "User Input") {
|
||||||
// Special for user input callbacks
|
// Special for user input callbacks
|
||||||
return workflowExecution, fmt.Sprintf("%s", err), nil
|
return workflowExecution, fmt.Sprintf("%s", err), nil
|
||||||
} else {
|
} else {
|
||||||
log.Printf("[WARNING] Failed in prepareExecution: %s", err)
|
log.Printf("[ERROR] Failed in prepareExecution: %s", err)
|
||||||
return shuffle.WorkflowExecution{}, fmt.Sprintf("Failed starting workflow: %s", err), err
|
return shuffle.WorkflowExecution{}, fmt.Sprintf("Failed starting workflow: %s", err), err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = shuffle.SetWorkflowExecution(ctx, workflowExecution, true)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[ERROR] Failed setting workflow execution during init (2): %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
err = imageCheckBuilder(execInfo.ImageNames)
|
err = imageCheckBuilder(execInfo.ImageNames)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("[ERROR] Failed building the required images from %#v: %s", execInfo.ImageNames, err)
|
log.Printf("[ERROR] Failed building the required images from %#v: %s", execInfo.ImageNames, err)
|
||||||
@@ -1643,7 +1653,6 @@ func handleExecution(id string, workflow shuffle.Workflow, request *http.Request
|
|||||||
return shuffle.WorkflowExecution{}, "Failed building missing Docker images", err
|
return shuffle.WorkflowExecution{}, "Failed building missing Docker images", err
|
||||||
}
|
}
|
||||||
|
|
||||||
//Org []Org `json:"org,omitempty" datastore:"org"`
|
|
||||||
err = shuffle.SetWorkflowExecution(ctx, workflowExecution, true)
|
err = shuffle.SetWorkflowExecution(ctx, workflowExecution, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("[WARNING] Error saving workflow execution for updates %s", err)
|
log.Printf("[WARNING] Error saving workflow execution for updates %s", err)
|
||||||
|
|||||||
@@ -60,6 +60,7 @@
|
|||||||
"react-driftjs": "^1.2.2",
|
"react-driftjs": "^1.2.2",
|
||||||
"react-dropzone": "^14.2.3",
|
"react-dropzone": "^14.2.3",
|
||||||
"react-ga4": "^2.0.0",
|
"react-ga4": "^2.0.0",
|
||||||
|
"react-hotkeys": "^2.0.0",
|
||||||
"react-instantsearch-dom": "^6.28.0",
|
"react-instantsearch-dom": "^6.28.0",
|
||||||
"react-json-pretty": "^2.2.0",
|
"react-json-pretty": "^2.2.0",
|
||||||
"react-json-view": "^1.21.3",
|
"react-json-view": "^1.21.3",
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import Workflows from "./views/Workflows";
|
|||||||
import GettingStarted from "./views/GettingStarted";
|
import GettingStarted from "./views/GettingStarted";
|
||||||
import AngularWorkflow from "./views/AngularWorkflow.jsx";
|
import AngularWorkflow from "./views/AngularWorkflow.jsx";
|
||||||
|
|
||||||
|
//import Header from "./components/NewHeader.jsx";
|
||||||
import Header from "./components/Header.jsx";
|
import Header from "./components/Header.jsx";
|
||||||
import theme from "./theme";
|
import theme from "./theme";
|
||||||
import Apps from "./views/Apps";
|
import Apps from "./views/Apps";
|
||||||
@@ -38,6 +39,7 @@ import ScrollToTop from "./components/ScrollToTop";
|
|||||||
import AlertTemplate from "./components/AlertTemplate";
|
import AlertTemplate from "./components/AlertTemplate";
|
||||||
import { useAlert, positions, Provider } from "react-alert";
|
import { useAlert, positions, Provider } from "react-alert";
|
||||||
import { isMobile } from "react-device-detect";
|
import { isMobile } from "react-device-detect";
|
||||||
|
import RuntimeDebugger from "./components/RuntimeDebugger.jsx"
|
||||||
|
|
||||||
import { ToastContainer, toast } from 'react-toastify';
|
import { ToastContainer, toast } from 'react-toastify';
|
||||||
import 'react-toastify/dist/ReactToastify.css';
|
import 'react-toastify/dist/ReactToastify.css';
|
||||||
@@ -405,6 +407,8 @@ const App = (message, props) => {
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
<Route exact path="/debug" element={<RuntimeDebugger userdata={userdata} globalUrl={globalUrl} /> } />
|
||||||
|
<Route exact path="/workflows/debug" element={<RuntimeDebugger userdata={userdata} globalUrl={globalUrl} /> } />
|
||||||
<Route
|
<Route
|
||||||
exact
|
exact
|
||||||
path="/workflows/:key"
|
path="/workflows/:key"
|
||||||
|
|||||||
@@ -33,62 +33,62 @@ import {
|
|||||||
ListItem,
|
ListItem,
|
||||||
ListItemAvatar,
|
ListItemAvatar,
|
||||||
ListItemText,
|
ListItemText,
|
||||||
IconButton,
|
IconButton,
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
|
|
||||||
const AppSearchButtons = (props) => {
|
const AppSearchButtons = (props) => {
|
||||||
const { userdata, globalUrl, appFramework, finishedApps, appType, totalApps, index, onNodeSelect, setDiscoveryData, appName, AppImage, setDefaultSearch, discoveryData, checkLogin, setMissing, } = props
|
const { userdata, globalUrl, appFramework, moreButton, finishedApps, appType, totalApps, index, onNodeSelect, setDiscoveryData, appName, AppImage, setDefaultSearch, discoveryData, checkLogin, setMissing, } = props
|
||||||
|
|
||||||
const ref = useRef()
|
const ref = useRef()
|
||||||
let navigate = useNavigate();
|
let navigate = useNavigate();
|
||||||
|
|
||||||
const [isHover, setIsHover] = useState(false);
|
const [isHover, setIsHover] = useState(false);
|
||||||
const [localSearchOpen, setLocalSearchOpen] = useState(false)
|
const [localSearchOpen, setLocalSearchOpen] = useState(false)
|
||||||
const [newSelectedApp, setNewSelectedApp] = useState(undefined)
|
const [newSelectedApp, setNewSelectedApp] = useState(undefined)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log("AppSearchButtons: newSelectedApp: " + JSON.stringify(newSelectedApp))
|
console.log("AppSearchButtons: newSelectedApp: " + JSON.stringify(newSelectedApp))
|
||||||
|
|
||||||
if (newSelectedApp !== undefined && setMissing != undefined) {
|
if (newSelectedApp !== undefined && setMissing != undefined) {
|
||||||
console.log("AppSearchButtons: setMissing is defined!")
|
console.log("AppSearchButtons: setMissing is defined!")
|
||||||
|
|
||||||
const submitAppFramework = {
|
const submitAppFramework = {
|
||||||
"description": newSelectedApp.description,
|
"description": newSelectedApp.description,
|
||||||
"id": newSelectedApp.objectID,
|
"id": newSelectedApp.objectID,
|
||||||
"name": newSelectedApp.name,
|
"name": newSelectedApp.name,
|
||||||
"type": appType,
|
"type": appType,
|
||||||
"large_image": newSelectedApp.image_url,
|
"large_image": newSelectedApp.image_url,
|
||||||
}
|
}
|
||||||
|
|
||||||
setFrameworkItem(submitAppFramework)
|
setFrameworkItem(submitAppFramework)
|
||||||
setMissing(newSelectedApp)
|
setMissing(newSelectedApp)
|
||||||
}
|
}
|
||||||
}, [newSelectedApp])
|
}, [newSelectedApp])
|
||||||
|
|
||||||
if (appType === undefined) {
|
if (appType === undefined) {
|
||||||
console.log("Apptype is required in AppSearchButtons")
|
console.log("Apptype is required in AppSearchButtons")
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const buttonWidth = 450;
|
const buttonWidth = 450;
|
||||||
const buttonMargin = 10;
|
const buttonMargin = 10;
|
||||||
|
|
||||||
const handleMouseEnter = () => {
|
const handleMouseEnter = () => {
|
||||||
setIsHover(true);
|
setIsHover(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleMouseLeave = () => {
|
const handleMouseLeave = () => {
|
||||||
setIsHover(false);
|
setIsHover(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const foundApp = findSpecificApp(appFramework, appType)
|
const foundApp = findSpecificApp(appFramework, appType)
|
||||||
if (foundApp === undefined || foundApp === null) {
|
if (foundApp === undefined || foundApp === null) {
|
||||||
console.log("AppSearchButtons: App not found in appFramework: " + appType)
|
console.log("AppSearchButtons: App not found in appFramework: " + appType)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
const setFrameworkItem = (data) => {
|
const setFrameworkItem = (data) => {
|
||||||
|
|
||||||
fetch(globalUrl + "/api/v1/apps/frameworkConfiguration", {
|
fetch(globalUrl + "/api/v1/apps/frameworkConfiguration", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
@@ -99,187 +99,207 @@ const AppSearchButtons = (props) => {
|
|||||||
body: JSON.stringify(data),
|
body: JSON.stringify(data),
|
||||||
credentials: "include",
|
credentials: "include",
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (response.status !== 200) {
|
if (response.status !== 200) {
|
||||||
console.log("Status not 200 for framework!");
|
console.log("Status not 200 for framework!");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (checkLogin !== undefined) {
|
if (checkLogin !== undefined) {
|
||||||
checkLogin()
|
checkLogin()
|
||||||
}
|
}
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
})
|
})
|
||||||
.then((responseJson) => {
|
.then((responseJson) => {
|
||||||
if (responseJson.success === false) {
|
if (responseJson.success === false) {
|
||||||
if (responseJson.reason !== undefined) {
|
if (responseJson.reason !== undefined) {
|
||||||
toast("Failed updating: " + responseJson.reason)
|
toast("Failed updating: " + responseJson.reason)
|
||||||
} else {
|
} else {
|
||||||
toast("Failed to update framework for your org.")
|
toast("Failed to update framework for your org.")
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//setFrameworkLoaded(true)
|
//setFrameworkLoaded(true)
|
||||||
//setFrameworkData(responseJson)
|
//setFrameworkData(responseJson)
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
if (checkLogin !== undefined) {
|
if (checkLogin !== undefined) {
|
||||||
checkLogin()
|
checkLogin()
|
||||||
}
|
}
|
||||||
|
|
||||||
toast(error.toString());
|
toast(error.toString());
|
||||||
//setFrameworkLoaded(true)
|
//setFrameworkLoaded(true)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
const icon = foundApp.large_image
|
||||||
|
console.log("index:", moreButton)
|
||||||
|
console.log("totalApps:", totalApps)
|
||||||
|
|
||||||
const icon = foundApp.large_image
|
|
||||||
let xsValue = 12;
|
let xsValue = 12;
|
||||||
if (index !== undefined && index !== 0) {
|
if (index === totalApps - 1 || index === totalApps - 2 || index === totalApps - 3 || index === totalApps - 4) {
|
||||||
xsValue = 6;
|
xsValue = 6;
|
||||||
}
|
}
|
||||||
|
if (index === totalApps - 5) {
|
||||||
if (index === totalApps - 4 && totalApps % 2 === 1) {
|
|
||||||
xsValue = 12;
|
xsValue = 12;
|
||||||
}
|
}
|
||||||
|
if (moreButton) {
|
||||||
if (index === totalApps - 1 || index === totalApps - 2 || index === totalApps - 3) {
|
switch (index) {
|
||||||
xsValue = 4;
|
case totalApps - 1:
|
||||||
}
|
case totalApps - 2:
|
||||||
|
case totalApps - 3:
|
||||||
|
xsValue = 4;
|
||||||
|
break;
|
||||||
|
case totalApps - 4:
|
||||||
|
case totalApps - 5:
|
||||||
|
case totalApps - 6:
|
||||||
|
case totalApps - 7:
|
||||||
|
xsValue = 6;
|
||||||
|
break;
|
||||||
|
case totalApps - 8:
|
||||||
|
xsValue = 12;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// Handle other cases if needed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Grid item xs={xsValue} style={{ alignItems: "center", marginTop: 5, }}
|
<Grid item xs={xsValue} style={{ alignItems: "center", marginTop: 5, }}
|
||||||
onMouseEnter={handleMouseEnter}
|
onMouseEnter={handleMouseEnter}
|
||||||
onMouseLeave={handleMouseLeave}
|
onMouseLeave={handleMouseLeave}
|
||||||
>
|
>
|
||||||
{localSearchOpen ? (
|
{localSearchOpen ? (
|
||||||
<div
|
<div
|
||||||
style={{
|
|
||||||
width: 319,
|
|
||||||
height: 395,
|
|
||||||
flexShrink: 0,
|
|
||||||
marginLeft: 70,
|
|
||||||
marginTop: 68,
|
|
||||||
position: "absolute",
|
|
||||||
zIndex: 100,
|
|
||||||
borderRadius: 6,
|
|
||||||
border: "1px solid var(--Container-Stroke, #494949)",
|
|
||||||
background: "var(--Container, #212121)",
|
|
||||||
boxShadow: "8px 8px 32px 24px rgba(0, 0, 0, 0.16)",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div style={{ display: "flex" }}>
|
|
||||||
<div style={{ display: "flex", textAlign: "center", textTransform: "capitalize" }}>
|
|
||||||
<Typography style={{ padding: 16, color: "#FFFFFF", textTransform: "capitalize" }}> {discoveryData} </Typography>
|
|
||||||
</div>
|
|
||||||
<div style={{ display: "flex" }}>
|
|
||||||
<Tooltip
|
|
||||||
title="Close"
|
|
||||||
placement="top"
|
|
||||||
style={{ zIndex: 10011 }}
|
|
||||||
>
|
|
||||||
<IconButton
|
|
||||||
style={{
|
|
||||||
flex: 1,
|
|
||||||
// width: 224,
|
|
||||||
marginLeft: discoveryData === ("communication") ? 112 : 200,
|
|
||||||
width: "100%",
|
|
||||||
marginBottom: 23,
|
|
||||||
fontSize: 16,
|
|
||||||
background: "rgba(33, 33, 33, 1)",
|
|
||||||
borderColor: "rgba(33, 33, 33, 1)",
|
|
||||||
borderRadius: 8,
|
|
||||||
}}
|
|
||||||
onClick={() => {
|
|
||||||
setLocalSearchOpen(false)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Closeicon style={{ width: 16 }} />
|
|
||||||
</IconButton>
|
|
||||||
</Tooltip>
|
|
||||||
<Tooltip
|
|
||||||
title="Delete app"
|
|
||||||
placement="bottom"
|
|
||||||
style={{ zIndex: 10011 }}
|
|
||||||
>
|
|
||||||
<IconButton
|
|
||||||
style={{ zIndex: 12501, position: "absolute", top: 32, right: 16 }}
|
|
||||||
onClick={(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
setLocalSearchOpen(false)
|
|
||||||
setDefaultSearch("")
|
|
||||||
const submitDeletedApp = {
|
|
||||||
"description": "",
|
|
||||||
"id": "remove",
|
|
||||||
"name": "",
|
|
||||||
"type": discoveryData
|
|
||||||
}
|
|
||||||
setFrameworkItem(submitDeletedApp)
|
|
||||||
setNewSelectedApp({})
|
|
||||||
setTimeout(() => {
|
|
||||||
setDiscoveryData({})
|
|
||||||
setFrameworkItem(submitDeletedApp)
|
|
||||||
//setNewSelectedApp({})
|
|
||||||
}, 1000)
|
|
||||||
//setAppName(discoveryData.cases.name)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<DeleteIcon style={{ color: "white", height: 15, width: 15, }} />
|
|
||||||
</IconButton>
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
style={{ width: "100%", border: "1px #494949 solid" }}
|
|
||||||
/>
|
|
||||||
<AppSearch
|
|
||||||
defaultSearch={appType}
|
|
||||||
userdata={userdata}
|
|
||||||
newSelectedApp={newSelectedApp}
|
|
||||||
setNewSelectedApp={setNewSelectedApp}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
<div style={{ display: "flex", height: 70, border: isHover ? "1px solid #f85a3e" : "var(--Container, #212121)", borderRadius: 8, background: isHover ? "var(--Container, #212121)":"var(--Container, #212121)",
|
|
||||||
alignItems: "center", justifyContent: "center",}}
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
fullWidth
|
|
||||||
color="secondary"
|
|
||||||
style={{
|
style={{
|
||||||
height:"100%",
|
width: 319,
|
||||||
width:"100%",
|
height: 395,
|
||||||
display:"grid"
|
flexShrink: 0,
|
||||||
}}
|
marginLeft: 70,
|
||||||
onClick={(event) => {
|
marginTop: 68,
|
||||||
if (onNodeSelect !== undefined) {
|
position: "absolute",
|
||||||
onNodeSelect(appType);
|
zIndex: 100,
|
||||||
} else {
|
borderRadius: 6,
|
||||||
setLocalSearchOpen(true)
|
border: "1px solid var(--Container-Stroke, #494949)",
|
||||||
}
|
background: "var(--Container, #212121)",
|
||||||
|
boxShadow: "8px 8px 32px 24px rgba(0, 0, 0, 0.16)",
|
||||||
if (setDefaultSearch !== undefined) {
|
|
||||||
setDefaultSearch(appType)
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div style={{ display:"flex", textAlign:"center", justifyContent: "center", alignItems:"center", marginRight: "auto" }}>
|
<div style={{ display: "flex" }}>
|
||||||
{AppImage === undefined || AppImage === null || AppImage.length === 0 ?
|
<div style={{ display: "flex", textAlign: "center", textTransform: "capitalize" }}>
|
||||||
|
<Typography style={{ padding: 16, color: "#FFFFFF", textTransform: "capitalize" }}> {discoveryData} </Typography>
|
||||||
|
</div>
|
||||||
|
<div style={{ display: "flex" }}>
|
||||||
|
<Tooltip
|
||||||
|
title="Close"
|
||||||
|
placement="top"
|
||||||
|
style={{ zIndex: 10011 }}
|
||||||
|
>
|
||||||
|
<IconButton
|
||||||
|
style={{
|
||||||
|
flex: 1,
|
||||||
|
// width: 224,
|
||||||
|
marginLeft: discoveryData === ("communication") ? 112 : 200,
|
||||||
|
width: "100%",
|
||||||
|
marginBottom: 23,
|
||||||
|
fontSize: 16,
|
||||||
|
background: "rgba(33, 33, 33, 1)",
|
||||||
|
borderColor: "rgba(33, 33, 33, 1)",
|
||||||
|
borderRadius: 8,
|
||||||
|
}}
|
||||||
|
onClick={() => {
|
||||||
|
setLocalSearchOpen(false)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Closeicon style={{ width: 16 }} />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip
|
||||||
|
title="Delete app"
|
||||||
|
placement="bottom"
|
||||||
|
style={{ zIndex: 10011 }}
|
||||||
|
>
|
||||||
|
<IconButton
|
||||||
|
style={{ zIndex: 12501, position: "absolute", top: 32, right: 16 }}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setLocalSearchOpen(false)
|
||||||
|
setDefaultSearch("")
|
||||||
|
const submitDeletedApp = {
|
||||||
|
"description": "",
|
||||||
|
"id": "remove",
|
||||||
|
"name": "",
|
||||||
|
"type": discoveryData
|
||||||
|
}
|
||||||
|
setFrameworkItem(submitDeletedApp)
|
||||||
|
setNewSelectedApp({})
|
||||||
|
setTimeout(() => {
|
||||||
|
setDiscoveryData({})
|
||||||
|
setFrameworkItem(submitDeletedApp)
|
||||||
|
//setNewSelectedApp({})
|
||||||
|
}, 1000)
|
||||||
|
//setAppName(discoveryData.cases.name)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DeleteIcon style={{ color: "white", height: 15, width: 15, }} />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{ width: "100%", border: "1px #494949 solid" }}
|
||||||
|
/>
|
||||||
|
<AppSearch
|
||||||
|
defaultSearch={appType}
|
||||||
|
userdata={userdata}
|
||||||
|
newSelectedApp={newSelectedApp}
|
||||||
|
setNewSelectedApp={setNewSelectedApp}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
<div style={{
|
||||||
|
display: "flex", height: 70, border: isHover ? "1px solid #f85a3e" : "var(--Container, #212121)", borderRadius: 8, background: isHover ? "var(--Container, #212121)" : "var(--Container, #212121)",
|
||||||
|
alignItems: "center", justifyContent: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
fullWidth
|
||||||
|
color="secondary"
|
||||||
|
style={{
|
||||||
|
height: "100%",
|
||||||
|
width: "100%",
|
||||||
|
display: "grid"
|
||||||
|
}}
|
||||||
|
onClick={(event) => {
|
||||||
|
if (onNodeSelect !== undefined) {
|
||||||
|
onNodeSelect(appType);
|
||||||
|
} else {
|
||||||
|
setLocalSearchOpen(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setDefaultSearch !== undefined) {
|
||||||
|
setDefaultSearch(appType)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div style={{ display: "flex", textAlign: "center", justifyContent: "center", alignItems: "center", marginRight: "auto" }}>
|
||||||
|
{AppImage === undefined || AppImage === null || AppImage.length === 0 ?
|
||||||
<div style={{ width: 40, height: 40, borderRadius: 9999, backgroundColor: "#2F2F2F", textAlign: "center" }}>
|
<div style={{ width: 40, height: 40, borderRadius: 9999, backgroundColor: "#2F2F2F", textAlign: "center" }}>
|
||||||
<img style={{paddingLeft: 11, paddingTop: 11, width: 40, height: 40, flexShrink: 0, }} src={icon} />
|
<img style={{ paddingLeft: 11, paddingTop: 11, width: 40, height: 40, flexShrink: 0, }} src={icon} />
|
||||||
</div>
|
</div>
|
||||||
:
|
:
|
||||||
<img style={{ marginRight: 8, width: 35, height: 35, flexShrink: 0, borderRadius: 40, }} src={AppImage} />
|
<img style={{ marginRight: 8, width: 35, height: 35, flexShrink: 0, borderRadius: 40, }} src={AppImage} />
|
||||||
}
|
}
|
||||||
<div style={{ marginLeft: 8, }}>
|
<div style={{ marginLeft: 8, }}>
|
||||||
<Typography style={{
|
<Typography style={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
border: "none",
|
border: "none",
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
textTransform: ["siem", "edr", "iam"].includes(appType.toLowerCase()) ? "uppercase" : 'capitalize'
|
textTransform: ["siem", "edr", "iam"].includes(appType.toLowerCase()) ? "uppercase" : 'capitalize'
|
||||||
}} >
|
}} >
|
||||||
{appType}
|
{appType}
|
||||||
</Typography>
|
</Typography>
|
||||||
{appName === undefined || appName === undefined ||
|
{appName === undefined || appName === undefined ||
|
||||||
appName === null || appName === null || appName.length === 0 ?
|
appName === null || appName === null || appName.length === 0 ?
|
||||||
""
|
""
|
||||||
:
|
:
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import React, { useState, useEffect, useRef } from "react";
|
import React, { useState, useEffect, useRef } from "react";
|
||||||
import theme from '../theme.jsx';
|
import theme from '../theme.jsx';
|
||||||
import ReactGA from 'react-ga4';
|
import ReactGA from 'react-ga4';
|
||||||
import { useNavigate, Link } from 'react-router-dom';
|
import { useNavigate, Link, } from 'react-router-dom';
|
||||||
import {isMobile} from "react-device-detect";
|
import { isMobile } from "react-device-detect";
|
||||||
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'
|
||||||
import DeleteIcon from '@mui/icons-material/Delete';
|
import DeleteIcon from '@mui/icons-material/Delete';
|
||||||
@@ -13,6 +13,7 @@ import NewReleasesIcon from "@mui/icons-material/NewReleases";
|
|||||||
import ExtensionIcon from "@mui/icons-material/Extension";
|
import ExtensionIcon from "@mui/icons-material/Extension";
|
||||||
import EmailIcon from "@mui/icons-material/Email";
|
import EmailIcon from "@mui/icons-material/Email";
|
||||||
import FingerprintIcon from '@mui/icons-material/Fingerprint';
|
import FingerprintIcon from '@mui/icons-material/Fingerprint';
|
||||||
|
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
|
||||||
import AppSearch from "../components/Appsearch.jsx";
|
import AppSearch from "../components/Appsearch.jsx";
|
||||||
import AppSearchButtons from "../components/AppSearchButtons.jsx";
|
import AppSearchButtons from "../components/AppSearchButtons.jsx";
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
@@ -60,7 +61,7 @@ const AppSelection = props => {
|
|||||||
let navigate = useNavigate();
|
let navigate = useNavigate();
|
||||||
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
|
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (newSelectedApp === undefined || newSelectedApp.objectID === undefined || newSelectedApp.objectID === undefined || newSelectedApp.objectID.length === 0) {
|
if (newSelectedApp === undefined || newSelectedApp.objectID === undefined || newSelectedApp.objectID === undefined || newSelectedApp.objectID.length === 0) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -109,17 +110,17 @@ const AppSelection = props => {
|
|||||||
// value.type = key;
|
// value.type = key;
|
||||||
// tempApps.push(value);
|
// tempApps.push(value);
|
||||||
// });
|
// });
|
||||||
|
|
||||||
// // Define the custom sorting order
|
// // Define the custom sorting order
|
||||||
// const customSortingOrder = ["CASES", "SIEM", "ENDPOINT", "INTEL", "EMAIL"];
|
// const customSortingOrder = ["CASES", "SIEM", "ENDPOINT", "INTEL", "EMAIL"];
|
||||||
|
|
||||||
const lastApps = {}
|
const lastApps = {}
|
||||||
let endTypes = ["network", "assets", "iam"]
|
let endTypes = ["network", "assets", "iam"]
|
||||||
|
|
||||||
if (appFramework === undefined || appFramework === null || Object.keys(appFramework).length === 0) {
|
if (appFramework === undefined || appFramework === null || Object.keys(appFramework).length === 0) {
|
||||||
//window.location.href = "/welcome"
|
//window.location.href = "/welcome"
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Object.entries(appFramework).forEach(([key, value]) => {
|
Object.entries(appFramework).forEach(([key, value]) => {
|
||||||
@@ -129,7 +130,7 @@ const AppSelection = props => {
|
|||||||
|
|
||||||
value.type = key;
|
value.type = key;
|
||||||
|
|
||||||
if (endTypes.includes(value.type.toLowerCase())) {
|
if (endTypes.includes(value.type.toLowerCase())) {
|
||||||
lastApps[value.type] = value
|
lastApps[value.type] = value
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -137,7 +138,7 @@ const AppSelection = props => {
|
|||||||
if (lastPosted.type === value.type) {
|
if (lastPosted.type === value.type) {
|
||||||
value = lastPosted
|
value = lastPosted
|
||||||
}
|
}
|
||||||
|
|
||||||
tempApps.push(JSON.parse(JSON.stringify(value)));
|
tempApps.push(JSON.parse(JSON.stringify(value)));
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -157,8 +158,8 @@ const AppSelection = props => {
|
|||||||
|
|
||||||
if (moreButton) {
|
if (moreButton) {
|
||||||
tempApps.push(JSON.parse(JSON.stringify(lastApps["network"])))
|
tempApps.push(JSON.parse(JSON.stringify(lastApps["network"])))
|
||||||
tempApps.push(JSON.parse(JSON.stringify(lastApps["assets"])))
|
tempApps.push(JSON.parse(JSON.stringify(lastApps["assets"])))
|
||||||
tempApps.push(JSON.parse(JSON.stringify(lastApps["iam"])))
|
tempApps.push(JSON.parse(JSON.stringify(lastApps["iam"])))
|
||||||
}
|
}
|
||||||
|
|
||||||
setAppButtons(tempApps)
|
setAppButtons(tempApps)
|
||||||
@@ -167,10 +168,10 @@ const AppSelection = props => {
|
|||||||
}
|
}
|
||||||
}, [lastPosted, moreButton])
|
}, [lastPosted, moreButton])
|
||||||
|
|
||||||
if (appFramework === undefined || appFramework === null || Object.keys(appFramework).length === 0) {
|
if (appFramework === undefined || appFramework === null || Object.keys(appFramework).length === 0) {
|
||||||
//window.location.href = "/welcome"
|
//window.location.href = "/welcome"
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
const setFrameworkItem = (data) => {
|
const setFrameworkItem = (data) => {
|
||||||
console.log("Setting framework item: ", data, isCloud)
|
console.log("Setting framework item: ", data, isCloud)
|
||||||
@@ -276,7 +277,7 @@ const AppSelection = props => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("NODESELECT: ", label)
|
console.log("NODESELECT: ", label)
|
||||||
|
|
||||||
setDiscoveryData(label)
|
setDiscoveryData(label)
|
||||||
setSelectionOpen(true)
|
setSelectionOpen(true)
|
||||||
@@ -285,7 +286,7 @@ const AppSelection = props => {
|
|||||||
setNewSelectedApp(undefined)
|
setNewSelectedApp(undefined)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// const sizing = moreButton ? 510 : 480;
|
// const sizing = moreButton ? 510 : 480;
|
||||||
const buttonWidth = 450;
|
const buttonWidth = 450;
|
||||||
@@ -304,12 +305,29 @@ const AppSelection = props => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Collapse in={true}>
|
<Collapse in={true}>
|
||||||
|
<Tooltip
|
||||||
|
title="Back"
|
||||||
|
placement="top"
|
||||||
|
style={{ zIndex: 10011 }}
|
||||||
|
>
|
||||||
|
<IconButton
|
||||||
|
style={{
|
||||||
|
}}
|
||||||
|
onClick={() => {
|
||||||
|
navigate('/welcome');
|
||||||
|
window.location.reload();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ArrowBackIcon style={{ width: 20 }} />
|
||||||
|
<Typography style={{fontSize : 16, marginLeft : 2}}>Back</Typography>
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
// minHeight: sizing,
|
// minHeight: sizing,
|
||||||
// maxHeight: sizing,
|
// maxHeight: sizing,
|
||||||
marginTop: 10,
|
marginTop: 10,
|
||||||
width: isMobile ? 380 : 500,
|
width: isMobile ? 350 : 500,
|
||||||
marginBottom: 25,
|
marginBottom: 25,
|
||||||
textAlign: isMobile ? "center" : null,
|
textAlign: isMobile ? "center" : null,
|
||||||
}}
|
}}
|
||||||
@@ -428,59 +446,59 @@ const AppSelection = props => {
|
|||||||
Select the apps you work with and we will connect them for you.
|
Select the apps you work with and we will connect them for you.
|
||||||
</Typography>
|
</Typography>
|
||||||
<Grid rowSpacing={1} columnSpacing={2} container >
|
<Grid rowSpacing={1} columnSpacing={2} container >
|
||||||
{appButtons.map((appData, index) => {
|
{appButtons.map((appData, index) => {
|
||||||
// This is here due to a memory issue with setting apps properly
|
// This is here due to a memory issue with setting apps properly
|
||||||
if (appData.id === "remove") {
|
if (appData.id === "remove") {
|
||||||
console.log("Removed as appdata is overridden: ", appData)
|
console.log("Removed as appdata is overridden: ", appData)
|
||||||
|
|
||||||
appData = {
|
appData = {
|
||||||
"count": 0,
|
"count": 0,
|
||||||
"description": "",
|
"description": "",
|
||||||
"id": "",
|
"id": "",
|
||||||
"large_image": "",
|
"large_image": "",
|
||||||
"name": "",
|
"name": "",
|
||||||
"type": appData.type,
|
"type": appData.type,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const appName = appData.name
|
const appName = appData.name
|
||||||
const AppImage = appData.large_image
|
const AppImage = appData.large_image
|
||||||
const appType = appData.type
|
const appType = appData.type
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppSearchButtons
|
<AppSearchButtons
|
||||||
appFramework={appFramework}
|
appFramework={appFramework}
|
||||||
index={index}
|
index={index}
|
||||||
totalApps={appButtons.length}
|
totalApps={appButtons.length}
|
||||||
appName={appName}
|
appName={appName}
|
||||||
appType={appType}
|
appType={appType}
|
||||||
AppImage={AppImage}
|
AppImage={AppImage}
|
||||||
defaultSearch={defaultSearch}
|
defaultSearch={defaultSearch}
|
||||||
finishedApps={finishedApps}
|
finishedApps={finishedApps}
|
||||||
onNodeSelect={onNodeSelect}
|
onNodeSelect={onNodeSelect}
|
||||||
discoveryData={discoveryData}
|
discoveryData={discoveryData}
|
||||||
setDiscoveryData={setDiscoveryData}
|
setDiscoveryData={setDiscoveryData}
|
||||||
setDefaultSearch={setDefaultSearch}
|
setDefaultSearch={setDefaultSearch}
|
||||||
apps={apps}
|
apps={apps}
|
||||||
setMoreButton={setMoreButton}
|
setMoreButton={setMoreButton}
|
||||||
moreButton={moreButton}
|
moreButton={moreButton}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</Grid>
|
</Grid>
|
||||||
</div>
|
</div>
|
||||||
{!moreButton ? (
|
{!moreButton ? (
|
||||||
<div style={{width: "100%", marginLeft: isMobile ? 100 : 200, marginBottom: 20, textAlign: isMobile ? "center" : null}}>
|
<div style={{ width: "100%", marginLeft: isMobile ? 80 : 200, marginBottom: 20, textAlign: isMobile ? "center" : null }}>
|
||||||
<Link style={{color:"#FF8444"}} onClick={()=>{
|
<Link style={{ color: "#FF8444" }} onClick={() => {
|
||||||
setMoreButton(true)
|
setMoreButton(true)
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
navigate("/welcome?tab=2")
|
navigate("/welcome?tab=2")
|
||||||
}, 250)
|
}, 250)
|
||||||
}}
|
}}
|
||||||
>See More Apps</Link>
|
>See More Apps</Link>
|
||||||
</div>): ""}
|
</div>) : ""}
|
||||||
<div style={{ flexDirection: "row", width: isMobile ? 380 : null, textAlign: isMobile ? "center" : null}}>
|
<div style={{ flexDirection: "row", width: isMobile ? 340 : null, textAlign: isMobile ? "center" : null }}>
|
||||||
<Button variant="contained" type="submit" fullWidth style={bottomButtonStyle} onClick={() => {
|
<Button variant="contained" type="submit" fullWidth style={bottomButtonStyle} onClick={() => {
|
||||||
navigate("/welcome?tab=3")
|
navigate("/welcome?tab=3")
|
||||||
setActiveStep(2)
|
setActiveStep(2)
|
||||||
|
|||||||
@@ -56,13 +56,13 @@ const AuthenticationData = (props) => {
|
|||||||
if (!responseJson.success) {
|
if (!responseJson.success) {
|
||||||
toast("Failed to set app auth: " + responseJson.reason);
|
toast("Failed to set app auth: " + responseJson.reason);
|
||||||
} else {
|
} else {
|
||||||
if (getAppAuthentication !== undefined) {
|
if (getAppAuthentication !== undefined) {
|
||||||
getAppAuthentication()
|
getAppAuthentication()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (setAuthenticationModalOpen !== undefined) {
|
if (setAuthenticationModalOpen !== undefined) {
|
||||||
setAuthenticationModalOpen(false)
|
setAuthenticationModalOpen(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Needs a refresh with the new authentication..
|
// Needs a refresh with the new authentication..
|
||||||
//toast("Successfully saved new app auth")
|
//toast("Successfully saved new app auth")
|
||||||
|
|||||||
@@ -887,6 +887,31 @@ const Billing = (props) => {
|
|||||||
"Shuffle is an Open Source automation platform, and no license is required to use it. You may however activate Cloud Sync, get our Scale license, get help with Kubernetes, or talk to Shuffle's Support team to get automation help."
|
"Shuffle is an Open Source automation platform, and no license is required to use it. You may however activate Cloud Sync, get our Scale license, get help with Kubernetes, or talk to Shuffle's Support team to get automation help."
|
||||||
}
|
}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
|
{userdata.support === true ?
|
||||||
|
<div style={{marginBottom: 10, }}>
|
||||||
|
For sales: Create
|
||||||
|
<a href={"https://docs.google.com/document/d/1OeJSi42812EMg7fUAw1HAj1ymOG8rfp8Ma_DGJKvwgI/copy?usp=sharing&organization=" + selectedOrganization.id} target="_blank" rel="noopener noreferrer" style={{ textDecoration: "none", color: "#f85a3e" }}>
|
||||||
|
New Cloud Contract
|
||||||
|
</a>
|
||||||
|
or
|
||||||
|
<a href={"https://docs.google.com/document/d/1IguxpeV4Wwwr9C0MPyUNhhajEu_PxjRfP7f0hYyRYOI/copy?usp=sharing&organization=" + selectedOrganization.id} target="_blank" rel="noopener noreferrer" style={{ textDecoration: "none", color: "#f85a3e" }}>
|
||||||
|
New Onprem Contract
|
||||||
|
</a>
|
||||||
|
-
|
||||||
|
<a href={"https://drive.google.com/drive/folders/1zVvwwkbQXW3p-DJYa0GBDzFo_ZnV_I_5"} target="_blank" rel="noopener noreferrer" style={{ textDecoration: "none", color: "#f85a3e" }}>
|
||||||
|
Google Drive Link
|
||||||
|
</a>
|
||||||
|
-
|
||||||
|
<a href={"https://github.com/Shuffle/Shuffle-docs/tree/master/handbook/Sales"} target="_blank" rel="noopener noreferrer" style={{ textDecoration: "none", color: "#f85a3e" }}>
|
||||||
|
Sales Process
|
||||||
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
:
|
||||||
|
null
|
||||||
|
}
|
||||||
<div style={{display: "flex", maxWidth: 768, minWidth: 768, }}>
|
<div style={{display: "flex", maxWidth: 768, minWidth: 768, }}>
|
||||||
{isCloud && billingInfo.subscription !== undefined && billingInfo.subscription !== null ?
|
{isCloud && billingInfo.subscription !== undefined && billingInfo.subscription !== null ?
|
||||||
<SubscriptionObject
|
<SubscriptionObject
|
||||||
@@ -1009,6 +1034,8 @@ const Billing = (props) => {
|
|||||||
</Grid>
|
</Grid>
|
||||||
*/}
|
*/}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
{isCloud &&
|
{isCloud &&
|
||||||
selectedOrganization.partner_info !== undefined &&
|
selectedOrganization.partner_info !== undefined &&
|
||||||
selectedOrganization.partner_info.reseller === true ? (
|
selectedOrganization.partner_info.reseller === true ? (
|
||||||
|
|||||||
@@ -339,7 +339,7 @@ const CacheView = (props) => {
|
|||||||
<div style={{ marginTop: 20, marginBottom: 20 }}>
|
<div style={{ marginTop: 20, marginBottom: 20 }}>
|
||||||
<h2 style={{ display: "inline" }}>Shuffle Datastore</h2>
|
<h2 style={{ display: "inline" }}>Shuffle Datastore</h2>
|
||||||
<span style={{ marginLeft: 25 }}>
|
<span style={{ marginLeft: 25 }}>
|
||||||
Datastore is a key-value store for storing data that can be used cross-workflow.
|
Datastore is a permanent key-value database for storing data that can be used cross-workflow. You can store anything from lists of IPs to complex configurations.
|
||||||
<a
|
<a
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
|
|||||||
@@ -1256,7 +1256,7 @@ const ConfigureWorkflow = (props) => {
|
|||||||
</Typography>
|
</Typography>
|
||||||
: null}
|
: null}
|
||||||
|
|
||||||
<List style={{paddingBottom: 250, }}>
|
<List style={{paddingBottom: window.location.pathname.includes("/workflows/") ? 0 : 250, }}>
|
||||||
{requiredActions.map((data, index) => {
|
{requiredActions.map((data, index) => {
|
||||||
return (
|
return (
|
||||||
<div key={index} style={{marginBottom: 10, }}>
|
<div key={index} style={{marginBottom: 10, }}>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import 'react-alice-carousel/lib/alice-carousel.css';
|
|||||||
import TrendingFlatIcon from '@mui/icons-material/TrendingFlat';
|
import TrendingFlatIcon from '@mui/icons-material/TrendingFlat';
|
||||||
import theme from '../theme.jsx';
|
import theme from '../theme.jsx';
|
||||||
import {isMobile} from "react-device-detect";
|
import {isMobile} from "react-device-detect";
|
||||||
|
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
|
||||||
import CheckBoxSharpIcon from '@mui/icons-material/CheckBoxSharp';
|
import CheckBoxSharpIcon from '@mui/icons-material/CheckBoxSharp';
|
||||||
import { findSpecificApp } from "../components/AppFramework.jsx"
|
import { findSpecificApp } from "../components/AppFramework.jsx"
|
||||||
import {
|
import {
|
||||||
@@ -291,11 +292,29 @@ const ExploreWorkflow = (props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ marginTop: 0, margin: "auto", minHeight: sizing, maxHeight: sizing, }}>
|
<div style={{ marginTop: 0, margin: "auto", minHeight: sizing, maxHeight: sizing, }}>
|
||||||
|
<Tooltip
|
||||||
|
title="Back"
|
||||||
|
placement="top"
|
||||||
|
style={{ zIndex: 10011 }}
|
||||||
|
>
|
||||||
|
<IconButton
|
||||||
|
style={{
|
||||||
|
marginRight: isMobile ? 230 : null
|
||||||
|
}}
|
||||||
|
onClick={() => {
|
||||||
|
navigate('/welcome?tab=2');
|
||||||
|
window.location.reload();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ArrowBackIcon style={{ width: 20 }} />
|
||||||
|
<Typography style={{fontSize : 16, marginLeft : 2}}>Back</Typography>
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
{modalView}
|
{modalView}
|
||||||
<Typography variant="h4" style={{ marginLeft: 8, marginTop: isMobile ? null : 40, marginRight: 30, marginBottom: 0, }} color="rgba(241, 241, 241, 1)">
|
<Typography variant="h4" style={{ marginLeft: 8, marginTop: isMobile ? null : 40, marginRight: 30, marginBottom: 0, }} color="rgba(241, 241, 241, 1)">
|
||||||
Start using workflows
|
Start using workflows
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="body2" style={{ marginLeft: 8, marginTop: 10, marginRight: 30, marginBottom: 40, }} color="rgba(158, 158, 158, 1)">
|
<Typography variant="body2" style={{ marginLeft: isMobile ? null : 8, marginTop: isMobile ? 5 : 10, marginRight: isMobile ? null : 100, marginBottom: isMobile ? 20 : 40, }} color="rgba(158, 158, 158, 1)">
|
||||||
Based on what you selected workflows, here are our recommendations! You will see more of these later.
|
Based on what you selected workflows, here are our recommendations! You will see more of these later.
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
|
|||||||
@@ -382,7 +382,7 @@ const Files = (props) => {
|
|||||||
<div style={{ marginTop: 20, marginBottom: 20 }}>
|
<div style={{ marginTop: 20, marginBottom: 20 }}>
|
||||||
<h2 style={{ display: "inline" }}>Files</h2>
|
<h2 style={{ display: "inline" }}>Files</h2>
|
||||||
<span style={{ marginLeft: 25 }}>
|
<span style={{ marginLeft: 25 }}>
|
||||||
Files from Workflows.{" "}
|
Files from Workflows are a way to store as well as edit files.{" "}
|
||||||
<a
|
<a
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
|
|||||||
@@ -43,9 +43,8 @@ import {
|
|||||||
Lightbulb as LightbulbIcon,
|
Lightbulb as LightbulbIcon,
|
||||||
} from "@mui/icons-material";
|
} from "@mui/icons-material";
|
||||||
|
|
||||||
//import { useAlert
|
|
||||||
|
|
||||||
import SearchField from '../components/Searchfield.jsx'
|
import SearchField from '../components/Searchfield.jsx'
|
||||||
|
|
||||||
const hoverColor = "#f85a3e"
|
const hoverColor = "#f85a3e"
|
||||||
const hoverOutColor = "#e8eaf6"
|
const hoverOutColor = "#e8eaf6"
|
||||||
|
|
||||||
@@ -615,7 +614,7 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho
|
|||||||
|
|
||||||
</List>
|
</List>
|
||||||
</div>
|
</div>
|
||||||
<div style={{flex: 1, marginTop: 0,}}>
|
<div style={{flex: 1, marginTop: 10,}}>
|
||||||
<SearchField serverside={serverside} userdata={userdata} />
|
<SearchField serverside={serverside} userdata={userdata} />
|
||||||
</div>
|
</div>
|
||||||
<div style={{flex: 1, display: "flex", flexDirection: "row-reverse"}}>
|
<div style={{flex: 1, display: "flex", flexDirection: "row-reverse"}}>
|
||||||
@@ -747,7 +746,7 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho
|
|||||||
*/}
|
*/}
|
||||||
</List>
|
</List>
|
||||||
</div>
|
</div>
|
||||||
<div style={{flex: 1, marginTop: 0,}}>
|
<div style={{flex: 1, marginTop: 10,}}>
|
||||||
<SearchField serverside={serverside} userdata={userdata} />
|
<SearchField serverside={serverside} userdata={userdata} />
|
||||||
</div>
|
</div>
|
||||||
<div style={{flex: 1, display: "flex", flexDirection: "row-reverse"}}>
|
<div style={{flex: 1, display: "flex", flexDirection: "row-reverse"}}>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -93,10 +93,10 @@ const AuthenticationOauth2 = (props) => {
|
|||||||
appAuthentication,
|
appAuthentication,
|
||||||
setSelectedAction,
|
setSelectedAction,
|
||||||
setNewAppAuth,
|
setNewAppAuth,
|
||||||
isCloud,
|
isCloud,
|
||||||
autoAuth,
|
autoAuth,
|
||||||
authButtonOnly,
|
authButtonOnly,
|
||||||
isLoggedIn,
|
isLoggedIn,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
let navigate = useNavigate();
|
let navigate = useNavigate();
|
||||||
@@ -125,7 +125,7 @@ const AuthenticationOauth2 = (props) => {
|
|||||||
const allscopes = authenticationType.scope !== undefined ? authenticationType.scope : [];
|
const allscopes = authenticationType.scope !== undefined ? authenticationType.scope : [];
|
||||||
|
|
||||||
|
|
||||||
const [selectedScopes, setSelectedScopes] = React.useState(allscopes.length === 1 ? [allscopes[0]] : [])
|
const [selectedScopes, setSelectedScopes] = React.useState(allscopes.length > 0 && allscopes.length <= 3 ? [allscopes[0]] : [])
|
||||||
const [manuallyConfigure, setManuallyConfigure] = React.useState(
|
const [manuallyConfigure, setManuallyConfigure] = React.useState(
|
||||||
defaultConfigSet ? false : true
|
defaultConfigSet ? false : true
|
||||||
);
|
);
|
||||||
@@ -297,15 +297,63 @@ const AuthenticationOauth2 = (props) => {
|
|||||||
|
|
||||||
|
|
||||||
const handleOauth2Request = (client_id, client_secret, oauth_url, scopes, admin_consent, prompt) => {
|
const handleOauth2Request = (client_id, client_secret, oauth_url, scopes, admin_consent, prompt) => {
|
||||||
setButtonClicked(true);
|
|
||||||
//console.log("SCOPES: ", scopes);
|
if ((authenticationType.redirect_uri === undefined || authenticationType.redirect_uri === null || authenticationType.redirect_uri.length === 0) && (authenticationType.token_uri !== undefined && authenticationType.token_uri !== null && authenticationType.token_uri.length > 0)) {
|
||||||
|
console.log("No redirect URI found, and token URI found. Assuming client credentials flow and saving directly in the database")
|
||||||
|
|
||||||
|
// Find app.configuration=true fields in the app.paramters
|
||||||
|
var parsedFields = [{
|
||||||
|
"key": "client_id",
|
||||||
|
"value": client_id,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "client_secret",
|
||||||
|
"value": client_secret,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "scope",
|
||||||
|
"value": scopes.join(","),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "token_uri",
|
||||||
|
"value": authenticationType.token_uri,
|
||||||
|
}]
|
||||||
|
|
||||||
|
const appAuthData = {
|
||||||
|
"label": "OAuth2 for " + selectedApp.name,
|
||||||
|
"app": {
|
||||||
|
"id": selectedApp.id,
|
||||||
|
"name": selectedApp.name,
|
||||||
|
"version": selectedApp.version,
|
||||||
|
"large_image": selectedApp.large_image,
|
||||||
|
},
|
||||||
|
"fields": parsedFields,
|
||||||
|
"type": "oauth2-app",
|
||||||
|
"reference_workflow": workflowId,
|
||||||
|
}
|
||||||
|
|
||||||
|
setNewAppAuth(appAuthData)
|
||||||
|
// Wait 1 second, then get app auth with update
|
||||||
|
//
|
||||||
|
if (getAppAuthentication !== undefined) {
|
||||||
|
setTimeout(() => {
|
||||||
|
getAppAuthentication(true, true, true);
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
setButtonClicked(true);
|
||||||
|
//console.log("SCOPES: ", scopes);
|
||||||
|
|
||||||
client_id = client_id.trim()
|
client_id = client_id.trim()
|
||||||
client_secret = client_secret.trim()
|
client_secret = client_secret.trim()
|
||||||
oauth_url = oauth_url.trim()
|
oauth_url = oauth_url.trim()
|
||||||
|
|
||||||
var resources = "";
|
var resources = "";
|
||||||
if (scopes !== undefined && (scopes !== null) & (scopes.length > 0)) {
|
if (scopes !== undefined && (scopes !== null) & (scopes.length > 0)) {
|
||||||
console.log("IN scope 1")
|
console.log("IN scope 1")
|
||||||
if (offlineAccess === true && !scopes.includes("offline_access")) {
|
if (offlineAccess === true && !scopes.includes("offline_access")) {
|
||||||
|
|
||||||
@@ -324,26 +372,26 @@ const AuthenticationOauth2 = (props) => {
|
|||||||
//console.log("AUTH: ", authenticationType)
|
//console.log("AUTH: ", authenticationType)
|
||||||
//console.log("SCOPES2: ", resources)
|
//console.log("SCOPES2: ", resources)
|
||||||
const redirectUri = `${window.location.protocol}//${window.location.host}/set_authentication`;
|
const redirectUri = `${window.location.protocol}//${window.location.host}/set_authentication`;
|
||||||
const workflowId = workflow !== undefined ? workflow.id : "";
|
const workflowId = workflow !== undefined ? workflow.id : "";
|
||||||
var state = `workflow_id%3D${workflowId}%26reference_action_id%3d${selectedAction.app_id}%26app_name%3d${selectedAction.app_name}%26app_id%3d${selectedAction.app_id}%26app_version%3d${selectedAction.app_version}%26authentication_url%3d${authentication_url}%26scope%3d${resources}%26client_id%3d${client_id}%26client_secret%3d${client_secret}`;
|
var state = `workflow_id%3D${workflowId}%26reference_action_id%3d${selectedAction.app_id}%26app_name%3d${selectedAction.app_name}%26app_id%3d${selectedAction.app_id}%26app_version%3d${selectedAction.app_version}%26authentication_url%3d${authentication_url}%26scope%3d${resources}%26client_id%3d${client_id}%26client_secret%3d${client_secret}`;
|
||||||
|
|
||||||
|
|
||||||
// This is to make sure authorization can be handled WITHOUT being logged in,
|
// This is to make sure authorization can be handled WITHOUT being logged in,
|
||||||
// kind of making it act like an api key
|
// kind of making it act like an api key
|
||||||
// https://shuffler.io/authorization -> 3rd party integration auth
|
// https://shuffler.io/authorization -> 3rd party integration auth
|
||||||
const urlParams = new URLSearchParams(window.location.search);
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
const userAuth = urlParams.get("authorization");
|
const userAuth = urlParams.get("authorization");
|
||||||
if (userAuth !== undefined && userAuth !== null && userAuth.length > 0) {
|
if (userAuth !== undefined && userAuth !== null && userAuth.length > 0) {
|
||||||
console.log("Adding authorization from user side")
|
console.log("Adding authorization from user side")
|
||||||
state += `%26authorization%3d${userAuth}`;
|
state += `%26authorization%3d${userAuth}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for org_id
|
// Check for org_id
|
||||||
const orgId = urlParams.get("org_id");
|
const orgId = urlParams.get("org_id");
|
||||||
if (orgId !== undefined && orgId !== null && orgId.length > 0) {
|
if (orgId !== undefined && orgId !== null && orgId.length > 0) {
|
||||||
console.log("Adding org_id from user side")
|
console.log("Adding org_id from user side")
|
||||||
state += `%26org_id%3d${orgId}`;
|
state += `%26org_id%3d${orgId}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oauth_url !== undefined && oauth_url !== null && oauth_url.length > 0) {
|
if (oauth_url !== undefined && oauth_url !== null && oauth_url.length > 0) {
|
||||||
state += `%26oauth_url%3d${oauth_url}`;
|
state += `%26oauth_url%3d${oauth_url}`;
|
||||||
@@ -363,7 +411,7 @@ const AuthenticationOauth2 = (props) => {
|
|||||||
|
|
||||||
// No prompt forcing
|
// No prompt forcing
|
||||||
//var url = `${authenticationType.redirect_uri}?client_id=${client_id}&redirect_uri=${redirectUri}&response_type=code&prompt=login&scope=${resources}&state=${state}&access_type=offline`;
|
//var url = `${authenticationType.redirect_uri}?client_id=${client_id}&redirect_uri=${redirectUri}&response_type=code&prompt=login&scope=${resources}&state=${state}&access_type=offline`;
|
||||||
var defaultPrompt = "login"
|
var defaultPrompt = "login"
|
||||||
if (prompt !== undefined && prompt !== null && prompt.length > 0) {
|
if (prompt !== undefined && prompt !== null && prompt.length > 0) {
|
||||||
defaultPrompt = prompt
|
defaultPrompt = prompt
|
||||||
}
|
}
|
||||||
@@ -404,9 +452,9 @@ const AuthenticationOauth2 = (props) => {
|
|||||||
//alert('"Secure Payment" window closed!');
|
//alert('"Secure Payment" window closed!');
|
||||||
//
|
//
|
||||||
|
|
||||||
if (getAppAuthentication !== undefined) {
|
if (getAppAuthentication !== undefined) {
|
||||||
getAppAuthentication(true, true, true);
|
getAppAuthentication(true, true, true);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log("Not closed")
|
console.log("Not closed")
|
||||||
}
|
}
|
||||||
@@ -798,6 +846,7 @@ const AuthenticationOauth2 = (props) => {
|
|||||||
}}
|
}}
|
||||||
fullWidth
|
fullWidth
|
||||||
color="primary"
|
color="primary"
|
||||||
|
label={"Client ID"}
|
||||||
placeholder={"Client ID"}
|
placeholder={"Client ID"}
|
||||||
onChange={(event) => {
|
onChange={(event) => {
|
||||||
setClientId(event.target.value);
|
setClientId(event.target.value);
|
||||||
@@ -816,20 +865,22 @@ const AuthenticationOauth2 = (props) => {
|
|||||||
}}
|
}}
|
||||||
fullWidth
|
fullWidth
|
||||||
color="primary"
|
color="primary"
|
||||||
|
label={"Client Secret"}
|
||||||
placeholder={"Client Secret"}
|
placeholder={"Client Secret"}
|
||||||
onChange={(event) => {
|
onChange={(event) => {
|
||||||
setClientSecret(event.target.value);
|
setClientSecret(event.target.value);
|
||||||
//authenticationOption.label = event.target.value
|
//authenticationOption.label = event.target.value
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
{allscopes.length === 0 ? null : "Scopes (access rights)"}
|
||||||
{allscopes.length === 0 ? null : (
|
{allscopes.length === 0 ? null : (
|
||||||
<div style={{width: "100%", marginTop: 10, display: "flex"}}>
|
<div style={{width: "100%", marginTop: 10, display: "flex"}}>
|
||||||
<span>
|
<span>
|
||||||
Scopes
|
|
||||||
<Select
|
<Select
|
||||||
multiple
|
multiple
|
||||||
underline={false}
|
underline={false}
|
||||||
value={selectedScopes}
|
value={selectedScopes}
|
||||||
|
label="Scopes"
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: theme.palette.inputColor,
|
backgroundColor: theme.palette.inputColor,
|
||||||
color: "white",
|
color: "white",
|
||||||
@@ -855,17 +906,20 @@ const AuthenticationOauth2 = (props) => {
|
|||||||
})}
|
})}
|
||||||
</Select>
|
</Select>
|
||||||
</span>
|
</span>
|
||||||
<span>
|
|
||||||
<Tooltip
|
{((authenticationType.redirect_uri === undefined || authenticationType.redirect_uri === null || authenticationType.redirect_uri.length === 0) && (authenticationType.token_uri !== undefined && authenticationType.token_uri !== null && authenticationType.token_uri.length > 0)) ? null :
|
||||||
color="primary"
|
<span>
|
||||||
title={"Automatic Refresh (default: true)"}
|
<Tooltip
|
||||||
placement="top"
|
color="primary"
|
||||||
>
|
title={"Automatic Refresh (default: true)"}
|
||||||
<Checkbox style={{paddingTop: 20}} color="secondary" checked={offlineAccess} onClick={() => {
|
placement="top"
|
||||||
setOfflineAccess(!offlineAccess)
|
>
|
||||||
}}/>
|
<Checkbox style={{paddingTop: 20}} color="secondary" checked={offlineAccess} onClick={() => {
|
||||||
</Tooltip>
|
setOfflineAccess(!offlineAccess)
|
||||||
</span>
|
}}/>
|
||||||
|
</Tooltip>
|
||||||
|
</span>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
@@ -882,19 +936,14 @@ const AuthenticationOauth2 = (props) => {
|
|||||||
variant="contained"
|
variant="contained"
|
||||||
fullWidth
|
fullWidth
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
handleOauth2Request(
|
handleOauth2Request(clientId, clientSecret, oauthUrl, selectedScopes);
|
||||||
clientId,
|
|
||||||
clientSecret,
|
|
||||||
oauthUrl,
|
|
||||||
selectedScopes
|
|
||||||
);
|
|
||||||
}}
|
}}
|
||||||
color="primary"
|
color="primary"
|
||||||
>
|
>
|
||||||
{buttonClicked ? (
|
{buttonClicked ? (
|
||||||
<CircularProgress style={{ color: "white" }} />
|
<CircularProgress style={{ color: "white" }} />
|
||||||
) : (
|
) : (
|
||||||
"Manually Authenticate"
|
"Authenticate"
|
||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,800 @@
|
|||||||
|
import React, { useState, useEffect, useRef } from 'react';
|
||||||
|
|
||||||
|
import theme from '../theme.jsx';
|
||||||
|
import { useNavigate, Link, useParams } from "react-router-dom";
|
||||||
|
|
||||||
|
import {
|
||||||
|
Chip,
|
||||||
|
IconButton,
|
||||||
|
TextField,
|
||||||
|
InputAdornment,
|
||||||
|
List,
|
||||||
|
Card,
|
||||||
|
ListItem,
|
||||||
|
ListItemAvatar,
|
||||||
|
ListItemText,
|
||||||
|
Avatar,
|
||||||
|
Grid,
|
||||||
|
Typography,
|
||||||
|
Tooltip,
|
||||||
|
Divider,
|
||||||
|
Button,
|
||||||
|
} from '@mui/material';
|
||||||
|
import ArticleIcon from '@mui/icons-material/Article';
|
||||||
|
import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight';
|
||||||
|
import ManageSearchIcon from '@mui/icons-material/ManageSearch';
|
||||||
|
import {
|
||||||
|
AvatarGroup,
|
||||||
|
} from "@mui/material"
|
||||||
|
|
||||||
|
import { Search as SearchIcon, Close as CloseIcon, Folder as FolderIcon, Code as CodeIcon, LibraryBooks as LibraryBooksIcon } from '@mui/icons-material'
|
||||||
|
|
||||||
|
import algoliasearch from 'algoliasearch/lite';
|
||||||
|
import aa from 'search-insights'
|
||||||
|
import { InstantSearch, Configure, connectSearchBox, connectHits, Index } from 'react-instantsearch-dom';
|
||||||
|
//import { InstantSearch, SearchBox, Hits, connectSearchBox, connectHits, Index } from 'react-instantsearch-dom';
|
||||||
|
|
||||||
|
// https://www.algolia.com/doc/api-reference/widgets/search-box/react/
|
||||||
|
const chipStyle = {
|
||||||
|
backgroundColor: "#3d3f43", height: 30, marginRight: 5, paddingLeft: 5, paddingRight: 5, height: 28, cursor: "pointer", borderColor: "#3d3f43", color: "white",
|
||||||
|
}
|
||||||
|
|
||||||
|
const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240")
|
||||||
|
const SearchData = props => {
|
||||||
|
const { serverside, userdata, setModalOpen, modalOpen } = props
|
||||||
|
|
||||||
|
let navigate = useNavigate();
|
||||||
|
const borderRadius = 3
|
||||||
|
const node = useRef()
|
||||||
|
const [searchOpen, setSearchOpen] = useState(true)
|
||||||
|
const [value, setValue] = useState("");
|
||||||
|
const [userTyped, setUserTyped] = useState(false)
|
||||||
|
|
||||||
|
if (serverside === true) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
//if (window !== undefined && window.location !== undefined && window.location.pathname === "/search") {
|
||||||
|
// return null
|
||||||
|
//}
|
||||||
|
|
||||||
|
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
|
||||||
|
|
||||||
|
//if (window.location.pathname !== oldPath) {
|
||||||
|
// setSearchOpen(false)
|
||||||
|
// setOldPath(window.location.pathname)
|
||||||
|
//}
|
||||||
|
|
||||||
|
//if (window.location.pathname === "/search") {
|
||||||
|
// setModalOpen(true)
|
||||||
|
//}
|
||||||
|
// if (window.location.pathname === "/docs" || window.location.pathname === "/apps" || window.location.pathname === "/usecases" ) {
|
||||||
|
// setModalOpen(false)
|
||||||
|
// }
|
||||||
|
|
||||||
|
//useEffect(() => {
|
||||||
|
// if (searchOpen) {
|
||||||
|
// var tarfield = document.getElementById("shuffle_search_field")
|
||||||
|
// tarfield.focus()
|
||||||
|
// }
|
||||||
|
//}, searchOpen)
|
||||||
|
|
||||||
|
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 });
|
||||||
|
setSearchOpen(false)
|
||||||
|
setModalOpen(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
};
|
||||||
|
/*
|
||||||
|
endAdornment: (
|
||||||
|
<InputAdornment position="end" style={{textAlign: "right", zIndex: 5001, cursor: "pointer", width: 100, }} onMouseOver={(event) => {
|
||||||
|
event.preventDefault()
|
||||||
|
}}>
|
||||||
|
<CloseIcon style={{marginRight: 5,}} onClick={() => {
|
||||||
|
setSearchOpen(false)
|
||||||
|
}} />
|
||||||
|
</InputAdornment>
|
||||||
|
),
|
||||||
|
*/
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form id="search_form" noValidate type="searchbox" action="" role="search" onClick={() => {
|
||||||
|
}}>
|
||||||
|
<TextField
|
||||||
|
fullWidth
|
||||||
|
style={{ backgroundColor: theme.palette.surfaceColor, borderRadius: borderRadius, minWidth: "100%", maxWidth: "100%", }}
|
||||||
|
InputProps={{
|
||||||
|
style: {
|
||||||
|
color: "white",
|
||||||
|
fontSize: "1em",
|
||||||
|
height: 50,
|
||||||
|
margin: 0,
|
||||||
|
fontSize: "0.9em",
|
||||||
|
paddingLeft: 10,
|
||||||
|
},
|
||||||
|
disableUnderline: true,
|
||||||
|
startAdornment: (
|
||||||
|
<InputAdornment position="start">
|
||||||
|
<SearchIcon style={{ marginLeft: 5, marginRight: 10, color: "#f86a3e", }} />
|
||||||
|
</InputAdornment>
|
||||||
|
),
|
||||||
|
endAdornment: (
|
||||||
|
<InputAdornment position="start">
|
||||||
|
<Typography position="end" style={{ fontSize: 18, color: "#f86a3e", marginLeft: 10, textTransform: "lowercase", width: 30 }}>
|
||||||
|
esc
|
||||||
|
</Typography>
|
||||||
|
</InputAdornment>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
autoComplete='off'
|
||||||
|
type="search"
|
||||||
|
color="primary"
|
||||||
|
placeholder="Find Public Apps, Workflows, Documentation..."
|
||||||
|
value={currentRefinement}
|
||||||
|
onKeyDown={keyPressHandler}
|
||||||
|
id="shuffle_search_field"
|
||||||
|
onClick={(event) => {
|
||||||
|
if (!searchOpen) {
|
||||||
|
setSearchOpen(true)
|
||||||
|
setTimeout(() => {
|
||||||
|
var tarfield = document.getElementById("shuffle_search_field")
|
||||||
|
//console.log("TARFIELD: ", tarfield)
|
||||||
|
tarfield.focus()
|
||||||
|
}, 250)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onBlur={(event) => {
|
||||||
|
//setTimeout(() => {
|
||||||
|
// setSearchOpen(false)
|
||||||
|
//}, 500)
|
||||||
|
}}
|
||||||
|
onChange={(event) => {
|
||||||
|
refine(event.currentTarget.value)
|
||||||
|
}}
|
||||||
|
limit={5}
|
||||||
|
/>
|
||||||
|
{/*isSearchStalled ? 'My search is stalled' : ''*/}
|
||||||
|
</form>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const WorkflowHits = ({ hits }) => {
|
||||||
|
const [mouseHoverIndex, setMouseHoverIndex] = useState(0)
|
||||||
|
|
||||||
|
var tmp = searchOpen
|
||||||
|
if (!searchOpen) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const positionInfo = document.activeElement.getBoundingClientRect()
|
||||||
|
const outerlistitemStyle = {
|
||||||
|
width: "100%",
|
||||||
|
overflowX: "hidden",
|
||||||
|
overflowY: "hidden",
|
||||||
|
borderBottom: "1px solid rgba(255,255,255,0.4)",
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hits.length > 4) {
|
||||||
|
hits = hits.slice(0, 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
var type = "workflows"
|
||||||
|
const baseImage = <CodeIcon />
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card elevation={0} style={{ marginRight: 10, color: "white", zIndex: 1002, backgroundColor: theme.palette.inputColor, width: "100%", left: 75, boxShadows: "none", }}>
|
||||||
|
<Typography variant="h6" style={{ margin: "10px 10px 0px 20px", }}>
|
||||||
|
Workflows
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<List style={{ backgroundColor: theme.palette.inputColor, }}>
|
||||||
|
{hits.length === 0 ?
|
||||||
|
<ListItem style={outerlistitemStyle}>
|
||||||
|
<ListItemAvatar onClick={() => console.log(hits)}>
|
||||||
|
<Avatar>
|
||||||
|
<FolderIcon />
|
||||||
|
</Avatar>
|
||||||
|
</ListItemAvatar>
|
||||||
|
<ListItemText
|
||||||
|
primary={"No workflows found."}
|
||||||
|
secondary={"Try a broader search term"}
|
||||||
|
/>
|
||||||
|
</ListItem>
|
||||||
|
:
|
||||||
|
hits.map((hit, index) => {
|
||||||
|
const innerlistitemStyle = {
|
||||||
|
width: positionInfo.width + 35,
|
||||||
|
overflowX: "hidden",
|
||||||
|
overflowY: "hidden",
|
||||||
|
borderBottom: "1px solid rgba(255,255,255,0.4)",
|
||||||
|
backgroundColor: mouseHoverIndex === index ? "#1f2023" : "inherit",
|
||||||
|
cursor: "pointer",
|
||||||
|
marginLeft: 5,
|
||||||
|
marginRight: 5,
|
||||||
|
maxHeight: 75,
|
||||||
|
minHeight: 75,
|
||||||
|
maxWidth: 420,
|
||||||
|
minWidth: "100%",
|
||||||
|
}
|
||||||
|
|
||||||
|
const name = hit.name === undefined ?
|
||||||
|
hit.filename.charAt(0).toUpperCase() + hit.filename.slice(1).replaceAll("_", " ") + " - " + hit.title :
|
||||||
|
(hit.name.charAt(0).toUpperCase() + hit.name.slice(1)).replaceAll("_", " ")
|
||||||
|
const secondaryText = hit.description !== undefined && hit.description !== null && hit.description.length > 3 ? hit.description.slice(0, 40) + "..." : ""
|
||||||
|
const appGroup = hit.action_references === undefined || hit.action_references === null ? [] : hit.action_references
|
||||||
|
const avatar = baseImage
|
||||||
|
|
||||||
|
var parsedUrl = isCloud ? `/workflows/${hit.objectID}` : `https://shuffler.io/workflows/${hit.objectID}`
|
||||||
|
|
||||||
|
parsedUrl += `?queryID=${hit.__queryID}`
|
||||||
|
|
||||||
|
// <a rel="noopener noreferrer" href="https://www.algolia.com/" target="_blank" style={{textDecoration: "none", color: "white"}}>
|
||||||
|
return (
|
||||||
|
<Link key={hit.objectID} to={parsedUrl} rel="noopener noreferrer" style={{ textDecoration: "none", color: "white", }} onClick={(event) => {
|
||||||
|
//console.log("CLICK")
|
||||||
|
setSearchOpen(true)
|
||||||
|
|
||||||
|
aa('init', {
|
||||||
|
appId: searchClient.appId,
|
||||||
|
apiKey: searchClient.transporter.queryParameters["x-algolia-api-key"]
|
||||||
|
})
|
||||||
|
|
||||||
|
const timestamp = new Date().getTime()
|
||||||
|
aa('sendEvents', [
|
||||||
|
{
|
||||||
|
eventType: 'click',
|
||||||
|
eventName: 'Workflow Clicked',
|
||||||
|
index: 'workflows',
|
||||||
|
objectIDs: [hit.objectID],
|
||||||
|
timestamp: timestamp,
|
||||||
|
queryID: hit.__queryID,
|
||||||
|
positions: [hit.__position],
|
||||||
|
userToken: userdata === undefined || userdata === null || userdata.id === undefined ? "unauthenticated" : userdata.id,
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
if (!isCloud) {
|
||||||
|
event.preventDefault()
|
||||||
|
window.open(parsedUrl, '_blank');
|
||||||
|
}
|
||||||
|
setModalOpen(false)
|
||||||
|
}}>
|
||||||
|
<ListItem key={hit.objectID} style={innerlistitemStyle} onMouseOver={() => {
|
||||||
|
setMouseHoverIndex(index)
|
||||||
|
}}>
|
||||||
|
<ListItemAvatar>
|
||||||
|
{avatar}
|
||||||
|
</ListItemAvatar>
|
||||||
|
<div style={{}}>
|
||||||
|
<ListItemText
|
||||||
|
primary={name}
|
||||||
|
/>
|
||||||
|
<AvatarGroup max={10} style={{ flexDirection: "row", padding: 0, margin: 0, itemAlign: "left", textAlign: "left", }}>
|
||||||
|
{appGroup.map((app, index) => {
|
||||||
|
// Putting all this in secondary of ListItemText looked weird.
|
||||||
|
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>
|
||||||
|
{/*
|
||||||
|
<ListItemSecondaryAction>
|
||||||
|
<IconButton edge="end" aria-label="delete">
|
||||||
|
<DeleteIcon />
|
||||||
|
</IconButton>
|
||||||
|
</ListItemSecondaryAction>
|
||||||
|
*/}
|
||||||
|
</ListItem>
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</List>
|
||||||
|
{/*
|
||||||
|
<span style={{display: "flex", textAlign: "left", float: "left", position: "absolute", left: 15, bottom: 10, }}>
|
||||||
|
<Link to="/search" style={{textDecoration: "none", color: "#f85a3e"}}>
|
||||||
|
<Typography variant="body2" style={{}}>
|
||||||
|
See all workflows
|
||||||
|
</Typography>
|
||||||
|
</Link>
|
||||||
|
</span>
|
||||||
|
*/}
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const AppHits = ({ hits }) => {
|
||||||
|
const [mouseHoverIndex, setMouseHoverIndex] = useState(0)
|
||||||
|
|
||||||
|
var tmp = searchOpen
|
||||||
|
if (!searchOpen) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const positionInfo = document.activeElement.getBoundingClientRect()
|
||||||
|
const outerlistitemStyle = {
|
||||||
|
width: "100%",
|
||||||
|
overflowX: "hidden",
|
||||||
|
overflowY: "hidden",
|
||||||
|
borderBottom: "1px solid rgba(255,255,255,0.4)",
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hits.length > 4) {
|
||||||
|
hits = hits.slice(0, 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
var type = "app"
|
||||||
|
const baseImage = <LibraryBooksIcon />
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card elevation={0} style={{ marginRight: 10, color: "white", zIndex: 1001, backgroundColor: theme.palette.inputColor, width: "100%", left: -30, boxShadows: "none", }}>
|
||||||
|
{/* <IconButton style={{ zIndex: 5000, position: "absolute", right: 14, color: "grey" }} onClick={() => {
|
||||||
|
setSearchOpen(false)
|
||||||
|
}}>
|
||||||
|
<CloseIcon />
|
||||||
|
</IconButton> */}
|
||||||
|
<Typography variant="h6" style={{ margin: "10px 10px 0px 20px", }}>
|
||||||
|
Apps
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<List style={{ backgroundColor: theme.palette.inputColor, }}>
|
||||||
|
{hits.length === 0 ?
|
||||||
|
<ListItem style={outerlistitemStyle}>
|
||||||
|
<ListItemAvatar onClick={() => console.log(hits)}>
|
||||||
|
<Avatar>
|
||||||
|
<FolderIcon />
|
||||||
|
</Avatar>
|
||||||
|
</ListItemAvatar>
|
||||||
|
<ListItemText
|
||||||
|
primary={"No apps found."}
|
||||||
|
secondary={"Try a broader search term"}
|
||||||
|
/>
|
||||||
|
</ListItem>
|
||||||
|
:
|
||||||
|
hits.map((hit, index) => {
|
||||||
|
const innerlistitemStyle = {
|
||||||
|
width: positionInfo.width + 35,
|
||||||
|
overflowX: "hidden",
|
||||||
|
overflowY: "hidden",
|
||||||
|
borderBottom: "1px solid rgba(255,255,255,0.4)",
|
||||||
|
backgroundColor: mouseHoverIndex === index ? "#1f2023" : "inherit",
|
||||||
|
cursor: "pointer",
|
||||||
|
marginLeft: 5,
|
||||||
|
marginRight: 5,
|
||||||
|
maxHeight: 75,
|
||||||
|
minHeight: 75,
|
||||||
|
maxWidth: 420,
|
||||||
|
minWidth: "100%",
|
||||||
|
}
|
||||||
|
|
||||||
|
const name = hit.name === undefined ?
|
||||||
|
hit.filename.charAt(0).toUpperCase() + hit.filename.slice(1).replaceAll("_", " ") + " - " + hit.title :
|
||||||
|
(hit.name.charAt(0).toUpperCase() + hit.name.slice(1)).replaceAll("_", " ")
|
||||||
|
|
||||||
|
var secondaryText = hit.data !== undefined ? hit.data.slice(0, 40) + "..." : ""
|
||||||
|
const avatar = hit.image_url === undefined ?
|
||||||
|
baseImage
|
||||||
|
:
|
||||||
|
<Avatar
|
||||||
|
src={hit.image_url}
|
||||||
|
variant="rounded"
|
||||||
|
/>
|
||||||
|
|
||||||
|
//console.log(hit)
|
||||||
|
if (hit.categories !== undefined && hit.categories !== null && hit.categories.length > 0) {
|
||||||
|
secondaryText = hit.categories.slice(0, 3).map((data, index) => {
|
||||||
|
if (index === 0) {
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
return ", " + data
|
||||||
|
|
||||||
|
/*
|
||||||
|
<Chip
|
||||||
|
key={index}
|
||||||
|
style={chipStyle}
|
||||||
|
label={data}
|
||||||
|
onClick={() => {
|
||||||
|
//handleChipClick
|
||||||
|
}}
|
||||||
|
variant="outlined"
|
||||||
|
color="primary"
|
||||||
|
/>
|
||||||
|
*/
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var parsedUrl = isCloud ? `/apps/${hit.objectID}` : `https://shuffler.io/apps/${hit.objectID}`
|
||||||
|
parsedUrl += `?queryID=${hit.__queryID}`
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Link key={hit.objectID} to={parsedUrl} style={{ textDecoration: "none", color: "white", }} onClick={(event) => {
|
||||||
|
console.log("CLICK")
|
||||||
|
setSearchOpen(true)
|
||||||
|
setModalOpen(false)
|
||||||
|
|
||||||
|
aa('init', {
|
||||||
|
appId: searchClient.appId,
|
||||||
|
apiKey: searchClient.transporter.queryParameters["x-algolia-api-key"]
|
||||||
|
})
|
||||||
|
|
||||||
|
const timestamp = new Date().getTime()
|
||||||
|
aa('sendEvents', [
|
||||||
|
{
|
||||||
|
eventType: 'click',
|
||||||
|
eventName: 'App Clicked',
|
||||||
|
index: 'appsearch',
|
||||||
|
objectIDs: [hit.objectID],
|
||||||
|
timestamp: timestamp,
|
||||||
|
queryID: hit.__queryID,
|
||||||
|
positions: [hit.__position],
|
||||||
|
userToken: userdata === undefined || userdata === null || userdata.id === undefined ? "unauthenticated" : userdata.id,
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
if (!isCloud) {
|
||||||
|
event.preventDefault()
|
||||||
|
window.open(parsedUrl, '_blank');
|
||||||
|
}
|
||||||
|
}}>
|
||||||
|
<ListItem key={hit.objectID} style={innerlistitemStyle} onMouseOver={() => {
|
||||||
|
setMouseHoverIndex(index)
|
||||||
|
}}>
|
||||||
|
<ListItemAvatar>
|
||||||
|
{avatar}
|
||||||
|
</ListItemAvatar>
|
||||||
|
<ListItemText
|
||||||
|
primary={name}
|
||||||
|
secondary={secondaryText}
|
||||||
|
/>
|
||||||
|
{/*
|
||||||
|
<ListItemSecondaryAction>
|
||||||
|
<IconButton edge="end" aria-label="delete">
|
||||||
|
<DeleteIcon />
|
||||||
|
</IconButton>
|
||||||
|
</ListItemSecondaryAction>
|
||||||
|
*/}
|
||||||
|
</ListItem>
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</List>
|
||||||
|
{/* <span style={{ display: "flex", textAlign: "left", float: "left", position: "absolute", left: 15, bottom: 10, }}>
|
||||||
|
<Link to="/search" style={{ textDecoration: "none", color: "#f85a3e" }}>
|
||||||
|
<Typography variant="body1" style={{}}>
|
||||||
|
See more
|
||||||
|
</Typography>
|
||||||
|
</Link>
|
||||||
|
</span> */}
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const DocHits = ({ hits }) => {
|
||||||
|
const [mouseHoverIndex, setMouseHoverIndex] = useState(0)
|
||||||
|
|
||||||
|
var tmp = searchOpen
|
||||||
|
if (!searchOpen) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const positionInfo = document.activeElement.getBoundingClientRect()
|
||||||
|
const outerlistitemStyle = {
|
||||||
|
width: "100%",
|
||||||
|
overflowX: "hidden",
|
||||||
|
overflowY: "hidden",
|
||||||
|
borderBottom: "1px solid rgba(255,255,255,0.4)",
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hits.length > 4) {
|
||||||
|
hits = hits.slice(0, 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
const type = "documentation"
|
||||||
|
const baseImage = <LibraryBooksIcon />
|
||||||
|
|
||||||
|
//console.log(type, hits.length, hits)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card elevation={0} style={{ marginRight: 10, color: "white", zIndex: 1002, backgroundColor: theme.palette.inputColor, width: "100%", left: 470, boxShadows: "none", }}>
|
||||||
|
{/* <IconButton style={{ zIndex: 5000, position: "absolute", right: 14, color: "grey" }} onClick={() => {
|
||||||
|
setSearchOpen(false)
|
||||||
|
}}>
|
||||||
|
<CloseIcon />
|
||||||
|
</IconButton> */}
|
||||||
|
<Typography variant="h6" style={{ margin: "10px 10px 0px 20px", }}>
|
||||||
|
Documentation
|
||||||
|
</Typography>
|
||||||
|
{/*
|
||||||
|
<IconButton edge="end" aria-label="delete" style={{position: "absolute", top: 5, right: 15,}} onClick={() => {
|
||||||
|
setSearchOpen(false)
|
||||||
|
}}>
|
||||||
|
<DeleteIcon />
|
||||||
|
</IconButton>
|
||||||
|
*/}
|
||||||
|
<List style={{ backgroundColor: theme.palette.inputColor, }}>
|
||||||
|
{hits.length === 0 ?
|
||||||
|
<ListItem style={outerlistitemStyle}>
|
||||||
|
<ListItemAvatar onClick={() => console.log(hits)}>
|
||||||
|
<Avatar>
|
||||||
|
<FolderIcon />
|
||||||
|
</Avatar>
|
||||||
|
</ListItemAvatar>
|
||||||
|
<ListItemText
|
||||||
|
primary={"No documentation."}
|
||||||
|
secondary={"Try a broader search term"}
|
||||||
|
/>
|
||||||
|
</ListItem>
|
||||||
|
:
|
||||||
|
hits.map((hit, index) => {
|
||||||
|
const innerlistitemStyle = {
|
||||||
|
width: positionInfo.width + 35,
|
||||||
|
overflowX: "hidden",
|
||||||
|
overflowY: "hidden",
|
||||||
|
borderBottom: "1px solid rgba(255,255,255,0.4)",
|
||||||
|
backgroundColor: mouseHoverIndex === index ? "#1f2023" : "inherit",
|
||||||
|
cursor: "pointer",
|
||||||
|
marginLeft: 5,
|
||||||
|
marginRight: 5,
|
||||||
|
maxHeight: 75,
|
||||||
|
minHeight: 75,
|
||||||
|
maxWidth: 420,
|
||||||
|
minWidth: "100%",
|
||||||
|
}
|
||||||
|
|
||||||
|
var name = hit.name === undefined ?
|
||||||
|
hit.filename.charAt(0).toUpperCase() + hit.filename.slice(1).replaceAll("_", " ") + " - " + hit.title
|
||||||
|
:
|
||||||
|
(hit.name.charAt(0).toUpperCase() + hit.name.slice(1)).replaceAll("_", " ")
|
||||||
|
|
||||||
|
if (name.length > 30) {
|
||||||
|
name = name.slice(0, 30) + "..."
|
||||||
|
}
|
||||||
|
const secondaryText = hit.data !== undefined ? hit.data.slice(0, 40) + "..." : ""
|
||||||
|
const avatar = hit.image_url === undefined ?
|
||||||
|
baseImage
|
||||||
|
:
|
||||||
|
<Avatar
|
||||||
|
src={hit.image_url}
|
||||||
|
variant="rounded"
|
||||||
|
/>
|
||||||
|
|
||||||
|
var parsedUrl = hit.urlpath !== undefined ? hit.urlpath : ""
|
||||||
|
parsedUrl += `?queryID=${hit.__queryID}`
|
||||||
|
if (parsedUrl.includes("/apps/")) {
|
||||||
|
const extraHash = hit.url_hash === undefined ? "" : `#${hit.url_hash}`
|
||||||
|
|
||||||
|
parsedUrl = `/apps/${hit.filename}`
|
||||||
|
parsedUrl += `?tab=docs&queryID=${hit.__queryID}${extraHash}`
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Link key={hit.objectID} to={parsedUrl} style={{ textDecoration: "none", color: "white", }} onClick={(event) => {
|
||||||
|
aa('init', {
|
||||||
|
appId: searchClient.appId,
|
||||||
|
apiKey: searchClient.transporter.queryParameters["x-algolia-api-key"]
|
||||||
|
})
|
||||||
|
|
||||||
|
const timestamp = new Date().getTime()
|
||||||
|
aa('sendEvents', [
|
||||||
|
{
|
||||||
|
eventType: 'click',
|
||||||
|
eventName: 'Document Clicked',
|
||||||
|
index: 'documentation',
|
||||||
|
objectIDs: [hit.objectID],
|
||||||
|
timestamp: timestamp,
|
||||||
|
queryID: hit.__queryID,
|
||||||
|
positions: [hit.__position],
|
||||||
|
userToken: userdata === undefined || userdata === null || userdata.id === undefined ? "unauthenticated" : userdata.id,
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
console.log("CLICK")
|
||||||
|
setSearchOpen(true)
|
||||||
|
setModalOpen(false)
|
||||||
|
}}>
|
||||||
|
<ListItem key={hit.objectID} style={innerlistitemStyle} onMouseOver={() => {
|
||||||
|
setMouseHoverIndex(index)
|
||||||
|
}}>
|
||||||
|
<ListItemAvatar>
|
||||||
|
{avatar}
|
||||||
|
</ListItemAvatar>
|
||||||
|
<ListItemText
|
||||||
|
primary={name}
|
||||||
|
secondary={secondaryText}
|
||||||
|
/>
|
||||||
|
{/*
|
||||||
|
<ListItemSecondaryAction>
|
||||||
|
<IconButton edge="end" aria-label="delete">
|
||||||
|
<DeleteIcon />
|
||||||
|
</IconButton>
|
||||||
|
</ListItemSecondaryAction>
|
||||||
|
*/}
|
||||||
|
</ListItem>
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</List>
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const CustomSearchBox = connectSearchBox(SearchBox)
|
||||||
|
const CustomAppHits = connectHits(AppHits)
|
||||||
|
const CustomWorkflowHits = connectHits(WorkflowHits)
|
||||||
|
const CustomDocHits = connectHits(DocHits)
|
||||||
|
|
||||||
|
const modalView = (
|
||||||
|
<div>
|
||||||
|
<Grid container style={{ display: "contents" }}>
|
||||||
|
<Grid item xs="auto" style={{ marginTop: 12 }}>
|
||||||
|
<Index indexName="appsearch">
|
||||||
|
<CustomAppHits />
|
||||||
|
</Index>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs="auto" style={{ marginTop: 12 }}>
|
||||||
|
<Index indexName="workflows">
|
||||||
|
<CustomWorkflowHits />
|
||||||
|
</Index>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs="auto" style={{ marginTop: 12 }}>
|
||||||
|
<Index indexName="documentation">
|
||||||
|
<CustomDocHits />
|
||||||
|
</Index>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
const gettingStartData = (
|
||||||
|
<Grid
|
||||||
|
container
|
||||||
|
direction="row"
|
||||||
|
alignItems="center"
|
||||||
|
// justify="space-evenly"
|
||||||
|
>
|
||||||
|
<Grid item xs={6} style={{ alignItems: "center", flexDirection: "row" }}>
|
||||||
|
<List style={{ width: "100%", marginLeft: 10, color: "var(--Paragraph-text, #C8C8C8)" }}>
|
||||||
|
<ListItem>
|
||||||
|
<ArticleIcon style={{ marginRight: 10, display: "flex", width: 22 }} />
|
||||||
|
<Typography variant="body1" style={{ fontSize: 16 }}>Getting Started</Typography>
|
||||||
|
</ListItem>
|
||||||
|
<div style={{ marginLeft: 25, }}>
|
||||||
|
<ListItem>
|
||||||
|
<Link onClick={() => { window.location = "/docs"; }} style={{ textDecoration: "none", color: "var(--Paragraph-text, #C8C8C8)", display: "flex" }}>
|
||||||
|
<Typography variant="body1" style={{ fontSize: 16, }}>Documentation</Typography>
|
||||||
|
<KeyboardArrowRightIcon />
|
||||||
|
</Link>
|
||||||
|
</ListItem>
|
||||||
|
|
||||||
|
<ListItem>
|
||||||
|
<Link to="https://github.com/Shuffle/Shuffle/blob/main/.github/install-guide.md" style={{ textDecoration: "none", color: "var(--Paragraph-text, #C8C8C8)", display: "flex" }}>
|
||||||
|
<Typography variant="body1" style={{ fontSize: 16, }}>Onprem Installation</Typography>
|
||||||
|
<KeyboardArrowRightIcon />
|
||||||
|
</Link>
|
||||||
|
</ListItem>
|
||||||
|
|
||||||
|
|
||||||
|
<ListItem>
|
||||||
|
<Link to="/usecases" style={{ textDecoration: "none", color: "var(--Paragraph-text, #C8C8C8)", display: "flex" }}>
|
||||||
|
<Typography variant="body1" style={{ fontSize: 16, }}>Explore Usecases</Typography>
|
||||||
|
<KeyboardArrowRightIcon />
|
||||||
|
</Link>
|
||||||
|
</ListItem>
|
||||||
|
<ListItem>
|
||||||
|
<Link to="/search?tab=workflows" style={{ textDecoration: "none", color: "var(--Paragraph-text, #C8C8C8)", display: "flex" }}>
|
||||||
|
<Typography variant="body1" style={{ fontSize: 16, }}>Find public workflows</Typography>
|
||||||
|
<KeyboardArrowRightIcon />
|
||||||
|
</Link>
|
||||||
|
</ListItem>
|
||||||
|
</div>
|
||||||
|
</List>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={6} style={{ alignItems: "center", flexDirection: "row", width: 22 }}>
|
||||||
|
<List style={{ width: "100%", marginLeft: 10, color: "var(--Paragraph-text, #C8C8C8)", }}>
|
||||||
|
<ListItem>
|
||||||
|
<ManageSearchIcon style={{ marginRight: 10, display: "flex" }} />
|
||||||
|
<Typography variant="body1" style={{ display: "flex", fontSize: 16 }}>Popular searches</Typography>
|
||||||
|
</ListItem>
|
||||||
|
<div style={{ marginLeft: 35 }}>
|
||||||
|
<ListItem>
|
||||||
|
<Link to="/docs/app_creation" style={{ textDecoration: "none", color: "var(--Paragraph-text, #C8C8C8)", display: "flex" }}>
|
||||||
|
<Typography variant="body1" style={{ fontSize: 16, }}>Create Apps</Typography>
|
||||||
|
<KeyboardArrowRightIcon />
|
||||||
|
</Link>
|
||||||
|
</ListItem>
|
||||||
|
<ListItem>
|
||||||
|
<Link to="/apps" style={{ textDecoration: "none", color: "var(--Paragraph-text, #C8C8C8)", display: "flex" }}>
|
||||||
|
<Typography variant="body1" style={{ fontSize: 16, }}>Find Apps</Typography>
|
||||||
|
<KeyboardArrowRightIcon />
|
||||||
|
</Link>
|
||||||
|
</ListItem>
|
||||||
|
<ListItem>
|
||||||
|
<Link to="/workflows" style={{ textDecoration: "none", color: "var(--Paragraph-text, #C8C8C8)", display: "flex" }}>
|
||||||
|
<Typography variant="body1" style={{ fontSize: 16, }}>Workflows</Typography>
|
||||||
|
<KeyboardArrowRightIcon />
|
||||||
|
</Link>
|
||||||
|
</ListItem>
|
||||||
|
<ListItem>
|
||||||
|
<Link to="/creators" style={{ textDecoration: "none", color: "var(--Paragraph-text, #C8C8C8)", display: "flex" }}>
|
||||||
|
<Typography variant="body1" style={{ fontSize: 16, }}>Creator</Typography>
|
||||||
|
<KeyboardArrowRightIcon />
|
||||||
|
</Link>
|
||||||
|
</ListItem>
|
||||||
|
</div>
|
||||||
|
</List>
|
||||||
|
</Grid>
|
||||||
|
{/* <Grid item xs={12} style={{ alignItems: "center", flexDirection: "row", width: 22 }}>
|
||||||
|
<List style={{ width: "100%", marginLeft: 10, color: "var(--Paragraph-text, #C8C8C8)", }}>
|
||||||
|
<ListItem>
|
||||||
|
<ManageSearchIcon style={{ marginRight: 10, display: "flex" }} />
|
||||||
|
<Typography variant="body1" style={{ display: "flex", fontSize: 16 }}>Popular searches</Typography>
|
||||||
|
</ListItem>
|
||||||
|
<div style={{ marginLeft: 35 }}>
|
||||||
|
<ListItem>
|
||||||
|
|
||||||
|
<Typography variant="body1" style={{ fontSize: 16, }}>Apps</Typography>
|
||||||
|
<KeyboardArrowRightIcon />
|
||||||
|
</ListItem>
|
||||||
|
<ListItem>
|
||||||
|
<Typography variant="body1" style={{ fontSize: 16, }}>Workflows</Typography>
|
||||||
|
<KeyboardArrowRightIcon />
|
||||||
|
</ListItem>
|
||||||
|
<ListItem>
|
||||||
|
<Typography variant="body1" style={{ fontSize: 16, }}>Creator</Typography>
|
||||||
|
<KeyboardArrowRightIcon />
|
||||||
|
</ListItem>
|
||||||
|
</div>
|
||||||
|
</List>
|
||||||
|
</Grid> */}
|
||||||
|
<Grid style={{ textAlign: "end", width: "100%", textTransform: 'capitalize', }}>
|
||||||
|
<Button style={{ textAlign: "center", textTransform: 'capitalize' }}
|
||||||
|
onClick={() => { window.location = "/search"; }} >
|
||||||
|
See More
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div ref={node} style={{ width: "100%", maxWidth: "100%", margin: "auto", position: "relative", }}>
|
||||||
|
<InstantSearch searchClient={searchClient} indexName="appsearch" onClick={() => {
|
||||||
|
console.log("CLICKED 1")
|
||||||
|
}}>
|
||||||
|
<Configure clickAnalytics />
|
||||||
|
<CustomSearchBox onClick={() => {
|
||||||
|
console.log("Click 2")
|
||||||
|
}}/>
|
||||||
|
{modalView}
|
||||||
|
</InstantSearch>
|
||||||
|
{gettingStartData}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SearchData;
|
||||||
@@ -1,34 +1,42 @@
|
|||||||
import React, {useState, useEffect, useRef} from 'react';
|
import React, { useState, useEffect, useRef } from 'react';
|
||||||
|
|
||||||
import theme from '../theme.jsx';
|
import theme from '../theme.jsx';
|
||||||
import { useNavigate, Link, useParams } from "react-router-dom";
|
import { useNavigate, Link, useParams } from "react-router-dom";
|
||||||
|
import SearchBox from "./SearchData.jsx";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Chip,
|
Chip,
|
||||||
IconButton,
|
IconButton,
|
||||||
TextField,
|
TextField,
|
||||||
InputAdornment,
|
InputAdornment,
|
||||||
List,
|
List,
|
||||||
Card,
|
Card,
|
||||||
ListItem,
|
ListItem,
|
||||||
ListItemAvatar,
|
Button,
|
||||||
ListItemText,
|
Dialog,
|
||||||
Avatar,
|
ListItemAvatar,
|
||||||
|
ListItemText,
|
||||||
|
Avatar,
|
||||||
Typography,
|
Typography,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
|
Divider,
|
||||||
|
DialogTitle,
|
||||||
|
DialogContent,
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
|
import Mousetrap from 'mousetrap';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AvatarGroup,
|
AvatarGroup,
|
||||||
} from "@mui/material"
|
} from "@mui/material"
|
||||||
|
|
||||||
import {Search as SearchIcon, Close as CloseIcon, Folder as FolderIcon, Code as CodeIcon, LibraryBooks as LibraryBooksIcon} from '@mui/icons-material'
|
import { Search as SearchIcon, Close as CloseIcon, Folder as FolderIcon, Code as CodeIcon, LibraryBooks as LibraryBooksIcon } from '@mui/icons-material'
|
||||||
|
import KeyboardCommandKeyIcon from '@mui/icons-material/KeyboardCommandKey';
|
||||||
import algoliasearch from 'algoliasearch/lite';
|
import algoliasearch from 'algoliasearch/lite';
|
||||||
import aa from 'search-insights'
|
import aa from 'search-insights'
|
||||||
import { InstantSearch, Configure, connectSearchBox, connectHits, Index } from 'react-instantsearch-dom';
|
import { InstantSearch, Configure, connectSearchBox, connectHits, Index } from 'react-instantsearch-dom';
|
||||||
//import { InstantSearch, SearchBox, Hits, connectSearchBox, connectHits, Index } from 'react-instantsearch-dom';
|
//import { InstantSearch, SearchBox, Hits, connectSearchBox, connectHits, Index } from 'react-instantsearch-dom';
|
||||||
|
|
||||||
|
import { HotKeys } from 'react-hotkeys';
|
||||||
// https://www.algolia.com/doc/api-reference/widgets/search-box/react/
|
// https://www.algolia.com/doc/api-reference/widgets/search-box/react/
|
||||||
const chipStyle = {
|
const chipStyle = {
|
||||||
backgroundColor: "#3d3f43", height: 30, marginRight: 5, paddingLeft: 5, paddingRight: 5, height: 28, cursor: "pointer", borderColor: "#3d3f43", color: "white",
|
backgroundColor: "#3d3f43", height: 30, marginRight: 5, paddingLeft: 5, paddingRight: 5, height: 28, cursor: "pointer", borderColor: "#3d3f43", color: "white",
|
||||||
@@ -36,623 +44,122 @@ const chipStyle = {
|
|||||||
|
|
||||||
const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240")
|
const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240")
|
||||||
const SearchField = props => {
|
const SearchField = props => {
|
||||||
const { serverside, userdata } = props
|
const { serverside, userdata, isMobile, isLoaded, globalUrl, isHeader, isLoggedIn, small, rounded } = props
|
||||||
|
|
||||||
let navigate = useNavigate();
|
let navigate = useNavigate();
|
||||||
const borderRadius = 3
|
const borderRadius = 3
|
||||||
const node = useRef()
|
const node = useRef()
|
||||||
const [searchOpen, setSearchOpen] = useState(false)
|
const [searchOpen, setSearchOpen] = useState(false)
|
||||||
|
const [modalOpen, setModalOpen] = React.useState(false);
|
||||||
const [oldPath, setOldPath] = useState("")
|
const [oldPath, setOldPath] = useState("")
|
||||||
const [value, setValue] = useState("");
|
const [value, setValue] = useState("");
|
||||||
|
useEffect(() => {
|
||||||
|
Mousetrap.bind(['command+k', 'ctrl+k'], () => {
|
||||||
|
setModalOpen(true);
|
||||||
|
return false; // Prevent the default action
|
||||||
|
});
|
||||||
|
Mousetrap.bind(['esc'], () => {
|
||||||
|
setModalOpen(false);
|
||||||
|
return false; // Prevent the default action
|
||||||
|
});
|
||||||
|
|
||||||
if (serverside === true) {
|
return () => {
|
||||||
return null
|
Mousetrap.unbind(['command+k', 'ctrl+k']);
|
||||||
}
|
|
||||||
|
|
||||||
if (window !== undefined && window.location !== undefined && window.location.pathname === "/search") {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
|
|
||||||
|
|
||||||
if (window.location.pathname !== oldPath) {
|
|
||||||
setSearchOpen(false)
|
|
||||||
setOldPath(window.location.pathname)
|
|
||||||
}
|
|
||||||
|
|
||||||
//useEffect(() => {
|
|
||||||
// if (searchOpen) {
|
|
||||||
// var tarfield = document.getElementById("shuffle_search_field")
|
|
||||||
// tarfield.focus()
|
|
||||||
// }
|
|
||||||
//}, searchOpen)
|
|
||||||
|
|
||||||
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: (
|
|
||||||
<InputAdornment position="end" style={{textAlign: "right", zIndex: 5001, cursor: "pointer", width: 100, }} onMouseOver={(event) => {
|
|
||||||
event.preventDefault()
|
|
||||||
}}>
|
|
||||||
<CloseIcon style={{marginRight: 5,}} onClick={() => {
|
|
||||||
setSearchOpen(false)
|
|
||||||
}} />
|
|
||||||
</InputAdornment>
|
|
||||||
),
|
|
||||||
*/
|
|
||||||
|
|
||||||
return (
|
const fieldWidth = small === true ? 120 : 310
|
||||||
<form id="search_form" noValidate type="searchbox" action="" role="search" style={{margin: "10px 0px 0px 0px", }} onClick={() => {
|
const modalView = (
|
||||||
}}>
|
// console.log("key:", dataValue.key),
|
||||||
<TextField
|
//console.log("value:",dataValue.value),
|
||||||
fullWidth
|
<Dialog
|
||||||
style={{backgroundColor: theme.palette.surfaceColor, borderRadius: borderRadius, minWidth: 403, maxWidth: 403, }}
|
open={modalOpen}
|
||||||
InputProps={{
|
onClose={() => {
|
||||||
style:{
|
setModalOpen(false);
|
||||||
color: "white",
|
}}
|
||||||
fontSize: "1em",
|
PaperProps={{
|
||||||
height: 50,
|
style: {
|
||||||
margin: 0,
|
color: "white",
|
||||||
fontSize: "0.9em",
|
minWidth: 750,
|
||||||
paddingLeft: 10,
|
height: 785,
|
||||||
},
|
borderRadius: 16,
|
||||||
disableUnderline: true,
|
border: "1px solid var(--Container-Stroke, #494949)",
|
||||||
endAdornment: (
|
background: "var(--Container, #000000)",
|
||||||
<InputAdornment position="start">
|
boxShadow: "0px 16px 24px 8px rgba(0, 0, 0, 0.25)",
|
||||||
<SearchIcon style={{marginLeft: 5, color: "#f86a3e",}}/>
|
zIndex: 13000,
|
||||||
|
},
|
||||||
</InputAdornment>
|
}}
|
||||||
),
|
>
|
||||||
}}
|
{isHeader ? <div style={{ display: "flex" }}>
|
||||||
autoComplete='off'
|
<DialogTitle style={{ marginTop: 15, marginLeft: 5, color: "var(--Paragraph-text, #C8C8C8)" }} >Search Shuffle</DialogTitle>
|
||||||
type="search"
|
<Button fullWidth style={{ marginLeft: 470 }} onClick={() => {
|
||||||
color="primary"
|
setModalOpen(false);
|
||||||
placeholder="Find Public Apps, Workflows, Documentation..."
|
}}><CloseIcon /></Button>
|
||||||
value={currentRefinement}
|
</div>
|
||||||
onKeyDown={keyPressHandler}
|
|
||||||
id="shuffle_search_field"
|
|
||||||
onClick={(event) => {
|
|
||||||
if (!searchOpen) {
|
|
||||||
setSearchOpen(true)
|
|
||||||
setTimeout(() => {
|
|
||||||
var tarfield = document.getElementById("shuffle_search_field")
|
|
||||||
//console.log("TARFIELD: ", tarfield)
|
|
||||||
tarfield.focus()
|
|
||||||
}, 100)
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onBlur={(event) => {
|
|
||||||
setTimeout(() => {
|
|
||||||
setSearchOpen(false)
|
|
||||||
}, 500)
|
|
||||||
}}
|
|
||||||
onChange={(event) => {
|
|
||||||
//if (event.currentTarget.value.length > 0 && !searchOpen) {
|
|
||||||
// setSearchOpen(true)
|
|
||||||
//}
|
|
||||||
|
|
||||||
refine(event.currentTarget.value)
|
|
||||||
}}
|
|
||||||
limit={5}
|
|
||||||
/>
|
|
||||||
{/*isSearchStalled ? 'My search is stalled' : ''*/}
|
|
||||||
</form>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const WorkflowHits = ({ hits }) => {
|
|
||||||
const [mouseHoverIndex, setMouseHoverIndex] = useState(0)
|
|
||||||
|
|
||||||
var tmp = searchOpen
|
|
||||||
if (!searchOpen) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const positionInfo = document.activeElement.getBoundingClientRect()
|
|
||||||
const outerlistitemStyle = {
|
|
||||||
width: "100%",
|
|
||||||
overflowX: "hidden",
|
|
||||||
overflowY: "hidden",
|
|
||||||
borderBottom: "1px solid rgba(255,255,255,0.4)",
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hits.length > 4) {
|
|
||||||
hits = hits.slice(0, 4)
|
|
||||||
}
|
|
||||||
|
|
||||||
var type = "workflows"
|
|
||||||
const baseImage = <CodeIcon />
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Card elevation={0} style={{position: "relative", marginLeft: 10, marginRight: 10, position: "absolute", color: "white", zIndex: 1002, backgroundColor: theme.palette.inputColor, width: 405, height: 408, left: 75, boxShadows: "none",}}>
|
|
||||||
<Typography variant="h6" style={{margin: "10px 10px 0px 20px", }}>
|
|
||||||
Workflows
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<List style={{backgroundColor: theme.palette.inputColor, }}>
|
|
||||||
{hits.length === 0 ?
|
|
||||||
<ListItem style={outerlistitemStyle}>
|
|
||||||
<ListItemAvatar onClick={() => console.log(hits)}>
|
|
||||||
<Avatar>
|
|
||||||
<FolderIcon />
|
|
||||||
</Avatar>
|
|
||||||
</ListItemAvatar>
|
|
||||||
<ListItemText
|
|
||||||
primary={"No workflows found."}
|
|
||||||
secondary={"Try a broader search term"}
|
|
||||||
/>
|
|
||||||
</ListItem>
|
|
||||||
:
|
|
||||||
hits.map((hit, index) => {
|
|
||||||
const innerlistitemStyle = {
|
|
||||||
width: positionInfo.width+35,
|
|
||||||
overflowX: "hidden",
|
|
||||||
overflowY: "hidden",
|
|
||||||
borderBottom: "1px solid rgba(255,255,255,0.4)",
|
|
||||||
backgroundColor: mouseHoverIndex === index ? "#1f2023" : "inherit",
|
|
||||||
cursor: "pointer",
|
|
||||||
marginLeft: 5,
|
|
||||||
marginRight: 5,
|
|
||||||
maxHeight: 75,
|
|
||||||
minHeight: 75,
|
|
||||||
maxWidth: 420,
|
|
||||||
minWidth: "100%",
|
|
||||||
}
|
|
||||||
|
|
||||||
const name = hit.name === undefined ?
|
|
||||||
hit.filename.charAt(0).toUpperCase() + hit.filename.slice(1).replaceAll("_", " ") + " - " + hit.title :
|
|
||||||
(hit.name.charAt(0).toUpperCase()+hit.name.slice(1)).replaceAll("_", " ")
|
|
||||||
const secondaryText = hit.description !== undefined && hit.description !== null && hit.description.length > 3 ? hit.description.slice(0, 40)+"..." : ""
|
|
||||||
const appGroup = hit.action_references === undefined || hit.action_references === null ? [] : hit.action_references
|
|
||||||
const avatar = baseImage
|
|
||||||
|
|
||||||
var parsedUrl = isCloud ? `/workflows/${hit.objectID}` : `https://shuffler.io/workflows/${hit.objectID}`
|
|
||||||
|
|
||||||
parsedUrl += `?queryID=${hit.__queryID}`
|
|
||||||
|
|
||||||
// <a rel="noopener noreferrer" href="https://www.algolia.com/" target="_blank" style={{textDecoration: "none", color: "white"}}>
|
|
||||||
return (
|
|
||||||
<Link key={hit.objectID} to={parsedUrl} rel="noopener noreferrer" style={{textDecoration: "none", color: "white",}} onClick={(event) => {
|
|
||||||
//console.log("CLICK")
|
|
||||||
setSearchOpen(true)
|
|
||||||
|
|
||||||
aa('init', {
|
|
||||||
appId: searchClient.appId,
|
|
||||||
apiKey: searchClient.transporter.queryParameters["x-algolia-api-key"]
|
|
||||||
})
|
|
||||||
|
|
||||||
const timestamp = new Date().getTime()
|
|
||||||
aa('sendEvents', [
|
|
||||||
{
|
|
||||||
eventType: 'click',
|
|
||||||
eventName: 'Workflow Clicked',
|
|
||||||
index: 'workflows',
|
|
||||||
objectIDs: [hit.objectID],
|
|
||||||
timestamp: timestamp,
|
|
||||||
queryID: hit.__queryID,
|
|
||||||
positions: [hit.__position],
|
|
||||||
userToken: userdata === undefined || userdata === null || userdata.id === undefined ? "unauthenticated" : userdata.id,
|
|
||||||
}
|
|
||||||
])
|
|
||||||
|
|
||||||
if (!isCloud) {
|
|
||||||
event.preventDefault()
|
|
||||||
window.open(parsedUrl, '_blank');
|
|
||||||
}
|
|
||||||
}}>
|
|
||||||
<ListItem key={hit.objectID} style={innerlistitemStyle} onMouseOver={() => {
|
|
||||||
setMouseHoverIndex(index)
|
|
||||||
}}>
|
|
||||||
<ListItemAvatar>
|
|
||||||
{avatar}
|
|
||||||
</ListItemAvatar>
|
|
||||||
<div style={{}}>
|
|
||||||
<ListItemText
|
|
||||||
primary={name}
|
|
||||||
/>
|
|
||||||
<AvatarGroup max={10} style={{flexDirection: "row", padding: 0, margin: 0, itemAlign: "left", textAlign: "left",}}>
|
|
||||||
{appGroup.map((app, index) => {
|
|
||||||
// Putting all this in secondary of ListItemText looked weird.
|
|
||||||
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>
|
|
||||||
{/*
|
|
||||||
<ListItemSecondaryAction>
|
|
||||||
<IconButton edge="end" aria-label="delete">
|
|
||||||
<DeleteIcon />
|
|
||||||
</IconButton>
|
|
||||||
</ListItemSecondaryAction>
|
|
||||||
*/}
|
|
||||||
</ListItem>
|
|
||||||
</Link>
|
|
||||||
)})
|
|
||||||
}
|
|
||||||
</List>
|
|
||||||
{/*
|
|
||||||
<span style={{display: "flex", textAlign: "left", float: "left", position: "absolute", left: 15, bottom: 10, }}>
|
|
||||||
<Link to="/search" style={{textDecoration: "none", color: "#f85a3e"}}>
|
|
||||||
<Typography variant="body2" style={{}}>
|
|
||||||
See all workflows
|
|
||||||
</Typography>
|
|
||||||
</Link>
|
|
||||||
</span>
|
|
||||||
*/}
|
|
||||||
</Card>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const AppHits = ({ hits }) => {
|
|
||||||
const [mouseHoverIndex, setMouseHoverIndex] = useState(0)
|
|
||||||
|
|
||||||
var tmp = searchOpen
|
|
||||||
if (!searchOpen) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
const positionInfo = document.activeElement.getBoundingClientRect()
|
|
||||||
const outerlistitemStyle = {
|
|
||||||
width: "100%",
|
|
||||||
overflowX: "hidden",
|
|
||||||
overflowY: "hidden",
|
|
||||||
borderBottom: "1px solid rgba(255,255,255,0.4)",
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hits.length > 4) {
|
|
||||||
hits = hits.slice(0, 4)
|
|
||||||
}
|
|
||||||
|
|
||||||
var type = "app"
|
|
||||||
const baseImage = <LibraryBooksIcon />
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Card elevation={0} style={{position: "relative", marginLeft: 10, marginRight: 10, position: "absolute", color: "white", zIndex: 1001, backgroundColor: theme.palette.inputColor, width: 1155, height: 408, left: -305, boxShadows: "none",}}>
|
|
||||||
<IconButton style={{zIndex: 5000, position: "absolute", right: 14, color: "grey"}} onClick={() => {
|
|
||||||
setSearchOpen(false)
|
|
||||||
}}>
|
|
||||||
<CloseIcon />
|
|
||||||
</IconButton>
|
|
||||||
<Typography variant="h6" style={{margin: "10px 10px 0px 20px", }}>
|
|
||||||
Apps
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<List style={{backgroundColor: theme.palette.inputColor, }}>
|
|
||||||
{hits.length === 0 ?
|
|
||||||
<ListItem style={outerlistitemStyle}>
|
|
||||||
<ListItemAvatar onClick={() => console.log(hits)}>
|
|
||||||
<Avatar>
|
|
||||||
<FolderIcon />
|
|
||||||
</Avatar>
|
|
||||||
</ListItemAvatar>
|
|
||||||
<ListItemText
|
|
||||||
primary={"No apps found."}
|
|
||||||
secondary={"Try a broader search term"}
|
|
||||||
/>
|
|
||||||
</ListItem>
|
|
||||||
:
|
|
||||||
hits.map((hit, index) => {
|
|
||||||
const innerlistitemStyle = {
|
|
||||||
width: positionInfo.width+35,
|
|
||||||
overflowX: "hidden",
|
|
||||||
overflowY: "hidden",
|
|
||||||
borderBottom: "1px solid rgba(255,255,255,0.4)",
|
|
||||||
backgroundColor: mouseHoverIndex === index ? "#1f2023" : "inherit",
|
|
||||||
cursor: "pointer",
|
|
||||||
marginLeft: 5,
|
|
||||||
marginRight: 5,
|
|
||||||
maxHeight: 75,
|
|
||||||
minHeight: 75,
|
|
||||||
maxWidth: 420,
|
|
||||||
minWidth: "100%",
|
|
||||||
}
|
|
||||||
|
|
||||||
const name = hit.name === undefined ?
|
|
||||||
hit.filename.charAt(0).toUpperCase() + hit.filename.slice(1).replaceAll("_", " ") + " - " + hit.title :
|
|
||||||
(hit.name.charAt(0).toUpperCase()+hit.name.slice(1)).replaceAll("_", " ")
|
|
||||||
|
|
||||||
var secondaryText = hit.data !== undefined ? hit.data.slice(0, 40)+"..." : ""
|
|
||||||
const avatar = hit.image_url === undefined ?
|
|
||||||
baseImage
|
|
||||||
:
|
|
||||||
<Avatar
|
|
||||||
src={hit.image_url}
|
|
||||||
variant="rounded"
|
|
||||||
/>
|
|
||||||
|
|
||||||
//console.log(hit)
|
|
||||||
if (hit.categories !== undefined && hit.categories !== null && hit.categories.length > 0) {
|
|
||||||
secondaryText = hit.categories.slice(0,3).map((data, index) => {
|
|
||||||
if (index === 0) {
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
|
|
||||||
return ", "+data
|
|
||||||
|
|
||||||
/*
|
|
||||||
<Chip
|
|
||||||
key={index}
|
|
||||||
style={chipStyle}
|
|
||||||
label={data}
|
|
||||||
onClick={() => {
|
|
||||||
//handleChipClick
|
|
||||||
}}
|
|
||||||
variant="outlined"
|
|
||||||
color="primary"
|
|
||||||
/>
|
|
||||||
*/
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
var parsedUrl = isCloud ? `/apps/${hit.objectID}` : `https://shuffler.io/apps/${hit.objectID}`
|
|
||||||
parsedUrl += `?queryID=${hit.__queryID}`
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Link key={hit.objectID} to={parsedUrl} style={{textDecoration: "none", color: "white",}} onClick={(event) => {
|
|
||||||
console.log("CLICK")
|
|
||||||
setSearchOpen(true)
|
|
||||||
|
|
||||||
aa('init', {
|
|
||||||
appId: searchClient.appId,
|
|
||||||
apiKey: searchClient.transporter.queryParameters["x-algolia-api-key"]
|
|
||||||
})
|
|
||||||
|
|
||||||
const timestamp = new Date().getTime()
|
|
||||||
aa('sendEvents', [
|
|
||||||
{
|
|
||||||
eventType: 'click',
|
|
||||||
eventName: 'App Clicked',
|
|
||||||
index: 'appsearch',
|
|
||||||
objectIDs: [hit.objectID],
|
|
||||||
timestamp: timestamp,
|
|
||||||
queryID: hit.__queryID,
|
|
||||||
positions: [hit.__position],
|
|
||||||
userToken: userdata === undefined || userdata === null || userdata.id === undefined ? "unauthenticated" : userdata.id,
|
|
||||||
}
|
|
||||||
])
|
|
||||||
|
|
||||||
if (!isCloud) {
|
|
||||||
event.preventDefault()
|
|
||||||
window.open(parsedUrl, '_blank');
|
|
||||||
}
|
|
||||||
}}>
|
|
||||||
<ListItem key={hit.objectID} style={innerlistitemStyle} onMouseOver={() => {
|
|
||||||
setMouseHoverIndex(index)
|
|
||||||
}}>
|
|
||||||
<ListItemAvatar>
|
|
||||||
{avatar}
|
|
||||||
</ListItemAvatar>
|
|
||||||
<ListItemText
|
|
||||||
primary={name}
|
|
||||||
secondary={secondaryText}
|
|
||||||
/>
|
|
||||||
{/*
|
|
||||||
<ListItemSecondaryAction>
|
|
||||||
<IconButton edge="end" aria-label="delete">
|
|
||||||
<DeleteIcon />
|
|
||||||
</IconButton>
|
|
||||||
</ListItemSecondaryAction>
|
|
||||||
*/}
|
|
||||||
</ListItem>
|
|
||||||
</Link>
|
|
||||||
)})
|
|
||||||
}
|
|
||||||
</List>
|
|
||||||
<span style={{display: "flex", textAlign: "left", float: "left", position: "absolute", left: 15, bottom: 10, }}>
|
|
||||||
<Link to="/search" style={{textDecoration: "none", color: "#f85a3e"}}>
|
|
||||||
<Typography variant="body1" style={{}}>
|
|
||||||
See more
|
|
||||||
</Typography>
|
|
||||||
</Link>
|
|
||||||
</span>
|
|
||||||
</Card>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const DocHits = ({ hits }) => {
|
|
||||||
const [mouseHoverIndex, setMouseHoverIndex] = useState(0)
|
|
||||||
|
|
||||||
var tmp = searchOpen
|
|
||||||
if (!searchOpen) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const positionInfo = document.activeElement.getBoundingClientRect()
|
|
||||||
const outerlistitemStyle = {
|
|
||||||
width: "100%",
|
|
||||||
overflowX: "hidden",
|
|
||||||
overflowY: "hidden",
|
|
||||||
borderBottom: "1px solid rgba(255,255,255,0.4)",
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hits.length > 4) {
|
|
||||||
hits = hits.slice(0, 4)
|
|
||||||
}
|
|
||||||
|
|
||||||
const type = "documentation"
|
|
||||||
const baseImage = <LibraryBooksIcon />
|
|
||||||
|
|
||||||
//console.log(type, hits.length, hits)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Card elevation={0} style={{position: "relative", marginLeft: 10, marginRight: 10, position: "absolute", color: "white", zIndex: 1002, backgroundColor: theme.palette.inputColor, width: 405, height: 408, left: 470, boxShadows: "none",}}>
|
|
||||||
<IconButton style={{zIndex: 5000, position: "absolute", right: 14, color: "grey"}} onClick={() => {
|
|
||||||
setSearchOpen(false)
|
|
||||||
}}>
|
|
||||||
<CloseIcon />
|
|
||||||
</IconButton>
|
|
||||||
<Typography variant="h6" style={{margin: "10px 10px 0px 20px", }}>
|
|
||||||
Documentation
|
|
||||||
</Typography>
|
|
||||||
{/*
|
|
||||||
<IconButton edge="end" aria-label="delete" style={{position: "absolute", top: 5, right: 15,}} onClick={() => {
|
|
||||||
setSearchOpen(false)
|
|
||||||
}}>
|
|
||||||
<DeleteIcon />
|
|
||||||
</IconButton>
|
|
||||||
*/}
|
|
||||||
<List style={{backgroundColor: theme.palette.inputColor, }}>
|
|
||||||
{hits.length === 0 ?
|
|
||||||
<ListItem style={outerlistitemStyle}>
|
|
||||||
<ListItemAvatar onClick={() => console.log(hits)}>
|
|
||||||
<Avatar>
|
|
||||||
<FolderIcon />
|
|
||||||
</Avatar>
|
|
||||||
</ListItemAvatar>
|
|
||||||
<ListItemText
|
|
||||||
primary={"No documentation."}
|
|
||||||
secondary={"Try a broader search term"}
|
|
||||||
/>
|
|
||||||
</ListItem>
|
|
||||||
:
|
|
||||||
hits.map((hit, index) => {
|
|
||||||
const innerlistitemStyle = {
|
|
||||||
width: positionInfo.width+35,
|
|
||||||
overflowX: "hidden",
|
|
||||||
overflowY: "hidden",
|
|
||||||
borderBottom: "1px solid rgba(255,255,255,0.4)",
|
|
||||||
backgroundColor: mouseHoverIndex === index ? "#1f2023" : "inherit",
|
|
||||||
cursor: "pointer",
|
|
||||||
marginLeft: 5,
|
|
||||||
marginRight: 5,
|
|
||||||
maxHeight: 75,
|
|
||||||
minHeight: 75,
|
|
||||||
maxWidth: 420,
|
|
||||||
minWidth: "100%",
|
|
||||||
}
|
|
||||||
|
|
||||||
var name = hit.name === undefined ?
|
|
||||||
hit.filename.charAt(0).toUpperCase() + hit.filename.slice(1).replaceAll("_", " ") + " - " + hit.title
|
|
||||||
:
|
|
||||||
(hit.name.charAt(0).toUpperCase()+hit.name.slice(1)).replaceAll("_", " ")
|
|
||||||
|
|
||||||
if (name.length > 30) {
|
|
||||||
name = name.slice(0, 30)+"..."
|
|
||||||
}
|
|
||||||
const secondaryText = hit.data !== undefined ? hit.data.slice(0, 40)+"..." : ""
|
|
||||||
const avatar = hit.image_url === undefined ?
|
|
||||||
baseImage
|
|
||||||
:
|
|
||||||
<Avatar
|
|
||||||
src={hit.image_url}
|
|
||||||
variant="rounded"
|
|
||||||
/>
|
|
||||||
|
|
||||||
var parsedUrl = hit.urlpath !== undefined ? hit.urlpath : ""
|
|
||||||
parsedUrl += `?queryID=${hit.__queryID}`
|
|
||||||
if (parsedUrl.includes("/apps/")) {
|
|
||||||
const extraHash = hit.url_hash === undefined ? "" : `#${hit.url_hash}`
|
|
||||||
|
|
||||||
parsedUrl = `/apps/${hit.filename}`
|
|
||||||
parsedUrl += `?tab=docs&queryID=${hit.__queryID}${extraHash}`
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Link key={hit.objectID} to={parsedUrl} style={{textDecoration: "none", color: "white",}} onClick={(event) => {
|
|
||||||
aa('init', {
|
|
||||||
appId: searchClient.appId,
|
|
||||||
apiKey: searchClient.transporter.queryParameters["x-algolia-api-key"]
|
|
||||||
})
|
|
||||||
|
|
||||||
const timestamp = new Date().getTime()
|
|
||||||
aa('sendEvents', [
|
|
||||||
{
|
|
||||||
eventType: 'click',
|
|
||||||
eventName: 'Document Clicked',
|
|
||||||
index: 'documentation',
|
|
||||||
objectIDs: [hit.objectID],
|
|
||||||
timestamp: timestamp,
|
|
||||||
queryID: hit.__queryID,
|
|
||||||
positions: [hit.__position],
|
|
||||||
userToken: userdata === undefined || userdata === null || userdata.id === undefined ? "unauthenticated" : userdata.id,
|
|
||||||
}
|
|
||||||
])
|
|
||||||
|
|
||||||
console.log("CLICK")
|
|
||||||
setSearchOpen(true)
|
|
||||||
}}>
|
|
||||||
<ListItem key={hit.objectID} style={innerlistitemStyle} onMouseOver={() => {
|
|
||||||
setMouseHoverIndex(index)
|
|
||||||
}}>
|
|
||||||
<ListItemAvatar>
|
|
||||||
{avatar}
|
|
||||||
</ListItemAvatar>
|
|
||||||
<ListItemText
|
|
||||||
primary={name}
|
|
||||||
secondary={secondaryText}
|
|
||||||
/>
|
|
||||||
{/*
|
|
||||||
<ListItemSecondaryAction>
|
|
||||||
<IconButton edge="end" aria-label="delete">
|
|
||||||
<DeleteIcon />
|
|
||||||
</IconButton>
|
|
||||||
</ListItemSecondaryAction>
|
|
||||||
*/}
|
|
||||||
</ListItem>
|
|
||||||
</Link>
|
|
||||||
)})
|
|
||||||
}
|
|
||||||
</List>
|
|
||||||
{type === "documentation" ?
|
|
||||||
<span style={{display: "flex", textAlign: "right", position: "absolute", right: 15, bottom: 10,}}>
|
|
||||||
<Typography variant="body2" style={{}}>
|
|
||||||
Search by
|
|
||||||
</Typography>
|
|
||||||
<a rel="noopener noreferrer" href="https://www.algolia.com/" target="_blank" style={{textDecoration: "none", color: "white"}}>
|
|
||||||
<img src={"/images/logo-algolia-nebula-blue-full.svg"} alt="Algolia logo" style={{height: 17, marginLeft: 5, marginTop: 3,}} />
|
|
||||||
</a>
|
|
||||||
</span>
|
|
||||||
: null}
|
: null}
|
||||||
</Card>
|
<DialogContent style={{}}>
|
||||||
)
|
<SearchBox setModalOpen={setModalOpen} modalOpen={modalOpen} serverside={serverside} userdata={userdata} />
|
||||||
}
|
</DialogContent>
|
||||||
|
<Divider style={{overflow: "hidden"}}/>
|
||||||
const CustomSearchBox = connectSearchBox(SearchBox)
|
<span style={{display:"flex", width:"100%"}}>
|
||||||
const CustomAppHits = connectHits(AppHits)
|
<div style={{display: "flex", marginTop: 6, marginBottom: 6, marginRight: 100, marginLeft: 20, alignItems: "center"}}>
|
||||||
const CustomWorkflowHits = connectHits(WorkflowHits)
|
{/* <Typography variant="body2" style={{ fontSize: 16, fontWidth: 550, color: "var(--Paragraph-text, #C8C8C8)"}}>
|
||||||
const CustomDocHits = connectHits(DocHits)
|
Discord
|
||||||
|
</Typography> */}
|
||||||
|
{/*
|
||||||
|
<a rel="noopener noreferrer" href="https://discord.com/invite/B2CBzUm" target="_blank" style={{ textDecoration: "none", color: "white" }}>
|
||||||
|
<img src={"/images/social/discode.svg"} alt="Algolia logo" style={{ height: 22, marginLeft: 5, marginTop: 3, }} />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div style={{ display: "flex", marginTop: 6, marginBottom: 6, marginLeft: 440 }}>
|
||||||
|
<Typography variant="body2" style={{color: "var(--Paragraph-text, #C8C8C8)"}}>
|
||||||
|
Search by
|
||||||
|
</Typography>
|
||||||
|
<a rel="noopener noreferrer" href="https://www.algolia.com/" target="_blank" style={{ textDecoration: "none", color: "white" }}>
|
||||||
|
<img src={"/images/logo-algolia-nebula-blue-full.svg"} alt="Algolia logo" style={{ height: 17, marginLeft: 5, marginTop: 3, }} />
|
||||||
|
</a>
|
||||||
|
*/}
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={node} style={{width: "100%", maxWidth: 425, margin: "auto", position: "relative", }}>
|
<div style={{ marginTop: "auto", marginLeft: !isLoggedIn ? 0 : 130 }}>
|
||||||
<InstantSearch searchClient={searchClient} indexName="appsearch" onClick={() => {
|
{modalView}
|
||||||
console.log("CLICKED")
|
<TextField
|
||||||
}}>
|
style={{ backgroundColor: "#212121", height: 48, borderRadius: rounded === true ? 25 : theme.palette.borderRadius, minWidth: fieldWidth, maxWidth: fieldWidth, }}
|
||||||
<Configure clickAnalytics />
|
InputProps={{
|
||||||
<CustomSearchBox />
|
style: {
|
||||||
<Index indexName="appsearch">
|
color: "white",
|
||||||
<CustomAppHits />
|
fontSize: "1em",
|
||||||
</Index>
|
height: 50,
|
||||||
<Index indexName="documentation">
|
margin: 7,
|
||||||
<CustomDocHits />
|
fontSize: "0.9em",
|
||||||
</Index>
|
},
|
||||||
<Index indexName="workflows">
|
readOnly: true,
|
||||||
<CustomWorkflowHits />
|
disableUnderline: true,
|
||||||
</Index>
|
startAdornment: (
|
||||||
</InstantSearch>
|
<InputAdornment position="start">
|
||||||
|
<SearchIcon style={{ color: "#f86a3e", }} />
|
||||||
|
</InputAdornment>
|
||||||
|
),
|
||||||
|
endAdornment: (
|
||||||
|
<Button position="end" style={{ marginLeft: 10, width: 15 }}>
|
||||||
|
<KeyboardCommandKeyIcon style={{ width: 15 }} />
|
||||||
|
+ K
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
variant="standard"
|
||||||
|
autoComplete='off'
|
||||||
|
color="primary"
|
||||||
|
placeholder="Search Apps, Workflows, Docs..."
|
||||||
|
onClick={(event) => {
|
||||||
|
setModalOpen(true)
|
||||||
|
}}
|
||||||
|
limit={5}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import {
|
|||||||
Check as CheckIcon,
|
Check as CheckIcon,
|
||||||
TrendingFlat as TrendingFlatIcon,
|
TrendingFlat as TrendingFlatIcon,
|
||||||
Close as CloseIcon,
|
Close as CloseIcon,
|
||||||
|
East as EastIcon,
|
||||||
} from '@mui/icons-material';
|
} from '@mui/icons-material';
|
||||||
|
|
||||||
import WorkflowTemplatePopup2 from "./WorkflowTemplatePopup.jsx";
|
import WorkflowTemplatePopup2 from "./WorkflowTemplatePopup.jsx";
|
||||||
@@ -36,6 +37,7 @@ const WorkflowTemplatePopup = (props) => {
|
|||||||
const [errorMessage, setErrorMessage] = useState("");
|
const [errorMessage, setErrorMessage] = useState("");
|
||||||
const [workflowLoading, setWorkflowLoading] = useState(false);
|
const [workflowLoading, setWorkflowLoading] = useState(false);
|
||||||
const [workflow, setWorkflow] = useState({});
|
const [workflow, setWorkflow] = useState({});
|
||||||
|
const [showLoginButton, setShowLoginButton] = useState(false);
|
||||||
const [appAuthentication, setAppAuthentication] = React.useState(undefined);
|
const [appAuthentication, setAppAuthentication] = React.useState(undefined);
|
||||||
|
|
||||||
const [missingSource, setMissingSource] = React.useState(undefined)
|
const [missingSource, setMissingSource] = React.useState(undefined)
|
||||||
@@ -119,7 +121,8 @@ const WorkflowTemplatePopup = (props) => {
|
|||||||
const loadAppAuth = () => {
|
const loadAppAuth = () => {
|
||||||
// Check if it exists, and has keys
|
// Check if it exists, and has keys
|
||||||
if (userdata === undefined || userdata === null || Object.keys(userdata).length === 0) {
|
if (userdata === undefined || userdata === null || Object.keys(userdata).length === 0) {
|
||||||
setErrorMessage("You need to be logged in to try usecases. Redirecting in 5 seconds...")
|
setErrorMessage("You need to be logged in to try the pre-built Workflow Templates.")
|
||||||
|
setShowLoginButton(true)
|
||||||
|
|
||||||
// Send the user to the login screen after 3 seconds
|
// Send the user to the login screen after 3 seconds
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@@ -350,12 +353,35 @@ const WorkflowTemplatePopup = (props) => {
|
|||||||
<Typography variant="h6" style={{marginTop: 75, }}>
|
<Typography variant="h6" style={{marginTop: 75, }}>
|
||||||
{errorMessage !== "" ? errorMessage : ""}
|
{errorMessage !== "" ? errorMessage : ""}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
{showLoginButton ?
|
||||||
|
<Link to="/register?message=Please login to create workflows&view=usecases"
|
||||||
|
style={{
|
||||||
|
textDecoration: 'none',
|
||||||
|
marginBottom: 50,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
fontSize: 18,
|
||||||
|
color: "rgba(255, 132, 68, 1)",
|
||||||
|
marginTop: 32,
|
||||||
|
justifyContent: isMobile ? "center" : null,
|
||||||
|
fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)",
|
||||||
|
fontWeight: 550,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Sign up
|
||||||
|
<EastIcon style={{ marginTop: 3, marginLeft: 7 }} />
|
||||||
|
</Typography>
|
||||||
|
</Link>
|
||||||
|
: null}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
{isLoggedIn && (missingSource !== undefined || missingDestination !== undefined) ?
|
{(missingSource !== undefined || missingDestination !== undefined) ?
|
||||||
<Typography variant="body1" style={{marginTop: 75, }}>
|
<Typography variant="body1" style={{marginTop: 75, marginBottom: 10, }}>
|
||||||
{"The following app category is required generate this workflow: "}
|
{"Find relevevant Apps for this Usecase"}
|
||||||
</Typography>
|
</Typography>
|
||||||
: null}
|
: null}
|
||||||
|
|
||||||
@@ -502,7 +528,7 @@ const WorkflowTemplatePopup = (props) => {
|
|||||||
<Typography variant="body1" style={{ marginTop: parsedDescription.length === 0 ? 10 : 0, fontSize: isMobile ? 13 : 16,fontWeight: isHomePage? 600 : null,textTransform: 'capitalize', color: isHomePage ? "var(--White-text, #F1F1F1)" :"rgba(241, 241, 241, 1)"}} >
|
<Typography variant="body1" style={{ marginTop: parsedDescription.length === 0 ? 10 : 0, fontSize: isMobile ? 13 : 16,fontWeight: isHomePage? 600 : null,textTransform: 'capitalize', color: isHomePage ? "var(--White-text, #F1F1F1)" :"rgba(241, 241, 241, 1)"}} >
|
||||||
{parsedTitle}
|
{parsedTitle}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="body2" color="textSecondary" style={{ fontSize: isMobile ? 10: 16, fontWeight: isHomePage ? 400 : null, textTransform: 'capitalize', marginTop: 0, overflow: "hidden",}} color="rgba(158, 158, 158, 1)">
|
<Typography variant="body2" color="textSecondary" style={{ fontSize: isMobile ? 10: 16, fontWeight: isHomePage ? 400 : null, textTransform: 'capitalize', marginTop: 0, overflow: "hidden", maxHeight: 21, overflow: "hidden",}} color="rgba(158, 158, 158, 1)">
|
||||||
{parsedDescription}
|
{parsedDescription}
|
||||||
</Typography>
|
</Typography>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -291,8 +291,6 @@ const Admin = (props) => {
|
|||||||
//const alert = useAlert();
|
//const alert = useAlert();
|
||||||
const handleStatusChange = (event) => {
|
const handleStatusChange = (event) => {
|
||||||
const { value } = event.target;
|
const { value } = event.target;
|
||||||
console.log("value: ", value)
|
|
||||||
|
|
||||||
setSelectedStatus(value);
|
setSelectedStatus(value);
|
||||||
|
|
||||||
|
|
||||||
@@ -331,7 +329,7 @@ const Admin = (props) => {
|
|||||||
var your_apps = "- Connecting "
|
var your_apps = "- Connecting "
|
||||||
|
|
||||||
var subject_add = 0
|
var subject_add = 0
|
||||||
var subject = "Want to automate "
|
var subject = "POC to automate "
|
||||||
|
|
||||||
if (org.security_framework !== undefined && org.security_framework !== null) {
|
if (org.security_framework !== undefined && org.security_framework !== null) {
|
||||||
if (org.security_framework.cases.name !== undefined && org.security_framework.cases.name !== null && org.security_framework.cases.name !== "") {
|
if (org.security_framework.cases.name !== undefined && org.security_framework.cases.name !== null && org.security_framework.cases.name !== "") {
|
||||||
@@ -400,7 +398,7 @@ const Admin = (props) => {
|
|||||||
|
|
||||||
|
|
||||||
// Remove comma
|
// Remove comma
|
||||||
subject += "?"
|
//subject += "?"
|
||||||
your_apps = your_apps.substring(0, your_apps.length - 2)
|
your_apps = your_apps.substring(0, your_apps.length - 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -432,12 +430,25 @@ const Admin = (props) => {
|
|||||||
var admins = ""
|
var admins = ""
|
||||||
|
|
||||||
// Loop users
|
// Loop users
|
||||||
|
var lastLogin = 0
|
||||||
for (var i = 0; i < users.length; i++) {
|
for (var i = 0; i < users.length; i++) {
|
||||||
|
if (users[i].username.includes("shuffler")) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
if (users[i].role === "admin") {
|
if (users[i].role === "admin") {
|
||||||
admins += users[i].username + ","
|
admins += users[i].username + ","
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const data = users[i]
|
||||||
|
for (var i = 0; i < data.login_info.length; i++) {
|
||||||
|
if (data.login_info[i].timestamp > lastLogin) {
|
||||||
|
lastLogin = data.login_info[i].timestamp
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Remove last comma
|
// Remove last comma
|
||||||
admins = admins.substring(0, admins.length - 1)
|
admins = admins.substring(0, admins.length - 1)
|
||||||
|
|
||||||
@@ -452,15 +463,24 @@ const Admin = (props) => {
|
|||||||
// Get drift username from userdata.username before @ in email
|
// Get drift username from userdata.username before @ in email
|
||||||
const username = userdata.username.substring(0, userdata.username.indexOf("@"))
|
const username = userdata.username.substring(0, userdata.username.indexOf("@"))
|
||||||
|
|
||||||
var body = `Hey,%0D%0A%0D%0AI saw you trying to use Shuffle, and thought we may be able to help. Right now, it looks like you have ${workflow_amount} workflows made, but it still doesn't look like you are getting the most out of Shuffle. If you're interested, I'd love to set up a quick call to see if we can help you get more out of Shuffle. %0D%0A%0D%0A
|
// Check if timestamp is more than 2 weeks ago and add "a while back" to the message
|
||||||
|
const timeComparison = 1209600
|
||||||
|
const extra_timestamp_text = lastLogin === 0 ? 0 : (Date.now()/1000 - lastLogin) > timeComparison ? " a while back" : ""
|
||||||
|
console.log("LAST LOGIN: " + lastLogin, extra_timestamp_text)
|
||||||
|
|
||||||
|
// Check if cloud sync is active, and if so, add a message about it
|
||||||
|
const cloudSyncInfo = selectedOrganization.cloud_sync === true ? "- Scale your onprem installation" : ""
|
||||||
|
|
||||||
|
var body = `Hey,%0D%0A%0D%0AI noticed you tried to use Shuffle${extra_timestamp_text}, and thought you may be interested in a POC. It looks like you have ${workflow_amount} workflows made, but it still doesn't look like you are getting what you wanted out of Shuffle. If you're interested, I'd love to set up a quick call to see if we can help you get more out of Shuffle. %0D%0A%0D%0A
|
||||||
|
|
||||||
Some of the things we can help with:%0D%0A
|
Some of the things we can help with:%0D%0A
|
||||||
${your_apps}
|
${your_apps}
|
||||||
- Configuring and authenticating your apps%0D%0A
|
- Configuring and authenticating your apps%0D%0A
|
||||||
${usecases}
|
${usecases}
|
||||||
- Creating special usecases and apps%0D%0A%0D%0A
|
- Multi-Tenancy and creating special usecases%0D%0A
|
||||||
|
${cloudSyncInfo}%0D%0A
|
||||||
|
|
||||||
Let me know if you're interested, or set up a call here: https://drift.me/${username}`
|
If you're interested, please let me know a time that works for you, or set up a call here: https://drift.me/${username}`
|
||||||
|
|
||||||
return `mailto:${admins}?bcc=frikky@shuffler.io,binu@shuffler.io&subject=${subject}&body=${body}`
|
return `mailto:${admins}?bcc=frikky@shuffler.io,binu@shuffler.io&subject=${subject}&body=${body}`
|
||||||
}
|
}
|
||||||
@@ -984,6 +1004,10 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user
|
|||||||
leads.push("old customer")
|
leads.push("old customer")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (responseJson.lead_info.old_lead) {
|
||||||
|
leads.push("old lead")
|
||||||
|
}
|
||||||
|
|
||||||
if (responseJson.lead_info.tech_partner) {
|
if (responseJson.lead_info.tech_partner) {
|
||||||
leads.push("tech partner")
|
leads.push("tech partner")
|
||||||
}
|
}
|
||||||
@@ -2428,7 +2452,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user
|
|||||||
renderValue={(selected) => selected.join(', ')}
|
renderValue={(selected) => selected.join(', ')}
|
||||||
MenuProps={MenuProps}
|
MenuProps={MenuProps}
|
||||||
>
|
>
|
||||||
{["contacted", "lead", "demo done", "pov", "customer", "open source", "student", "internal", "old customer", "creator", "tech partner"].map((name) => (
|
{["contacted", "lead", "demo done", "pov", "customer", "open source", "student", "internal", "creator", "tech partner", "old customer", "old lead", ].map((name) => (
|
||||||
<MenuItem key={name} value={name}>
|
<MenuItem key={name} value={name}>
|
||||||
<Checkbox checked={selectedStatus.indexOf(name) > -1} />
|
<Checkbox checked={selectedStatus.indexOf(name) > -1} />
|
||||||
<ListItemText primary={name} />
|
<ListItemText primary={name} />
|
||||||
@@ -3124,7 +3148,6 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (loginInfo > 0) {
|
if (loginInfo > 0) {
|
||||||
console.log("SETTING LAST LOGIN: " + loginInfo)
|
|
||||||
lastLogin = new Date(loginInfo * 1000).toISOString().slice(0, 10) + " (" + data.login_info.length + ")"
|
lastLogin = new Date(loginInfo * 1000).toISOString().slice(0, 10) + " (" + data.login_info.length + ")"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4382,7 +4405,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user
|
|||||||
<Tab
|
<Tab
|
||||||
label=<span>
|
label=<span>
|
||||||
<LockIcon style={iconStyle} />
|
<LockIcon style={iconStyle} />
|
||||||
App Authentication
|
App Auth
|
||||||
</span>
|
</span>
|
||||||
/>
|
/>
|
||||||
<Tab
|
<Tab
|
||||||
|
|||||||
@@ -108,10 +108,10 @@ import {
|
|||||||
Preview as PreviewIcon,
|
Preview as PreviewIcon,
|
||||||
ContentCopy as ContentCopyIcon,
|
ContentCopy as ContentCopyIcon,
|
||||||
Circle as CircleIcon,
|
Circle as CircleIcon,
|
||||||
SquareFoot as SquareFootIcon,
|
SquareFoot as SquareFootIcon,
|
||||||
AutoFixHigh as AutoFixHighIcon,
|
AutoFixHigh as AutoFixHighIcon,
|
||||||
|
|
||||||
Polyline as PolylineIcon,
|
Polyline as PolylineIcon,
|
||||||
|
QueryStats as QueryStatsIcon,
|
||||||
} from "@mui/icons-material";
|
} from "@mui/icons-material";
|
||||||
|
|
||||||
|
|
||||||
@@ -591,7 +591,7 @@ const AngularWorkflow = (defaultprops) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log("In useeffect for loopRunning: ", loopRunning)
|
//console.log("In useeffect for loopRunning: ", loopRunning)
|
||||||
if (loopRunning) {
|
if (loopRunning) {
|
||||||
const intervalId = setInterval(() => {
|
const intervalId = setInterval(() => {
|
||||||
if (!loopRunning) {
|
if (!loopRunning) {
|
||||||
@@ -1244,7 +1244,7 @@ const AngularWorkflow = (defaultprops) => {
|
|||||||
// 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)
|
||||||
} else {
|
} else {
|
||||||
if (responseJson.status === "ABORTED" || responseJson.status === "STOPPED" || responseJson.status === "FAILURE" || responseJson.status === "WAITING") {
|
if (responseJson.status === "ABORTED" || responseJson.status === "STOPPED" || responseJson.status === "FAILURE" || responseJson.status === "WAITING") {
|
||||||
@@ -1713,7 +1713,9 @@ const AngularWorkflow = (defaultprops) => {
|
|||||||
})
|
})
|
||||||
.then((responseJson) => {
|
.then((responseJson) => {
|
||||||
if (!responseJson.success) {
|
if (!responseJson.success) {
|
||||||
toast("Failed to start: " + responseJson.reason);
|
//toast("Failed to start: " + responseJson.reason);
|
||||||
|
toast(responseJson.reason);
|
||||||
|
//toast.error(responseJson.reason);
|
||||||
setExecutionRunning(false);
|
setExecutionRunning(false);
|
||||||
setExecutionRequestStarted(false);
|
setExecutionRequestStarted(false);
|
||||||
stop();
|
stop();
|
||||||
@@ -4593,7 +4595,6 @@ const AngularWorkflow = (defaultprops) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("NODE: ", nodedata)
|
|
||||||
if (nodedata.decorator !== true && nodedata.attachedTo === undefined) {
|
if (nodedata.decorator !== true && nodedata.attachedTo === undefined) {
|
||||||
var newdata = JSON.parse(JSON.stringify(nodedata))
|
var newdata = JSON.parse(JSON.stringify(nodedata))
|
||||||
newdata.large_image = ""
|
newdata.large_image = ""
|
||||||
@@ -14591,7 +14592,7 @@ const AngularWorkflow = (defaultprops) => {
|
|||||||
onClose={() => {
|
onClose={() => {
|
||||||
setExecutionModalOpen(false)
|
setExecutionModalOpen(false)
|
||||||
}}
|
}}
|
||||||
style={{ resize: "both", overflow: "auto", zIndex: 10005 }}
|
style={{ resize: "both", overflow: "auto", }}
|
||||||
hideBackdrop={false}
|
hideBackdrop={false}
|
||||||
variant="temporary"
|
variant="temporary"
|
||||||
BackdropProps={{
|
BackdropProps={{
|
||||||
@@ -14607,7 +14608,6 @@ const AngularWorkflow = (defaultprops) => {
|
|||||||
maxWidth: isMobile ? "100%" : 420,
|
maxWidth: isMobile ? "100%" : 420,
|
||||||
color: "white",
|
color: "white",
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
zIndex: 10005,
|
|
||||||
borderLeft: theme.palette.defaultBorder,
|
borderLeft: theme.palette.defaultBorder,
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
@@ -14631,16 +14631,32 @@ const AngularWorkflow = (defaultprops) => {
|
|||||||
: null}
|
: null}
|
||||||
{executionModalView === 0 ? (
|
{executionModalView === 0 ? (
|
||||||
<div style={{ padding: isMobile ? "0px 0px 0px 10px" : 25, zIndex: 12502, }}>
|
<div style={{ padding: isMobile ? "0px 0px 0px 10px" : 25, zIndex: 12502, }}>
|
||||||
<Breadcrumbs
|
<div style={{display: "flex", }}>
|
||||||
aria-label="breadcrumb"
|
<Breadcrumbs
|
||||||
separator="›"
|
aria-label="breadcrumb"
|
||||||
style={{ color: "white", fontSize: 16 }}
|
separator="›"
|
||||||
>
|
style={{ color: "white", fontSize: 16 }}
|
||||||
<h2 style={{ color: "rgba(255,255,255,0.5)" }}>
|
>
|
||||||
<DirectionsRunIcon style={{ marginRight: 10 }} />
|
<h2 style={{ color: "rgba(255,255,255,0.5)" }}>
|
||||||
All Workflow Runs
|
<DirectionsRunIcon style={{ marginRight: 10 }} />
|
||||||
</h2>
|
All Workflow Runs
|
||||||
</Breadcrumbs>
|
</h2>
|
||||||
|
</Breadcrumbs>
|
||||||
|
<Tooltip
|
||||||
|
title={"Explore Executions further"}
|
||||||
|
placement="left-start"
|
||||||
|
style={{ zIndex: 10010 }}
|
||||||
|
>
|
||||||
|
<a href={`/workflows/debug?workflow_id=${workflow.id}`} style={{textDecoration: "none", }}>
|
||||||
|
<Button
|
||||||
|
color="secondary"
|
||||||
|
style={{marginLeft: 50, maxHeight: 30, marginTop: 20, }}
|
||||||
|
>
|
||||||
|
<QueryStatsIcon style={{color: "white", }}/>
|
||||||
|
</Button>
|
||||||
|
</a>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
<Button
|
<Button
|
||||||
style={{ borderRadius: "0px" }}
|
style={{ borderRadius: "0px" }}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
@@ -15367,7 +15383,7 @@ const AngularWorkflow = (defaultprops) => {
|
|||||||
}}
|
}}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const oldstartnode = cy.getElementById(data.action.id);
|
const oldstartnode = cy.getElementById(data.action.id);
|
||||||
console.log("FOUND NODe: ", oldstartnode)
|
//console.log("FOUND NODe: ", oldstartnode)
|
||||||
if (oldstartnode !== undefined && oldstartnode !== null) {
|
if (oldstartnode !== undefined && oldstartnode !== null) {
|
||||||
const foundname = oldstartnode.data("label")
|
const foundname = oldstartnode.data("label")
|
||||||
if (foundname !== undefined && foundname !== null) {
|
if (foundname !== undefined && foundname !== null) {
|
||||||
@@ -15375,7 +15391,7 @@ const AngularWorkflow = (defaultprops) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("Click data: ", data)
|
//console.log("Click data: ", data)
|
||||||
//data.action.label = ""
|
//data.action.label = ""
|
||||||
setSelectedResult(data);
|
setSelectedResult(data);
|
||||||
setCodeModalOpen(true);
|
setCodeModalOpen(true);
|
||||||
@@ -16476,7 +16492,7 @@ const AngularWorkflow = (defaultprops) => {
|
|||||||
|
|
||||||
var newAuthOption = JSON.parse(JSON.stringify(authenticationOption));
|
var newAuthOption = JSON.parse(JSON.stringify(authenticationOption));
|
||||||
var newFields = [];
|
var newFields = [];
|
||||||
console.log("Fields: ", newAuthOption.fields)
|
console.log("Fields: ", newAuthOption.fields)
|
||||||
for (let authkey in newAuthOption.fields) {
|
for (let authkey in newAuthOption.fields) {
|
||||||
const value = newAuthOption.fields[authkey];
|
const value = newAuthOption.fields[authkey];
|
||||||
newFields.push({
|
newFields.push({
|
||||||
|
|||||||
+223
-141
@@ -242,7 +242,7 @@ export const appCategories = [
|
|||||||
"name": "Eradication",
|
"name": "Eradication",
|
||||||
"color": "#FFC107",
|
"color": "#FFC107",
|
||||||
"icon": "eradication",
|
"icon": "eradication",
|
||||||
"action_labels": ["List Alerts", "Close Alert", "Get 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", "Trigger host scan",],
|
||||||
}, {
|
}, {
|
||||||
"name": "Cases",
|
"name": "Cases",
|
||||||
"color": "#FFC107",
|
"color": "#FFC107",
|
||||||
@@ -374,8 +374,8 @@ const AppCreator = (defaultprops) => {
|
|||||||
"API key",
|
"API key",
|
||||||
"Bearer auth",
|
"Bearer auth",
|
||||||
"Basic auth",
|
"Basic auth",
|
||||||
"Oauth2",
|
|
||||||
"JWT",
|
"JWT",
|
||||||
|
"Oauth2",
|
||||||
];
|
];
|
||||||
const apikeySelection = ["Header", "Query"];
|
const apikeySelection = ["Header", "Query"];
|
||||||
|
|
||||||
@@ -411,6 +411,7 @@ const AppCreator = (defaultprops) => {
|
|||||||
const [appBuilding, setAppBuilding] = useState(false);
|
const [appBuilding, setAppBuilding] = useState(false);
|
||||||
const [fileDownloadEnabled, setFileDownloadEnabled] = useState(false);
|
const [fileDownloadEnabled, setFileDownloadEnabled] = useState(false);
|
||||||
const [actionAmount, setActionAmount] = useState(increaseAmount);
|
const [actionAmount, setActionAmount] = useState(increaseAmount);
|
||||||
|
const [oauth2Type, setOauth2Type] = useState("application");
|
||||||
const defaultAuth = {
|
const defaultAuth = {
|
||||||
name: "",
|
name: "",
|
||||||
type: "header",
|
type: "header",
|
||||||
@@ -1678,7 +1679,7 @@ const AppCreator = (defaultprops) => {
|
|||||||
value.in.length > 0
|
value.in.length > 0
|
||||||
) {
|
) {
|
||||||
setParameterName(value.in);
|
setParameterName(value.in);
|
||||||
optionset = true
|
optionset = true
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (value.scheme === "bearer") {
|
} else if (value.scheme === "bearer") {
|
||||||
@@ -1709,24 +1710,24 @@ const AppCreator = (defaultprops) => {
|
|||||||
newauth.push({
|
newauth.push({
|
||||||
"name": key,
|
"name": key,
|
||||||
"type": value.in.toLowerCase(),
|
"type": value.in.toLowerCase(),
|
||||||
"in": value.in.toLowerCase(),
|
"in": value.in.toLowerCase(),
|
||||||
"example": "",
|
"example": "",
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
newauth.push({
|
newauth.push({
|
||||||
"name": key,
|
"name": key,
|
||||||
"type": value.in.toLowerCase(),
|
"type": value.in.toLowerCase(),
|
||||||
"in": value.in.toLowerCase(),
|
"in": value.in.toLowerCase(),
|
||||||
"example": "",
|
"example": "",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value.description !== undefined && value.description !== null && value.description.length > 0) {
|
if (value.description !== undefined && value.description !== null && value.description.length > 0) {
|
||||||
// Don't want a real description - just the ones we're replacing with
|
// Don't want a real description - just the ones we're replacing with
|
||||||
if ((value.description.split(" ").length - 1) <= 2) {
|
if ((value.description.split(" ").length - 1) <= 2) {
|
||||||
setRefreshUrl(value.description)
|
setRefreshUrl(value.description)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (value.scheme === "basic") {
|
} else if (value.scheme === "basic") {
|
||||||
setAuthenticationOption("Basic auth");
|
setAuthenticationOption("Basic auth");
|
||||||
@@ -1734,15 +1735,15 @@ const AppCreator = (defaultprops) => {
|
|||||||
optionset = true
|
optionset = true
|
||||||
|
|
||||||
} else if (value.scheme === "oauth2") {
|
} else if (value.scheme === "oauth2") {
|
||||||
setAuthenticationOption("Oauth2");
|
setAuthenticationOption("Oauth2");
|
||||||
setAuthenticationRequired(true);
|
setAuthenticationRequired(true);
|
||||||
optionset = true
|
optionset = true
|
||||||
|
|
||||||
} else if (value.type === "oauth2" || key === "Oauth2" || key === "Oauth2c" || (key !== undefined && key !== null && key.toLowerCase().includes("oauth2"))) {
|
} else if (value.type === "oauth2" || key === "Oauth2" || key === "Oauth2c" || (key !== undefined && key !== null && key.toLowerCase().includes("oauth2"))) {
|
||||||
//toast("Can't handle Oauth2 auth yet.")
|
//toast("Can't handle Oauth2 auth yet.")
|
||||||
setAuthenticationOption("Oauth2");
|
setAuthenticationOption("Oauth2");
|
||||||
setAuthenticationRequired(true);
|
setAuthenticationRequired(true);
|
||||||
optionset = true
|
optionset = true
|
||||||
|
|
||||||
//console.log("FLOW-1: ", value)
|
//console.log("FLOW-1: ", value)
|
||||||
const flowkey = value.flow === undefined ? "flows" : "flow";
|
const flowkey = value.flow === undefined ? "flows" : "flow";
|
||||||
@@ -1754,12 +1755,11 @@ const AppCreator = (defaultprops) => {
|
|||||||
//console.log("FLOW2: ", value[flowkey][basekey])
|
//console.log("FLOW2: ", value[flowkey][basekey])
|
||||||
if (value[flowkey] !== undefined && value[flowkey][basekey] !== undefined
|
if (value[flowkey] !== undefined && value[flowkey][basekey] !== undefined
|
||||||
) {
|
) {
|
||||||
if (
|
if (value[flowkey][basekey].authorizationUrl !== undefined && parameterName.length === 0) {
|
||||||
value[flowkey][basekey].authorizationUrl !== undefined &&
|
setParameterName(value[flowkey][basekey].authorizationUrl);
|
||||||
parameterName.length === 0
|
// } else {
|
||||||
) {
|
// setOauth2Type("application")
|
||||||
setParameterName(value[flowkey][basekey].authorizationUrl);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
var tokenUrl = "";
|
var tokenUrl = "";
|
||||||
if (value[flowkey][basekey].tokenUrl !== undefined) {
|
if (value[flowkey][basekey].tokenUrl !== undefined) {
|
||||||
@@ -2846,7 +2846,6 @@ const AppCreator = (defaultprops) => {
|
|||||||
style={{ margin: 0, flex: "1", backgroundColor: inputColor }}
|
style={{ margin: 0, flex: "1", backgroundColor: inputColor }}
|
||||||
fullWidth={true}
|
fullWidth={true}
|
||||||
placeholder="/security/user/authenticate"
|
placeholder="/security/user/authenticate"
|
||||||
type="name"
|
|
||||||
id="standard-required"
|
id="standard-required"
|
||||||
margin="normal"
|
margin="normal"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
@@ -2866,6 +2865,40 @@ const AppCreator = (defaultprops) => {
|
|||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
<Typography
|
||||||
|
variant="body2"
|
||||||
|
color="textSecondary"
|
||||||
|
style={{ marginTop: 10 }}
|
||||||
|
>
|
||||||
|
Optional: Authentication queries
|
||||||
|
</Typography>
|
||||||
|
{/*
|
||||||
|
<TextField
|
||||||
|
style={{ margin: 0, flex: "1", backgroundColor: inputColor }}
|
||||||
|
fullWidth={true}
|
||||||
|
placeholder="grant_type=client_credentials&scope=connect.api.read"
|
||||||
|
id=""
|
||||||
|
margin="normal"
|
||||||
|
variant="outlined"
|
||||||
|
defaultValue={parameterName}
|
||||||
|
helperText={
|
||||||
|
<span style={{ color: "white", marginBottom: "2px" }}>
|
||||||
|
Must use 'key=value&key=value' format
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
onBlur={(e) => {
|
||||||
|
//setParameterName(e.target.value)
|
||||||
|
}}
|
||||||
|
InputProps={{
|
||||||
|
classes: {
|
||||||
|
notchedOutline: classes.notchedOutline,
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
color: "white",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
*/}
|
||||||
</div>
|
</div>
|
||||||
) : null;
|
) : null;
|
||||||
|
|
||||||
@@ -2878,56 +2911,64 @@ const AppCreator = (defaultprops) => {
|
|||||||
color="textSecondary"
|
color="textSecondary"
|
||||||
style={{ marginTop: 10 }}
|
style={{ marginTop: 10 }}
|
||||||
>
|
>
|
||||||
Find the Authorization URL, Token URL and scopes in question for the API. Ensure the app in question is pointed at https://shuffler.io/set_authentication
|
{oauth2Type === "delegated" ?
|
||||||
|
"Find the Authorization URL, Token URL and scopes in question for the API. Ensure your app in the service uses redirect url https://shuffler.io/set_authentication"
|
||||||
|
:
|
||||||
|
"Find the Token URL and scopes in question for the API"
|
||||||
|
}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<Typography
|
{oauth2Type === "delegated" ?
|
||||||
variant="body2"
|
<span>
|
||||||
color="textSecondary"
|
<Typography
|
||||||
style={{ marginTop: 10 }}
|
variant="body2"
|
||||||
>
|
color="textSecondary"
|
||||||
Base Authorization URL for Oauth2
|
style={{ marginTop: 10 }}
|
||||||
</Typography>
|
>
|
||||||
<TextField
|
Base Authorization URL for Oauth2
|
||||||
required
|
</Typography>
|
||||||
style={{ margin: 0, flex: "1", backgroundColor: inputColor }}
|
<TextField
|
||||||
fullWidth={true}
|
required
|
||||||
placeholder="https://.../oauth2/authorize"
|
style={{ margin: 0, flex: "1", backgroundColor: inputColor }}
|
||||||
type="name"
|
fullWidth={true}
|
||||||
id="standard-required"
|
placeholder="https://.../oauth2/authorize"
|
||||||
margin="normal"
|
type="name"
|
||||||
variant="outlined"
|
id="standard-required"
|
||||||
value={parameterName}
|
margin="normal"
|
||||||
onChange={(e) => setParameterName(e.target.value)}
|
variant="outlined"
|
||||||
|
value={parameterName}
|
||||||
|
onChange={(e) => setParameterName(e.target.value)}
|
||||||
onBlur={(event) => {
|
onBlur={(event) => {
|
||||||
var tmpstring = event.target.value.trim();
|
var tmpstring = event.target.value.trim();
|
||||||
|
|
||||||
if (
|
if (
|
||||||
tmpstring.length > 4 &&
|
tmpstring.length > 4 &&
|
||||||
!tmpstring.startsWith("http") &&
|
!tmpstring.startsWith("http") &&
|
||||||
!tmpstring.startsWith("ftp")
|
!tmpstring.startsWith("ftp")
|
||||||
) {
|
) {
|
||||||
toast("Auth URL must start with http(s)://");
|
toast("Auth URL must start with http(s)://");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tmpstring.includes("?")) {
|
if (tmpstring.includes("?")) {
|
||||||
var newtmp = tmpstring.split("?")
|
var newtmp = tmpstring.split("?")
|
||||||
if (tmpstring.length > 1) {
|
if (tmpstring.length > 1) {
|
||||||
tmpstring = newtmp[0]
|
tmpstring = newtmp[0]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setParameterName(tmpstring)
|
setParameterName(tmpstring)
|
||||||
}}
|
}}
|
||||||
InputProps={{
|
InputProps={{
|
||||||
classes: {
|
classes: {
|
||||||
notchedOutline: classes.notchedOutline,
|
notchedOutline: classes.notchedOutline,
|
||||||
},
|
},
|
||||||
style: {
|
style: {
|
||||||
color: "white",
|
color: "white",
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
</span>
|
||||||
|
: null}
|
||||||
<Typography
|
<Typography
|
||||||
variant="body2"
|
variant="body2"
|
||||||
color="textSecondary"
|
color="textSecondary"
|
||||||
@@ -2977,75 +3018,79 @@ const AppCreator = (defaultprops) => {
|
|||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Typography
|
{oauth2Type === "delegated" ?
|
||||||
variant="body2"
|
<span>
|
||||||
color="textSecondary"
|
<Typography
|
||||||
style={{ marginTop: 10 }}
|
variant="body2"
|
||||||
>
|
color="textSecondary"
|
||||||
Refresh-token URL for Oauth2 (Optional)
|
style={{ marginTop: 10 }}
|
||||||
</Typography>
|
>
|
||||||
<TextField
|
Refresh-token URL for Oauth2 (Optional)
|
||||||
style={{ margin: 0, flex: "1", backgroundColor: inputColor }}
|
</Typography>
|
||||||
fullWidth={true}
|
<TextField
|
||||||
placeholder="The URL to retrieve refresh-tokens at"
|
style={{ margin: 0, flex: "1", backgroundColor: inputColor }}
|
||||||
type="name"
|
fullWidth={true}
|
||||||
id="standard-required"
|
placeholder="The URL to retrieve refresh-tokens at"
|
||||||
margin="normal"
|
type="name"
|
||||||
variant="outlined"
|
id="standard-required"
|
||||||
value={refreshUrl}
|
margin="normal"
|
||||||
onChange={(e) => setRefreshUrl(e.target.value)}
|
variant="outlined"
|
||||||
onBlur={(event) => {
|
value={refreshUrl}
|
||||||
var tmpstring = event.target.value.trim();
|
onChange={(e) => setRefreshUrl(e.target.value)}
|
||||||
|
onBlur={(event) => {
|
||||||
|
var tmpstring = event.target.value.trim();
|
||||||
|
|
||||||
if (
|
if (
|
||||||
tmpstring.length > 4 &&
|
tmpstring.length > 4 &&
|
||||||
!tmpstring.startsWith("http") &&
|
!tmpstring.startsWith("http") &&
|
||||||
!tmpstring.startsWith("ftp")
|
!tmpstring.startsWith("ftp")
|
||||||
) {
|
) {
|
||||||
toast("Refresh URL must start with http(s)://");
|
toast("Refresh URL must start with http(s)://");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tmpstring.includes("?")) {
|
if (tmpstring.includes("?")) {
|
||||||
var newtmp = tmpstring.split("?")
|
var newtmp = tmpstring.split("?")
|
||||||
if (tmpstring.length > 1) {
|
if (tmpstring.length > 1) {
|
||||||
tmpstring = newtmp[0]
|
tmpstring = newtmp[0]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setRefreshUrl(tmpstring)
|
setRefreshUrl(tmpstring)
|
||||||
}}
|
}}
|
||||||
InputProps={{
|
InputProps={{
|
||||||
style: {
|
style: {
|
||||||
color: "white",
|
color: "white",
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Typography
|
</span>
|
||||||
variant="body2"
|
: null}
|
||||||
color="textSecondary"
|
<Typography
|
||||||
style={{ marginTop: 10 }}
|
variant="body2"
|
||||||
>
|
color="textSecondary"
|
||||||
Scopes for Oauth2
|
style={{ marginTop: 10 }}
|
||||||
</Typography>
|
>
|
||||||
<MuiChipsInput
|
Scopes for Oauth2
|
||||||
style={{border: "2px solid #f86a3e", borderRadius: theme.palette.borderRadius,}}
|
</Typography>
|
||||||
|
<MuiChipsInput
|
||||||
|
style={{border: "2px solid #f86a3e", borderRadius: theme.palette.borderRadius,}}
|
||||||
required
|
required
|
||||||
InputProps={{
|
InputProps={{
|
||||||
style: {
|
style: {
|
||||||
color: "white",
|
color: "white",
|
||||||
maxHeight: 50,
|
maxHeight: 50,
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
style={{ maxHeight: 80, overflowX: "hidden", overflowY: "auto" }}
|
style={{ maxHeight: 80, overflowX: "hidden", overflowY: "auto" }}
|
||||||
placeholder="Available Oauth2 Scopes"
|
placeholder="Available Oauth2 Scopes (enter to add)"
|
||||||
color="primary"
|
color="primary"
|
||||||
fullWidth
|
fullWidth
|
||||||
value={oauth2Scopes}
|
value={oauth2Scopes}
|
||||||
onChange={(chips) => {
|
onChange={(chips) => {
|
||||||
setOauth2Scopes(chips)
|
setOauth2Scopes(chips)
|
||||||
setUpdate(Math.random())
|
setUpdate(Math.random())
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : null;
|
) : null;
|
||||||
|
|
||||||
@@ -3125,7 +3170,7 @@ const AppCreator = (defaultprops) => {
|
|||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
<div style={{marginLeft: 5, flex: 1,}}>
|
<div style={{marginLeft: 5, flex: 1,}}>
|
||||||
Value prefix
|
Prefix
|
||||||
<TextField
|
<TextField
|
||||||
style={{ marginTop: 0, flex: "1", backgroundColor: inputColor }}
|
style={{ marginTop: 0, flex: "1", backgroundColor: inputColor }}
|
||||||
fullWidth={true}
|
fullWidth={true}
|
||||||
@@ -3137,7 +3182,7 @@ const AppCreator = (defaultprops) => {
|
|||||||
value={refreshUrl}
|
value={refreshUrl}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
// Just reusing this state
|
// Just reusing this state
|
||||||
setRefreshUrl(e.target.value);
|
setRefreshUrl(e.target.value);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -3618,13 +3663,13 @@ const AppCreator = (defaultprops) => {
|
|||||||
<Dialog
|
<Dialog
|
||||||
open={actionsModalOpen}
|
open={actionsModalOpen}
|
||||||
fullWidth
|
fullWidth
|
||||||
PaperProps={{
|
PaperProps={{
|
||||||
style: {
|
style: {
|
||||||
backgroundColor: surfaceColor,
|
backgroundColor: surfaceColor,
|
||||||
color: "white",
|
color: "white",
|
||||||
minWidth: 500,
|
minWidth: 550,
|
||||||
maxWidth: 500,
|
maxWidth: 550,
|
||||||
maxHeight: 800,
|
maxHeight: 750,
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
@@ -5865,6 +5910,7 @@ const AppCreator = (defaultprops) => {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<div style={{padding: 25, border: "2px solid rgba(255,255,255,0.7)", borderRadius: theme.palette.borderRadius, }}>
|
<div style={{padding: 25, border: "2px solid rgba(255,255,255,0.7)", borderRadius: theme.palette.borderRadius, }}>
|
||||||
|
<span style={{display: "flex", }}>
|
||||||
<FormControl style={{ }} variant="outlined">
|
<FormControl style={{ }} variant="outlined">
|
||||||
<Typography variant="h6">Authentication</Typography>
|
<Typography variant="h6">Authentication</Typography>
|
||||||
<a
|
<a
|
||||||
@@ -5889,7 +5935,7 @@ const AppCreator = (defaultprops) => {
|
|||||||
setParameterLocation("")
|
setParameterLocation("")
|
||||||
}
|
}
|
||||||
|
|
||||||
setExtraAuth([])
|
setExtraAuth([])
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
value={authenticationOption}
|
value={authenticationOption}
|
||||||
@@ -5910,6 +5956,42 @@ const AppCreator = (defaultprops) => {
|
|||||||
))}
|
))}
|
||||||
</Select>
|
</Select>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
{authenticationOption === "Oauth2" ?
|
||||||
|
<FormControl style={{ marginLeft: 350, maxWidth: 200, }} variant="outlined">
|
||||||
|
{/*
|
||||||
|
<Typography variant="body2">
|
||||||
|
- Delegated: The user will get a popup for access their personal data.
|
||||||
|
- Application: Permissions are set by the app creator in the 3rd party platform.
|
||||||
|
</Typography>
|
||||||
|
*/}
|
||||||
|
<Select
|
||||||
|
fullWidth
|
||||||
|
label="Oauth2 type"
|
||||||
|
onChange={(e) => {
|
||||||
|
setOauth2Type(e.target.value);
|
||||||
|
}}
|
||||||
|
value={oauth2Type}
|
||||||
|
style={{
|
||||||
|
backgroundColor: inputColor,
|
||||||
|
color: "white",
|
||||||
|
height: "50px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{["delegated", "application"].map((data, index) => (
|
||||||
|
<MenuItem
|
||||||
|
key={index}
|
||||||
|
style={{ backgroundColor: inputColor, color: "white" }}
|
||||||
|
value={data}
|
||||||
|
>
|
||||||
|
{data}
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
: null}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
|
||||||
<div style={{marginTop: 15 }} />
|
<div style={{marginTop: 15 }} />
|
||||||
{basicAuth}
|
{basicAuth}
|
||||||
{bearerAuth}
|
{bearerAuth}
|
||||||
|
|||||||
@@ -419,7 +419,7 @@ const Welcome = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
:
|
:
|
||||||
<Fade in={true}>
|
<Fade in={true}>
|
||||||
<div style={{maxWidth: isMobile ? null : 590, margin: "auto", marginTop: 50, }}>
|
<div style={{maxWidth: isMobile ? null : 590, margin: "auto", marginTop: 50,textAlign : isMobile ? "center" : null }}>
|
||||||
<Typography variant="h4" style={{color: "#F1F1F1", marginTop: 50, fontSize: 32}}>
|
<Typography variant="h4" style={{color: "#F1F1F1", marginTop: 50, fontSize: 32}}>
|
||||||
Help us get to know you
|
Help us get to know you
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|||||||
@@ -68,11 +68,12 @@ import {
|
|||||||
CloudDownload as CloudDownloadIcon,
|
CloudDownload as CloudDownloadIcon,
|
||||||
ExpandLess as ExpandLessIcon,
|
ExpandLess as ExpandLessIcon,
|
||||||
ExpandMore as ExpandMoreIcon,
|
ExpandMore as ExpandMoreIcon,
|
||||||
Done as DoneIcon,
|
Done as DoneIcon,
|
||||||
CheckCircle as CheckCircleIcon,
|
CheckCircle as CheckCircleIcon,
|
||||||
RadioButtonUnchecked as RadioButtonUncheckedIcon,
|
RadioButtonUnchecked as RadioButtonUncheckedIcon,
|
||||||
ArrowLeft as ArrowLeftIcon,
|
ArrowLeft as ArrowLeftIcon,
|
||||||
ArrowRight as ArrowRightIcon,
|
ArrowRight as ArrowRightIcon,
|
||||||
|
QueryStats as QueryStatsIcon,
|
||||||
} from "@mui/icons-material";
|
} from "@mui/icons-material";
|
||||||
|
|
||||||
import { DataGrid, GridToolbar } from "@mui/x-data-grid";
|
import { DataGrid, GridToolbar } from "@mui/x-data-grid";
|
||||||
@@ -3061,6 +3062,18 @@ const Workflows = (props) => {
|
|||||||
|
|
||||||
const workflowButtons = (
|
const workflowButtons = (
|
||||||
<span>
|
<span>
|
||||||
|
<Tooltip color="primary" title={"Explore Workflow Runs"} placement="top">
|
||||||
|
<Button
|
||||||
|
disabled={workflows.length === 0}
|
||||||
|
color="secondary"
|
||||||
|
variant="text"
|
||||||
|
onClick={() => {
|
||||||
|
navigate("/workflows/debug")
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<QueryStatsIcon />
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
{view === "list" && (
|
{view === "list" && (
|
||||||
<Tooltip color="primary" title={"Grid View"} placement="top">
|
<Tooltip color="primary" title={"Grid View"} placement="top">
|
||||||
<Button
|
<Button
|
||||||
@@ -3423,8 +3436,8 @@ const Workflows = (props) => {
|
|||||||
height: isCloud ? imgSize : imgSize+4,
|
height: isCloud ? imgSize : imgSize+4,
|
||||||
width: isCloud ? imgSize : imgSize+4,
|
width: isCloud ? imgSize : imgSize+4,
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
top: isCloud ? -1 : -2,
|
top: -2,
|
||||||
left: isCloud ? -1 : -2,
|
left: -2,
|
||||||
cursor: "pointer",
|
cursor: "pointer",
|
||||||
zIndex: 99,
|
zIndex: 99,
|
||||||
border: "2px solid rgba(255,255,255,0.7)",
|
border: "2px solid rgba(255,255,255,0.7)",
|
||||||
@@ -3786,7 +3799,7 @@ const Workflows = (props) => {
|
|||||||
color: "white",
|
color: "white",
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
borderLeft: theme.palette.defaultBorder,
|
borderLeft: theme.palette.defaultBorder,
|
||||||
marginTop: 64,
|
marginTop: 100,
|
||||||
borderRadius: "5px 0px 0px 0px",
|
borderRadius: "5px 0px 0px 0px",
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
|
|||||||
Reference in New Issue
Block a user