diff --git a/frontend/src/components/NewHeader.jsx b/frontend/src/components/NewHeader.jsx
new file mode 100644
index 00000000..7b72f0bd
--- /dev/null
+++ b/frontend/src/components/NewHeader.jsx
@@ -0,0 +1,1377 @@
+import React, { useState } from "react";
+import { toast } from "react-toastify";
+import theme from "../theme.jsx";
+import { BrowserView, MobileView } from "react-device-detect";
+
+import { useNavigate, Link } from "react-router-dom";
+import ReactGA from "react-ga4";
+import SearchField from "../components/Searchfield.jsx";
+import {
+ Paper,
+ Typography,
+ Badge,
+ Tooltip,
+ List,
+ ListItem,
+ Avatar,
+ Menu,
+ MenuItem,
+ Select,
+ Button,
+ Grid,
+ IconButton,
+ Divider,
+ LinearProgress,
+
+ AppBar,
+} from "@mui/material";
+
+import {
+ MeetingRoom as MeetingRoomIcon,
+ HelpOutline as HelpOutlineIcon,
+ Settings as SettingsIcon,
+ Notifications as NotificationsIcon,
+ Home as HomeIcon,
+ Apps as AppsIcon,
+ Description as DescriptionIcon,
+ EmojiObjects as EmojiObjectsIcon,
+ Business as BusinessIcon,
+ Polyline as PolylineIcon,
+} from "@mui/icons-material";
+
+import {
+ Analytics as AnalyticsIcon,
+ Lightbulb as LightbulbIcon,
+} from "@mui/icons-material";
+
+const hoverColor = "#f85a3e";
+const hoverOutColor = "#e8eaf6";
+
+const Header = (props) => {
+ const {
+ globalUrl,
+ setNotifications,
+ notifications,
+ isLoaded,
+ isLoggedIn,
+ removeCookie,
+ homePage,
+ userdata,
+ isMobile,
+ serverside,
+ } = props;
+
+ const [HomeHoverColor, setHomeHoverColor] = useState(hoverOutColor);
+ const [SoarHoverColor, setSoarHoverColor] = useState(hoverOutColor);
+ const [LoginHoverColor, setLoginHoverColor] = useState(hoverOutColor);
+ const [DocsHoverColor, setDocsHoverColor] = useState(hoverOutColor);
+ const [HelpHoverColor, setHelpHoverColor] = useState(hoverOutColor);
+ const [isHeader, setIsHeader] = React.useState(false);
+ const [anchorEl, setAnchorEl] = React.useState(null);
+ const [anchorElAvatar, setAnchorElAvatar] = React.useState(null);
+ const [subAnchorEl, setSubAnchorEl] = React.useState(null);
+ let navigate = useNavigate();
+
+ const handleClick = (event) => {
+ setAnchorEl(event.currentTarget);
+ };
+
+ const handleClose = () => {
+ setAnchorEl(null);
+ setAnchorElAvatar(null);
+ };
+ // Should be based on some path
+ const logoCheck = !homePage ? null : null
+
+ const hrefStyle = {
+ color: hoverOutColor,
+ textDecoration: "none",
+ };
+
+ const menuText = {
+ textTransform: "none",
+ color: "#FFF",
+ textAlign: "center",
+ fontSize: 16,
+ fontStyle: "normal",
+ fontWeight: 400,
+ lineHeight: "normal",
+ }
+
+ const isCloud =
+ serverside === true || typeof window === "undefined"
+ ? true
+ : window.location.host === "localhost:3002" ||
+ window.location.host === "shuffler.io";
+
+ const clearNotifications = () => {
+ // Don't really care about the logout
+ fetch(`${globalUrl}/api/v1/notifications/clear`, {
+ credentials: "include",
+ method: "GET",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ })
+ .then(function (response) {
+ if (response.status !== 200) {
+ console.log("Error in response");
+ }
+
+ return response.json();
+ })
+ .then(function (responseJson) {
+ if (responseJson.success === true) {
+ setNotifications([]);
+ handleClose();
+ } else {
+ toast("Failed dismissing notifications. Please try again later.");
+ }
+ })
+ .catch((error) => {
+ console.log("error in notification dismissal: ", error);
+ //removeCookie("session_token", {path: "/"})
+ });
+ };
+
+ const dismissNotification = (alert_id) => {
+ // Don't really care about the logout
+ fetch(`${globalUrl}/api/v1/notifications/${alert_id}/markasread`, {
+ credentials: "include",
+ method: "GET",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ })
+ .then(function (response) {
+ if (response.status !== 200) {
+ console.log("Error in response");
+ }
+
+ return response.json();
+ })
+ .then(function (responseJson) {
+ if (responseJson.success === true) {
+ const newNotifications = notifications.filter(
+ (data) => data.id !== alert_id
+ );
+ console.log("NEW NOTIFICATIONS: ", newNotifications);
+ setNotifications(newNotifications);
+ } else {
+ toast("Failed dismissing notification. Please try again later.");
+ }
+ })
+ .catch((error) => {
+ console.log("error in notification dismissal: ", error);
+ //removeCookie("session_token", {path: "/"})
+ });
+ };
+
+ // DEBUG HERE
+ const handleClickLogout = () => {
+ console.log("SHOULD LOG OUT");
+
+ // Don't really care about the logout
+ fetch(globalUrl + "/api/v1/logout", {
+ credentials: "include",
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ })
+ .then(() => {
+ // Log out anyway
+ removeCookie("session_token", { path: "/" });
+ removeCookie("session_token", { path: "/" });
+ removeCookie("session_token", { path: "/" });
+ removeCookie("session_token", { path: "/" });
+ window.location.pathname = "/";
+ })
+ .catch((error) => {
+ console.log(error);
+ });
+ };
+
+ // Rofl this is weird
+ const handleDocsHover = () => {
+ setDocsHoverColor(hoverColor);
+ };
+
+ const handleDocsHoverOut = () => {
+ setDocsHoverColor(hoverOutColor);
+ };
+
+ const handleHomeHover = () => {
+ setHomeHoverColor(hoverColor);
+ };
+
+ const handleHelpHover = () => {
+ setHelpHoverColor(hoverColor);
+ };
+
+ const handleHelpHoverOut = () => {
+ setHelpHoverColor(hoverOutColor);
+ };
+
+ const handleSoarHover = () => {
+ setSoarHoverColor(hoverColor);
+ };
+
+ const handleSoarHoverOut = () => {
+ setSoarHoverColor(hoverOutColor);
+ };
+
+ const handleHomeHoverOut = () => {
+ setHomeHoverColor(hoverOutColor);
+ };
+
+ const handleLoginHover = () => {
+ setLoginHoverColor(hoverColor);
+ };
+
+ const handleLoginHoverOut = () => {
+ setLoginHoverColor(hoverOutColor);
+ };
+
+ const notificationWidth = 300
+ const imagesize = 22;
+ const boxColor = "#86c142";
+
+ const NotificationItem = (props) => {
+ const {data} = props
+
+ var image = "";
+ var orgName = "";
+ var orgId = "";
+ if (userdata.orgs !== undefined) {
+ const foundOrg = userdata.orgs.find((org) => org.id === data["org_id"]);
+ if (foundOrg !== undefined && foundOrg !== null) {
+ //position: "absolute", bottom: 5, right: -5,
+ const imageStyle = {
+ width: imagesize,
+ height: imagesize,
+ pointerEvents: "none",
+ marginLeft:
+ data.creator_org !== undefined && data.creator_org.length > 0
+ ? 20
+ : 0,
+ borderRadius: 10,
+ border:
+ foundOrg.id === userdata.active_org.id
+ ? `3px solid ${boxColor}`
+ : null,
+ cursor: "pointer",
+ marginRight: 10,
+ };
+
+ image =
+ foundOrg.image === "" ? (
+

+ ) : (
+

{}}
+ />
+ );
+
+ orgName = foundOrg.name;
+ orgId = foundOrg.id;
+ }
+ }
+
+ return (
+
+ {/*
+ {new Date(data.updated_at).toISOString()}
+ */}
+ {data.reference_url !== undefined &&
+ data.reference_url !== null &&
+ data.reference_url.length > 0 ? (
+
+ {data.title}
+
+ ) : (
+
+ {data.title}
+
+ )}
+
+ {data.image !== undefined &&
+ data.image !== null &&
+ data.image.length > 0 ? (
+
+ ) : null}
+ {data.description}
+ {/*data.tags !== undefined && data.tags !== null && data.tags.length > 0 ?
+ data.tags.map((tag, index) => {
+ return (
+ {
+ }}
+ variant="outlined"
+ color="primary"
+ />
+ )
+ })
+ : null */}
+
+ {data.read === false ? (
+
+ ) : null}
+
+ {}}
+ >
+ {image}
+
+
+
+
+ );
+ };
+
+ const notificationMenu = (
+
+ {
+ setAnchorEl(event.currentTarget);
+ }}
+ >
+
+
+
+
+
+
+ );
+
+ const handleClickChangeOrg = (orgId) => {
+ // Don't really care about the logout
+ //name: org.name,
+ //orgId = "asd"
+ const data = {
+ org_id: orgId,
+ };
+
+ localStorage.setItem("globalUrl", "");
+ localStorage.setItem("getting_started_sidebar", "open");
+
+ fetch(`${globalUrl}/api/v1/orgs/${orgId}/change`, {
+ mode: "cors",
+ credentials: "include",
+ crossDomain: true,
+ method: "POST",
+ body: JSON.stringify(data),
+ withCredentials: true,
+ headers: {
+ "Content-Type": "application/json; charset=utf-8",
+ },
+ })
+ .then(function (response) {
+ if (response.status !== 200) {
+ console.log("Error in response");
+ }
+
+ return response.json();
+ })
+ .then(function (responseJson) {
+ if (responseJson.success === true) {
+ if (
+ responseJson.region_url !== undefined &&
+ responseJson.region_url !== null &&
+ responseJson.region_url.length > 0
+ ) {
+ console.log("Region Change: ", responseJson.region_url);
+ localStorage.setItem("globalUrl", responseJson.region_url);
+ //globalUrl = responseJson.region_url
+ }
+
+ setTimeout(() => {
+ window.location.reload();
+ }, 2000);
+ toast("Successfully changed active organization - refreshing!");
+ } else {
+ toast("Failed changing org: ", responseJson.reason);
+ }
+ })
+ .catch((error) => {
+ console.log("error changing: ", error);
+ //removeCookie("session_token", {path: "/"})
+ });
+ };
+
+ const supportMenu = (
+
+
+
+ {}}
+ >
+
+
+
+
+
+ );
+
+ // Should be based on some path
+ const parsedAvatar =
+ userdata.avatar !== undefined &&
+ userdata.avatar !== null &&
+ userdata.avatar.length > 0
+ ? userdata.avatar
+ : "";
+
+ const avatarMenu = (
+
+ {
+ setAnchorElAvatar(event.currentTarget);
+ }}
+ >
+
+
+
+
+ );
+
+ const listItemStyle = {
+ textAlign: "center",
+ marginTop: "auto",
+ marginBottom: "auto",
+ marginRight: 10,
+ };
+
+ // Handle top bar or something
+ const defaultTop = isCloud ? 0 : 7;
+ const loginTextBrowser = !isLoggedIn ? (
+
+
+
+
+ {
+ if (isCloud) {
+ ReactGA.event({
+ category: "header",
+ action: "home_click",
+ label: "",
+ });
+ }
+ }}
+ >
+
+
+
+
+
+
+
+
+
+
+
+ {isCloud ? (
+
+
+
+
+
+ ) : null}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ) : (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/*
+
+ */}
+
+ Workflows
+
+
+
+
+
+
+
+ {/*
+
+ */}
+
+ Apps
+
+
+
+
+ {/*
+
+
+ Dashboard
+
+
+ */}
+
+
+
+ {/*
+
+ */}
+
+ Docs
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {avatarMenu}
+ {notificationMenu}
+ {/*supportMenu*/}
+ {logoCheck}
+
+
+ {userdata === undefined ||
+ userdata.orgs === undefined ||
+ userdata.orgs === null ||
+ userdata.orgs.length <= 1 ? null : (
+
+
+
+ )}
+
+ {/* Show on cloud, if not suborg and if not customer/pov/internal */}
+ {isCloud &&
+ (userdata.org_status === undefined ||
+ userdata.org_status === null ||
+ userdata.org_status.length === 0) ? (
+
+
+
+
+
+ ) : null}
+
+ {userdata === undefined ||
+ userdata.app_execution_limit === undefined ||
+ userdata.app_execution_usage === undefined ||
+ userdata.app_execution_usage < 1000 ? null : (
+
+ =
+ 0.9
+ ? "#f86a3e"
+ : null,
+ }}
+ onClick={() => {
+ console.log(
+ userdata.appe_execution_usage /
+ userdata.app_execution_limit
+ );
+ if (window.drift !== undefined) {
+ window.drift.api.startInteraction({
+ interactionId: 326905,
+ });
+ navigate("/pricing");
+ } else {
+ console.log(
+ "Couldn't find drift in window.drift and not .drift-open-chat with querySelector: ",
+ window.drift
+ );
+ }
+ }}
+ >
+
+
+ {(
+ (userdata.app_execution_usage /
+ userdata.app_execution_limit) *
+ 100
+ ).toFixed(0)}
+ %
+
+
+
+
+
+ )}
+
+
+
+
+ );
+
+ const loginTextMobile = !isLoggedIn ? (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ About
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ) : (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Logout
+
+
+ {logoCheck}
+
+
+
+
+ );
+
+ //
+ return !isMobile ?
+ isLoggedIn ?
+
+
+ {loginTextBrowser}
+
+
+ :
+
+
+ {loginTextBrowser}
+
+
+ :
+
{loginTextMobile}
+};
+
+export default Header;
diff --git a/frontend/src/components/Oauth2Auth.jsx b/frontend/src/components/Oauth2Auth.jsx
index 204af1c6..3e834341 100755
--- a/frontend/src/components/Oauth2Auth.jsx
+++ b/frontend/src/components/Oauth2Auth.jsx
@@ -93,10 +93,10 @@ const AuthenticationOauth2 = (props) => {
appAuthentication,
setSelectedAction,
setNewAppAuth,
- isCloud,
- autoAuth,
- authButtonOnly,
- isLoggedIn,
+ isCloud,
+ autoAuth,
+ authButtonOnly,
+ isLoggedIn,
} = props;
let navigate = useNavigate();
@@ -125,7 +125,7 @@ const AuthenticationOauth2 = (props) => {
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(
defaultConfigSet ? false : true
);
@@ -297,15 +297,63 @@ const AuthenticationOauth2 = (props) => {
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_secret = client_secret.trim()
oauth_url = oauth_url.trim()
- var resources = "";
- if (scopes !== undefined && (scopes !== null) & (scopes.length > 0)) {
+ var resources = "";
+ if (scopes !== undefined && (scopes !== null) & (scopes.length > 0)) {
console.log("IN scope 1")
if (offlineAccess === true && !scopes.includes("offline_access")) {
@@ -324,26 +372,26 @@ const AuthenticationOauth2 = (props) => {
//console.log("AUTH: ", authenticationType)
//console.log("SCOPES2: ", resources)
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}`;
- // This is to make sure authorization can be handled WITHOUT being logged in,
- // kind of making it act like an api key
- // https://shuffler.io/authorization -> 3rd party integration auth
- const urlParams = new URLSearchParams(window.location.search);
- const userAuth = urlParams.get("authorization");
- if (userAuth !== undefined && userAuth !== null && userAuth.length > 0) {
- console.log("Adding authorization from user side")
- state += `%26authorization%3d${userAuth}`;
- }
+ // This is to make sure authorization can be handled WITHOUT being logged in,
+ // kind of making it act like an api key
+ // https://shuffler.io/authorization -> 3rd party integration auth
+ const urlParams = new URLSearchParams(window.location.search);
+ const userAuth = urlParams.get("authorization");
+ if (userAuth !== undefined && userAuth !== null && userAuth.length > 0) {
+ console.log("Adding authorization from user side")
+ state += `%26authorization%3d${userAuth}`;
+ }
- // Check for org_id
- const orgId = urlParams.get("org_id");
- if (orgId !== undefined && orgId !== null && orgId.length > 0) {
- console.log("Adding org_id from user side")
- state += `%26org_id%3d${orgId}`;
- }
+ // Check for org_id
+ const orgId = urlParams.get("org_id");
+ if (orgId !== undefined && orgId !== null && orgId.length > 0) {
+ console.log("Adding org_id from user side")
+ state += `%26org_id%3d${orgId}`;
+ }
if (oauth_url !== undefined && oauth_url !== null && oauth_url.length > 0) {
state += `%26oauth_url%3d${oauth_url}`;
@@ -363,7 +411,7 @@ const AuthenticationOauth2 = (props) => {
// 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 defaultPrompt = "login"
+ var defaultPrompt = "login"
if (prompt !== undefined && prompt !== null && prompt.length > 0) {
defaultPrompt = prompt
}
@@ -404,9 +452,9 @@ const AuthenticationOauth2 = (props) => {
//alert('"Secure Payment" window closed!');
//
- if (getAppAuthentication !== undefined) {
- getAppAuthentication(true, true, true);
- }
+ if (getAppAuthentication !== undefined) {
+ getAppAuthentication(true, true, true);
+ }
} else {
console.log("Not closed")
}
@@ -798,6 +846,7 @@ const AuthenticationOauth2 = (props) => {
}}
fullWidth
color="primary"
+ label={"Client ID"}
placeholder={"Client ID"}
onChange={(event) => {
setClientId(event.target.value);
@@ -816,20 +865,22 @@ const AuthenticationOauth2 = (props) => {
}}
fullWidth
color="primary"
+ label={"Client Secret"}
placeholder={"Client Secret"}
onChange={(event) => {
setClientSecret(event.target.value);
//authenticationOption.label = event.target.value
}}
/>
+ {allscopes.length === 0 ? null : "Scopes (access rights)"}
{allscopes.length === 0 ? null : (
- Scopes
-
-
- {
- setOfflineAccess(!offlineAccess)
- }}/>
-
-
+
+ {((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 :
+
+
+ {
+ setOfflineAccess(!offlineAccess)
+ }}/>
+
+
+ }
)}
@@ -882,19 +936,14 @@ const AuthenticationOauth2 = (props) => {
variant="contained"
fullWidth
onClick={() => {
- handleOauth2Request(
- clientId,
- clientSecret,
- oauthUrl,
- selectedScopes
- );
+ handleOauth2Request(clientId, clientSecret, oauthUrl, selectedScopes);
}}
color="primary"
>
{buttonClicked ? (
) : (
- "Manually Authenticate"
+ "Authenticate"
)}
diff --git a/frontend/src/components/SearchData.jsx b/frontend/src/components/SearchData.jsx
new file mode 100644
index 00000000..cc9c1551
--- /dev/null
+++ b/frontend/src/components/SearchData.jsx
@@ -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: (
+
{
+ event.preventDefault()
+ }}>
+ {
+ setSearchOpen(false)
+ }} />
+
+ ),
+ */
+
+ return (
+
+ )
+ }
+
+ 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 =
+
+ return (
+
+
+ Workflows
+
+
+
+ {hits.length === 0 ?
+
+ console.log(hits)}>
+
+
+
+
+
+
+ :
+ 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}`
+
+ //
+ return (
+ {
+ //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)
+ }}>
+ {
+ setMouseHoverIndex(index)
+ }}>
+
+ {avatar}
+
+
+
+
+ {appGroup.map((app, index) => {
+ // Putting all this in secondary of ListItemText looked weird.
+ return (
+ {
+ navigate("/apps/" + app.id)
+ }}
+ >
+
+
+
+
+ )
+ })}
+
+
+ {/*
+
+
+
+
+
+ */}
+
+
+ )
+ })
+ }
+
+ {/*
+
+
+
+ See all workflows
+
+
+
+ */}
+
+ )
+ }
+
+ 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 =
+
+ return (
+
+ {/* {
+ setSearchOpen(false)
+ }}>
+
+ */}
+
+ Apps
+
+
+
+ {hits.length === 0 ?
+
+ console.log(hits)}>
+
+
+
+
+
+
+ :
+ 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
+ :
+
+
+ //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
+
+ /*
+ {
+ //handleChipClick
+ }}
+ variant="outlined"
+ color="primary"
+ />
+ */
+ })
+ }
+
+ var parsedUrl = isCloud ? `/apps/${hit.objectID}` : `https://shuffler.io/apps/${hit.objectID}`
+ parsedUrl += `?queryID=${hit.__queryID}`
+
+ return (
+ {
+ 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');
+ }
+ }}>
+ {
+ setMouseHoverIndex(index)
+ }}>
+
+ {avatar}
+
+
+ {/*
+
+
+
+
+
+ */}
+
+
+ )
+ })
+ }
+
+ {/*
+
+
+ See more
+
+
+ */}
+
+ )
+ }
+
+ 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 =
+
+ //console.log(type, hits.length, hits)
+
+ return (
+
+ {/* {
+ setSearchOpen(false)
+ }}>
+
+ */}
+
+ Documentation
+
+ {/*
+ {
+ setSearchOpen(false)
+ }}>
+
+
+ */}
+
+ {hits.length === 0 ?
+
+ console.log(hits)}>
+
+
+
+
+
+
+ :
+ 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
+ :
+
+
+ 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 (
+ {
+ 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)
+ }}>
+ {
+ setMouseHoverIndex(index)
+ }}>
+
+ {avatar}
+
+
+ {/*
+
+
+
+
+
+ */}
+
+
+ )
+ })
+ }
+
+
+ )
+ }
+
+ const CustomSearchBox = connectSearchBox(SearchBox)
+ const CustomAppHits = connectHits(AppHits)
+ const CustomWorkflowHits = connectHits(WorkflowHits)
+ const CustomDocHits = connectHits(DocHits)
+
+ const modalView = (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+
+ const gettingStartData = (
+
+
+
+
+
+ Getting Started
+
+
+
+ { window.location = "/docs"; }} style={{ textDecoration: "none", color: "var(--Paragraph-text, #C8C8C8)", display: "flex" }}>
+ Documentation
+
+
+
+
+
+
+ Onprem Installation
+
+
+
+
+
+
+
+ Explore Usecases
+
+
+
+
+
+ Find public workflows
+
+
+
+
+
+
+
+
+
+
+ Popular searches
+
+
+
+
+ Create Apps
+
+
+
+
+
+ Find Apps
+
+
+
+
+
+ Workflows
+
+
+
+
+
+ Creator
+
+
+
+
+
+
+ {/*
+
+
+
+ Popular searches
+
+
+
+
+ Apps
+
+
+
+ Workflows
+
+
+
+ Creator
+
+
+
+
+ */}
+
+
+
+
+ )
+
+ return (
+
+ {
+ console.log("CLICKED 1")
+ }}>
+
+ {
+ console.log("Click 2")
+ }}/>
+ {modalView}
+
+ {gettingStartData}
+
+ )
+}
+
+export default SearchData;
diff --git a/frontend/src/components/Searchfield.jsx b/frontend/src/components/Searchfield.jsx
index 3bafef61..49c28f64 100644
--- a/frontend/src/components/Searchfield.jsx
+++ b/frontend/src/components/Searchfield.jsx
@@ -1,34 +1,42 @@
-import React, {useState, useEffect, useRef} from 'react';
+import React, { useState, useEffect, useRef } from 'react';
import theme from '../theme.jsx';
import { useNavigate, Link, useParams } from "react-router-dom";
+import SearchBox from "./SearchData.jsx";
import {
- Chip,
- IconButton,
- TextField,
- InputAdornment,
- List,
- Card,
- ListItem,
- ListItemAvatar,
- ListItemText,
- Avatar,
+ Chip,
+ IconButton,
+ TextField,
+ InputAdornment,
+ List,
+ Card,
+ ListItem,
+ Button,
+ Dialog,
+ ListItemAvatar,
+ ListItemText,
+ Avatar,
Typography,
Tooltip,
+ Divider,
+ DialogTitle,
+ DialogContent,
} from '@mui/material';
+import Mousetrap from 'mousetrap';
import {
- AvatarGroup,
+ 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 { 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 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';
+import { HotKeys } from 'react-hotkeys';
// 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",
@@ -36,623 +44,122 @@ const chipStyle = {
const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240")
const SearchField = props => {
- const { serverside, userdata } = props
+ const { serverside, userdata, isMobile, isLoaded, globalUrl, isHeader, isLoggedIn, small, rounded } = props
let navigate = useNavigate();
const borderRadius = 3
const node = useRef()
const [searchOpen, setSearchOpen] = useState(false)
+ const [modalOpen, setModalOpen] = React.useState(false);
const [oldPath, setOldPath] = 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 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)
- }
-
- //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 });
- }
+ return () => {
+ Mousetrap.unbind(['command+k', 'ctrl+k']);
};
- /*
- endAdornment: (
-
{
- event.preventDefault()
- }}>
- {
- setSearchOpen(false)
- }} />
-
- ),
- */
+ }, []);
- return (
-
- )
- }
-
- 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 =
-
- return (
-
-
- Workflows
-
-
-
- {hits.length === 0 ?
-
- console.log(hits)}>
-
-
-
-
-
-
- :
- 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}`
-
- //
- return (
- {
- //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');
- }
- }}>
- {
- setMouseHoverIndex(index)
- }}>
-
- {avatar}
-
-
-
-
- {appGroup.map((app, index) => {
- // Putting all this in secondary of ListItemText looked weird.
- return (
- {
- navigate("/apps/"+app.id)
- }}
- >
-
-
-
-
- )
- })}
-
-
- {/*
-
-
-
-
-
- */}
-
-
- )})
- }
-
- {/*
-
-
-
- See all workflows
-
-
-
- */}
-
- )
- }
-
- 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 =
-
- return (
-
- {
- setSearchOpen(false)
- }}>
-
-
-
- Apps
-
-
-
- {hits.length === 0 ?
-
- console.log(hits)}>
-
-
-
-
-
-
- :
- 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
- :
-
-
- //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
-
- /*
- {
- //handleChipClick
- }}
- variant="outlined"
- color="primary"
- />
- */
- })
- }
-
- var parsedUrl = isCloud ? `/apps/${hit.objectID}` : `https://shuffler.io/apps/${hit.objectID}`
- parsedUrl += `?queryID=${hit.__queryID}`
-
- return (
- {
- 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');
- }
- }}>
- {
- setMouseHoverIndex(index)
- }}>
-
- {avatar}
-
-
- {/*
-
-
-
-
-
- */}
-
-
- )})
- }
-
-
-
-
- See more
-
-
-
-
- )
- }
-
- 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 =
-
- //console.log(type, hits.length, hits)
-
- return (
-
- {
- setSearchOpen(false)
- }}>
-
-
-
- Documentation
-
- {/*
- {
- setSearchOpen(false)
- }}>
-
-
- */}
-
- {hits.length === 0 ?
-
- console.log(hits)}>
-
-
-
-
-
-
- :
- 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
- :
-
-
- 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 (
- {
- 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)
- }}>
- {
- setMouseHoverIndex(index)
- }}>
-
- {avatar}
-
-
- {/*
-
-
-
-
-
- */}
-
-
- )})
- }
-
- {type === "documentation" ?
-
-
- Search by
-
-
-
-
-
+ const fieldWidth = small === true ? 120 : 310
+ const modalView = (
+ // console.log("key:", dataValue.key),
+ //console.log("value:",dataValue.value),
+
- )
- }
-
- const CustomSearchBox = connectSearchBox(SearchBox)
- const CustomAppHits = connectHits(AppHits)
- const CustomWorkflowHits = connectHits(WorkflowHits)
- const CustomDocHits = connectHits(DocHits)
+
+
+
+
+
+
+ {/*
+ Discord
+ */}
+ {/*
+
+
+
+
+
+
+ Search by
+
+
+
+
+ */}
+
+
+
+ );
return (
-
-
{
- console.log("CLICKED")
- }}>
-
-
-
-
-
-
-
-
-
-
-
-
+
+ {modalView}
+
+
+
+ ),
+ endAdornment: (
+
+ )
+ }}
+ variant="standard"
+ autoComplete='off'
+ color="primary"
+ placeholder="Search Apps, Workflows, Docs..."
+ onClick={(event) => {
+ setModalOpen(true)
+ }}
+ limit={5}
+ />
)
}
diff --git a/frontend/src/components/WorkflowTemplatePopup.jsx b/frontend/src/components/WorkflowTemplatePopup.jsx
index 9b283da9..ff20ad36 100644
--- a/frontend/src/components/WorkflowTemplatePopup.jsx
+++ b/frontend/src/components/WorkflowTemplatePopup.jsx
@@ -22,6 +22,7 @@ import {
Check as CheckIcon,
TrendingFlat as TrendingFlatIcon,
Close as CloseIcon,
+ East as EastIcon,
} from '@mui/icons-material';
import WorkflowTemplatePopup2 from "./WorkflowTemplatePopup.jsx";
@@ -36,6 +37,7 @@ const WorkflowTemplatePopup = (props) => {
const [errorMessage, setErrorMessage] = useState("");
const [workflowLoading, setWorkflowLoading] = useState(false);
const [workflow, setWorkflow] = useState({});
+ const [showLoginButton, setShowLoginButton] = useState(false);
const [appAuthentication, setAppAuthentication] = React.useState(undefined);
const [missingSource, setMissingSource] = React.useState(undefined)
@@ -119,7 +121,8 @@ const WorkflowTemplatePopup = (props) => {
const loadAppAuth = () => {
// Check if it exists, and has keys
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
setTimeout(() => {
@@ -350,12 +353,35 @@ const WorkflowTemplatePopup = (props) => {
{errorMessage !== "" ? errorMessage : ""}
+ {showLoginButton ?
+
+
+ Sign up
+
+
+
+ : null}
}
- {isLoggedIn && (missingSource !== undefined || missingDestination !== undefined) ?
-
- {"The following app category is required generate this workflow: "}
+ {(missingSource !== undefined || missingDestination !== undefined) ?
+
+ {"Find relevevant Apps for this Usecase"}
: null}
@@ -502,7 +528,7 @@ const WorkflowTemplatePopup = (props) => {
{parsedTitle}
-
+
{parsedDescription}
diff --git a/frontend/src/views/Admin.jsx b/frontend/src/views/Admin.jsx
index 5f65e35a..9631fd1e 100755
--- a/frontend/src/views/Admin.jsx
+++ b/frontend/src/views/Admin.jsx
@@ -291,8 +291,6 @@ const Admin = (props) => {
//const alert = useAlert();
const handleStatusChange = (event) => {
const { value } = event.target;
- console.log("value: ", value)
-
setSelectedStatus(value);
@@ -331,7 +329,7 @@ const Admin = (props) => {
var your_apps = "- Connecting "
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.cases.name !== undefined && org.security_framework.cases.name !== null && org.security_framework.cases.name !== "") {
@@ -400,7 +398,7 @@ const Admin = (props) => {
// Remove comma
- subject += "?"
+ //subject += "?"
your_apps = your_apps.substring(0, your_apps.length - 2)
}
@@ -432,12 +430,25 @@ const Admin = (props) => {
var admins = ""
// Loop users
+ var lastLogin = 0
for (var i = 0; i < users.length; i++) {
+ if (users[i].username.includes("shuffler")) {
+ continue
+ }
+
if (users[i].role === "admin") {
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
admins = admins.substring(0, admins.length - 1)
@@ -452,15 +463,24 @@ const Admin = (props) => {
// Get drift username from userdata.username before @ in email
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
${your_apps}
- Configuring and authenticating your apps%0D%0A
${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}`
}
@@ -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")
}
+ if (responseJson.lead_info.old_lead) {
+ leads.push("old lead")
+ }
+
if (responseJson.lead_info.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(', ')}
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) => (