From daf200149f1ec64b1df6ec2585e974a43ed53c1a Mon Sep 17 00:00:00 2001 From: frikky Date: Tue, 21 Jun 2022 01:38:40 +0200 Subject: [PATCH] Added search engine to open source Shuffle with external links --- backend/go-app/go.mod | 4 +- frontend/package.json | 3 + .../images/logo-algolia-nebula-blue-full.svg | 1 + frontend/src/App.jsx | 6 +- frontend/src/components/AppGrid.jsx | 378 +++++++++++ frontend/src/components/CreatorGrid.jsx | 261 ++++++++ frontend/src/components/DocsGrid.jsx | 340 ++++++++++ frontend/src/components/EditWorkflow.jsx | 331 ++++++++++ frontend/src/components/Header.js | 27 +- frontend/src/components/Searchfield.js | 586 ++++++++++++++++++ frontend/src/components/WorkflowGrid.jsx | 360 +++++++++++ frontend/src/components/WorkflowPaper.jsx | 7 +- frontend/src/views/Search.jsx | 180 ++++++ 13 files changed, 2472 insertions(+), 12 deletions(-) create mode 100644 frontend/public/images/logo-algolia-nebula-blue-full.svg create mode 100644 frontend/src/components/AppGrid.jsx create mode 100644 frontend/src/components/CreatorGrid.jsx create mode 100644 frontend/src/components/DocsGrid.jsx create mode 100644 frontend/src/components/EditWorkflow.jsx create mode 100644 frontend/src/components/Searchfield.js create mode 100644 frontend/src/components/WorkflowGrid.jsx create mode 100644 frontend/src/views/Search.jsx diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index ebc1cfba..9eb2f94b 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -2,7 +2,7 @@ module main go 1.16 -replace github.com/shuffle/shuffle-shared => ../../../shuffle-shared +//replace github.com/shuffle/shuffle-shared => ../../../shuffle-shared //replace github.com/frikky/kin-openapi => ../../../../git/kin-openapi //replace github.com/frikky/go-elasticsearch => ../../../../git/go-elasticsearch @@ -24,7 +24,7 @@ require ( github.com/h2non/filetype v1.1.3 github.com/nirasan/go-oauth-pkce-code-verifier v0.0.0-20170819232839-0fbfe93532da // indirect github.com/satori/go.uuid v1.2.0 - github.com/shuffle/shuffle-shared v0.2.46 + github.com/shuffle/shuffle-shared v0.2.52 go4.org v0.0.0-20201209231011-d4a079459e60 // indirect golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce google.golang.org/api v0.65.0 diff --git a/frontend/package.json b/frontend/package.json index 55e61c59..ff3cdef7 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -19,6 +19,7 @@ "@mui/material": "^5.2.3", "@uiw/react-codemirror": "^3.2.1", "@use-it/interval": "^1.0.0", + "algoliasearch": "^4.13.1", "babel-eslint": "^10.1.0", "class-transformer": "^0.4.0", "create-react-app": "^4.0.3", @@ -58,6 +59,7 @@ "react-dropzone": "^10.1.10", "react-ga": "^2.7.0", "react-iframe": "^1.8.0", + "react-instantsearch-dom": "^6.28.0", "react-json-pretty": "^2.2.0", "react-json-view": "^1.19.1", "react-markdown": "^4.2.2", @@ -69,6 +71,7 @@ "react-shepherd": "^3.3.6", "reactstrap": "^7.1.0", "reaviz": "^12.1.0", + "search-insights": "^2.2.1", "shellwords": "^0.1.1", "simplebar": "^4.2.3", "styled-components": "^4.4.0", diff --git a/frontend/public/images/logo-algolia-nebula-blue-full.svg b/frontend/public/images/logo-algolia-nebula-blue-full.svg new file mode 100644 index 00000000..886c422e --- /dev/null +++ b/frontend/public/images/logo-algolia-nebula-blue-full.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 545e111c..9858567c 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -23,6 +23,7 @@ import Docs from "./views/Docs"; import Introduction from "./views/Introduction"; import SetAuthentication from "./views/SetAuthentication"; import SetAuthenticationSSO from "./views/SetAuthenticationSSO"; +import Search from "./views/Search.jsx"; import LandingPageNew from "./views/LandingpageNew"; import LoginPage from "./views/LoginPage"; @@ -56,7 +57,7 @@ if (globalUrl.includes("githubpreview.dev")) { //globalUrl = globalUrl.replace("3000", "5001") globalUrl = "https://frikky-shuffle-5gvr4xx62w64-5001.githubpreview.dev" } -console.log("global: ", globalUrl) +//console.log("global: ", globalUrl) const App = (message, props) => { @@ -128,7 +129,7 @@ const App = (message, props) => { .then((responseJson) => { var userInfo = {}; if (responseJson.success === true) { - console.log(responseJson); + //console.log("USER: ", responseJson); userInfo = responseJson; setIsLoggedIn(true); @@ -368,6 +369,7 @@ const App = (message, props) => { /> } /> + } /> { + const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs } = props + const isCloud = + window.location.host === "localhost:3002" || + window.location.host === "shuffler.io"; + + const rowHandler = maxRows === undefined || maxRows === null ? 50 : maxRows + const xs = parsedXs === undefined || parsedXs === null ? isMobile ? 6 : 2 : parsedXs + const theme = useTheme(); + //const [apps, setApps] = React.useState([]); + //const [filteredApps, setFilteredApps] = React.useState([]); + const [formMail, setFormMail] = React.useState(""); + const [message, setMessage] = React.useState(""); + const [formMessage, setFormMessage] = React.useState(""); + + const buttonStyle = {borderRadius: 30, height: 50, width: 220, margin: isMobile ? "15px auto 15px auto" : 20, fontSize: 18,} + + const innerColor = "rgba(255,255,255,0.65)" + const borderRadius = 3 + window.title = "Shuffle | Apps | Find and integrate any app" + + const submitContact = (email, message) => { + const data = { + "firstname": "", + "lastname": "", + "title": "", + "companyname": "", + "email": email, + "phone": "", + "message": message, + } + + const errorMessage = "Something went wrong. Please contact frikky@shuffler.io directly." + + fetch(globalUrl+"/api/v1/contact", { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(data), + }) + .then(response => response.json()) + .then(response => { + if (response.success === true) { + setFormMessage(response.reason) + //alert.info("Thanks for submitting!") + } else { + setFormMessage(errorMessage) + } + + setFormMail("") + setMessage("") + }) + .catch(error => { + setFormMessage(errorMessage) + console.log(error) + }); + } + + const SearchBox = ({currentRefinement, refine, isSearchStalled} ) => { + useEffect(() => { + if (window !== undefined && window.location !== undefined && window.location.search !== undefined && window.location.search !== null) { + const urlSearchParams = new URLSearchParams(window.location.search) + const params = Object.fromEntries(urlSearchParams.entries()) + const foundQuery = params["q"] + if (foundQuery !== null && foundQuery !== undefined) { + console.log("Got query: ", foundQuery) + refine(foundQuery) + } + } + }, []) + + return ( +
+ + + + ), + }} + autoComplete='off' + type="search" + color="primary" + defaultValue={currentRefinement} + placeholder="Find Apps..." + id="shuffle_search_field" + onChange={(event) => { + refine(event.currentTarget.value) + }} + limit={5} + /> + {/*isSearchStalled ? 'My search is stalled' : ''*/} + + ) + } + + var workflowDelay = -50 + const Hits = ({ hits, insights }) => { + const [mouseHoverIndex, setMouseHoverIndex] = useState(-1) + var counted = 0 + + //console.log(hits) + //var curhits = hits + //if (hits.length > 0 && defaultApps.length === 0) { + // setDefaultApps(hits) + //} + + //const [defaultApps, setDefaultApps] = React.useState([]) + //console.log(hits) + //if (hits.length > 0 && hits.length !== innerHits.length) { + // setInnerHits(hits) + //} + + return ( + + {hits.map((data, index) => { + + workflowDelay += 50 + + const paperStyle = { + backgroundColor: index === mouseHoverIndex ? "rgba(255,255,255,0.8)" : theme.palette.inputColor, + color: index === mouseHoverIndex ? theme.palette.inputColor : "rgba(255,255,255,0.8)", + border: `1px solid ${innerColor}`, + padding: 15, + cursor: "pointer", + position: "relative", + minHeight: 116, + } + + if (counted === 12/xs*rowHandler) { + return null + } + + counted += 1 + var parsedname = "" + for (var key = 0; key < data.name.length; key++) { + var character = data.name.charAt(key) + if (character === character.toUpperCase()) { + //console.log(data.name[key], data.name[key+1]) + if (data.name.charAt(key+1) !== undefined && data.name.charAt(key+1) === data.name.charAt(key+1).toUpperCase()) { + } else { + parsedname += " " + } + } + + parsedname += character + } + + parsedname = (parsedname.charAt(0).toUpperCase()+parsedname.substring(1)).replaceAll("_", " ") + + return ( + + + + { + setMouseHoverIndex(index) + /* + ReactGA.event({ + category: "app_grid_view", + action: `search_bar_click`, + label: "", + }) + */ + }} onMouseOut={() => { + setMouseHoverIndex(-1) + }} onClick={() => { + if (isCloud) { + ReactGA.event({ + category: "app_grid_view", + action: `app_${parsedname}_${data.id}_click`, + label: "", + }) + } + + console.log(searchClient) + //const searchClient = algoliasearch("L55H18ZINA", "a19be455e7e75ee8f20a93d26b9fc6d6") + aa('init', { + appId: searchClient.appId, + apiKey: searchClient.transporter.queryParameters["x-algolia-api-key"] + }) + + console.log("DATA: ", data) + + const timestamp = new Date().getTime() + console.log("Timestamp: ", timestamp) + + aa('sendEvents', [ + { + eventType: 'click', + eventName: 'Product Clicked', + index: 'appsearch', + objectIDs: [data.objectID], + timestamp: timestamp, + queryID: data.__queryID, + positions: [data.__position], + } + ]) + + }}> + + {data.name} + +
+ {index === mouseHoverIndex || showName === true ? + parsedname + : + null + } + {data.generated ? + + {data.invalid ? + + : + + } + + : + + + + } + + + + + ) + })} + + ) + } + + const CustomSearchBox = connectSearchBox(SearchBox) + const CustomHits = connectHits(Hits) + //const CustomHits = connectHitInsights(aa)(Hits) + const selectButtonStyle = { + minWidth: 150, + maxWidth: 150, + minHeight: 50, + } + + return ( +
+ {/* +
+ +
+ */} +
+ +
+ +
+ + +
+ {showSuggestion === true ? +
+ + Can't find what you're looking for? + +
+ setFormMail(e.target.value)} + /> + setMessage(e.target.value)} + /> +
+ + {formMessage} +
+ : null + } + + + + Search by + + + Algolia logo + + +
+
+ ) +} + +export default AppGrid; diff --git a/frontend/src/components/CreatorGrid.jsx b/frontend/src/components/CreatorGrid.jsx new file mode 100644 index 00000000..bc9bad64 --- /dev/null +++ b/frontend/src/components/CreatorGrid.jsx @@ -0,0 +1,261 @@ +import React, { useEffect, useState } from 'react'; + +import ReactGA from 'react-ga'; +import { useTheme } from '@material-ui/core/styles'; +import {Link} from 'react-router-dom'; + +import { Search as SearchIcon, CloudQueue as CloudQueueIcon, Code as CodeIcon } from '@material-ui/icons'; + +import algoliasearch from 'algoliasearch/lite'; +import { InstantSearch, Configure, connectSearchBox, connectHits } from 'react-instantsearch-dom'; +import { + Grid, + Paper, + TextField, + ButtonBase, + InputAdornment, + Typography, + Button, + Tooltip, + Card, + Box, + CardContent, + IconButton, + Zoom, + CardMedia, + CardActionArea, +} from '@material-ui/core'; + +import { + SkipNext as SkipNextIcon, + SkipPrevious as SkipPreviousIcon, + PlayArrow as PlayArrowIcon, +} from "@material-ui/icons"; + +import WorkflowPaper from "../components/WorkflowPaper.jsx" + +const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240") +const CreatorGrid = props => { + const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs } = props + const rowHandler = maxRows === undefined || maxRows === null ? 50 : maxRows + const xs = parsedXs === undefined || parsedXs === null ? isMobile ? 6 : 2 : parsedXs + const theme = useTheme(); + //const [apps, setApps] = React.useState([]); + //const [filteredApps, setFilteredApps] = React.useState([]); + const [formMail, setFormMail] = React.useState(""); + const [message, setMessage] = React.useState(""); + const [formMessage, setFormMessage] = React.useState(""); + + const buttonStyle = {borderRadius: 30, height: 50, width: 220, margin: isMobile ? "15px auto 15px auto" : 20, fontSize: 18,} + + const innerColor = "rgba(255,255,255,0.65)" + const borderRadius = 3 + window.title = "Shuffle | Workflows | Discover your use-case" + + const submitContact = (email, message) => { + const data = { + "firstname": "", + "lastname": "", + "title": "", + "companyname": "", + "email": email, + "phone": "", + "message": message, + } + + const errorMessage = "Something went wrong. Please contact frikky@shuffler.io directly." + + fetch(globalUrl+"/api/v1/contact", { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(data), + }) + .then(response => response.json()) + .then(response => { + if (response.success === true) { + setFormMessage(response.reason) + //alert.info("Thanks for submitting!") + } else { + setFormMessage(errorMessage) + } + + setFormMail("") + setMessage("") + }) + .catch(error => { + setFormMessage(errorMessage) + console.log(error) + }); + } + + // value={currentRefinement} + const SearchBox = ({currentRefinement, refine, isSearchStalled} ) => { + useEffect(() => { + if (window !== undefined && window.location !== undefined && window.location.search !== undefined && window.location.search !== null) { + const urlSearchParams = new URLSearchParams(window.location.search) + const params = Object.fromEntries(urlSearchParams.entries()) + const foundQuery = params["q"] + if (foundQuery !== null && foundQuery !== undefined) { + refine(foundQuery) + } + } + }, []) + + return ( +
+ + + + ), + }} + autoComplete='off' + type="search" + color="primary" + value={currentRefinement} + placeholder="Find Creators..." + id="shuffle_search_field" + onChange={(event) => { + refine(event.currentTarget.value) + }} + limit={5} + /> + {/*isSearchStalled ? 'My search is stalled' : ''*/} + + ) + } + + const paperAppContainer = { + display: "flex", + flexWrap: "wrap", + alignContent: "space-between", + marginTop: 5, + } + + var workflowDelay = -50 + const Hits = ({ hits }) => { + const [mouseHoverIndex, setMouseHoverIndex] = useState(-1) + var counted = 0 + + return ( + + {hits.map((data, index) => { + workflowDelay += 50 + + return ( + + + + + + + {data.username} + + + {"Creator + + + + + + ) + })} + + ) + } + + const CustomSearchBox = connectSearchBox(SearchBox) + const CustomHits = connectHits(Hits) + + return ( +
+ + +
+ +
+ +
+ {showSuggestion === true ? +
+ + Can't find what you're looking for? + +
+ setFormMail(e.target.value)} + /> + setMessage(e.target.value)} + /> +
+ + {formMessage} +
+ : null + } + + + + Search by + + + Algolia logo + + +
+ ) +} + +export default CreatorGrid; diff --git a/frontend/src/components/DocsGrid.jsx b/frontend/src/components/DocsGrid.jsx new file mode 100644 index 00000000..a2aa63c3 --- /dev/null +++ b/frontend/src/components/DocsGrid.jsx @@ -0,0 +1,340 @@ +import React, {useEffect, useState} from 'react'; + +import ReactGA from 'react-ga'; +import { useTheme } from '@material-ui/core/styles'; +import {Link} from 'react-router-dom'; + +import { Search as SearchIcon, CloudQueue as CloudQueueIcon, Code as CodeIcon } from '@material-ui/icons'; + +import algoliasearch from 'algoliasearch/lite'; +import { InstantSearch, Configure, connectSearchBox, connectHits } from 'react-instantsearch-dom'; +import { + Zoom, + Grid, + Paper, + TextField, + ButtonBase, + InputAdornment, + Typography, + Button, + Tooltip +} from '@material-ui/core'; + +const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240") +const DocsGrid = props => { + const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs } = props + const rowHandler = maxRows === undefined || maxRows === null ? 50 : maxRows + const xs = parsedXs === undefined || parsedXs === null ? isMobile ? 6 : 2 : parsedXs + const theme = useTheme(); + //const [apps, setApps] = React.useState([]); + //const [filteredApps, setFilteredApps] = React.useState([]); + const [formMail, setFormMail] = React.useState(""); + const [message, setMessage] = React.useState(""); + const [formMessage, setFormMessage] = React.useState(""); + + const buttonStyle = {borderRadius: 30, height: 50, width: 220, margin: isMobile ? "15px auto 15px auto" : 20, fontSize: 18,} + + const innerColor = "rgba(255,255,255,0.65)" + const borderRadius = 3 + window.title = "Shuffle | Apps | Find and integrate any app" + + const submitContact = (email, message) => { + const data = { + "firstname": "", + "lastname": "", + "title": "", + "companyname": "", + "email": email, + "phone": "", + "message": message, + } + + const errorMessage = "Something went wrong. Please contact frikky@shuffler.io directly." + + fetch(globalUrl+"/api/v1/contact", { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(data), + }) + .then(response => response.json()) + .then(response => { + if (response.success === true) { + setFormMessage(response.reason) + //alert.info("Thanks for submitting!") + } else { + setFormMessage(errorMessage) + } + + setFormMail("") + setMessage("") + }) + .catch(error => { + setFormMessage(errorMessage) + console.log(error) + }); + } + + const SearchBox = ({currentRefinement, refine, isSearchStalled} ) => { + useEffect(() => { + if (window !== undefined && window.location !== undefined && window.location.search !== undefined && window.location.search !== null) { + const urlSearchParams = new URLSearchParams(window.location.search) + const params = Object.fromEntries(urlSearchParams.entries()) + const foundQuery = params["q"] + if (foundQuery !== null && foundQuery !== undefined) { + console.log("Got query: ", foundQuery) + refine(foundQuery) + } + } + }, []) + + return ( +
+ + + + ), + }} + autoComplete='off' + type="search" + color="primary" + defaultValue={currentRefinement} + placeholder="Search our Documentation..." + id="shuffle_search_field" + onChange={(event) => { + refine(event.currentTarget.value) + }} + limit={5} + /> + {/*isSearchStalled ? 'My search is stalled' : ''*/} + + ) + } + + var workflowDelay = -50 + const Hits = ({ hits }) => { + const [mouseHoverIndex, setMouseHoverIndex] = useState(-1) + var counted = 0 + //console.log(hits) + //var curhits = hits + //if (hits.length > 0 && defaultApps.length === 0) { + // setDefaultApps(hits) + //} + + //const [defaultApps, setDefaultApps] = React.useState([]) + //console.log(hits) + //if (hits.length > 0 && hits.length !== innerHits.length) { + // setInnerHits(hits) + //} + + return ( + + {hits.map((data, index) => { + workflowDelay += 50 + + const paperStyle = { + backgroundColor: index === mouseHoverIndex ? "rgba(255,255,255,0.8)" : theme.palette.inputColor, + color: index === mouseHoverIndex ? theme.palette.inputColor : "rgba(255,255,255,0.8)", + border: `1px solid ${innerColor}`, + padding: 15, + cursor: "pointer", + position: "relative", + minHeight: 116, + } + + if (counted === 12/xs*rowHandler) { + return null + } + + counted += 1 + var parsedname = "" + for (var key = 0; key < data.name.length; key++) { + var character = data.name.charAt(key) + if (character === character.toUpperCase()) { + //console.log(data.name[key], data.name[key+1]) + if (data.name.charAt(key+1) !== undefined && data.name.charAt(key+1) === data.name.charAt(key+1).toUpperCase()) { + } else { + parsedname += " " + } + } + + parsedname += character + } + + parsedname = (parsedname.charAt(0).toUpperCase()+parsedname.substring(1)).replaceAll("_", " ") + + return ( + + + + { + setMouseHoverIndex(index) + /* + ReactGA.event({ + category: "app_grid_view", + action: `search_bar_click`, + label: "", + }) + */ + }} onMouseOut={() => { + setMouseHoverIndex(-1) + }} onClick={() => { + ReactGA.event({ + category: "docs_grid_view", + action: `docs_${parsedname}_${data.id}_click`, + label: "", + }) + }}> + + {data.name} + +
+ {index === mouseHoverIndex || showName === true ? + parsedname + : + null + } + {data.generated ? + + {data.invalid ? + + : + + } + + : + + + + } + + + + + ) + })} + + ) + } + + const CustomSearchBox = connectSearchBox(SearchBox) + const CustomHits = connectHits(Hits) + const selectButtonStyle = { + minWidth: 150, + maxWidth: 150, + minHeight: 50, + } + + return ( +
+ {/* +
+ +
+ */} +
+ +
+ +
+ + +
+ {showSuggestion === true ? +
+ + Can't find what you're looking for? + +
+ setFormMail(e.target.value)} + /> + setMessage(e.target.value)} + /> +
+ + {formMessage} +
+ : null + } + + + + Search by + + + Algolia logo + + +
+
+ ) +} + +export default DocsGrid; diff --git a/frontend/src/components/EditWorkflow.jsx b/frontend/src/components/EditWorkflow.jsx new file mode 100644 index 00000000..786fc3b5 --- /dev/null +++ b/frontend/src/components/EditWorkflow.jsx @@ -0,0 +1,331 @@ +import React, { useEffect, useContext } from "react"; +import theme from '../theme'; +import { isMobile } from "react-device-detect" +import ChipInput from "material-ui-chip-input"; + +import { + Badge, + Avatar, + Grid, + InputLabel, + Select, + ListSubheader, + Paper, + Tooltip, + Divider, + Button, + TextField, + FormControl, + IconButton, + Menu, + MenuItem, + FormControlLabel, + Chip, + Switch, + Typography, + Zoom, + CircularProgress, + Dialog, + DialogTitle, + DialogActions, + DialogContent, + OutlinedInput, + Checkbox, + ListItemText, +} from "@material-ui/core"; + +import { + ExpandLess as ExpandLessIcon, + ExpandMore as ExpandMoreIcon, + Publish as PublishIcon, +} from "@material-ui/icons"; + +const EditWorkflow = (props) => { + const { workflow, setWorkflow, modalOpen, setModalOpen, showUpload, usecases, } = props + + const [submitLoading, setSubmitLoading] = React.useState(false); + const [selectedUsecases, setSelectedUsecases] = React.useState([]); + const [showMoreClicked, setShowMoreClicked] = React.useState(false); + const [innerWorkflow, setInnerWorkflow] = React.useState(workflow) + const [_, setUpdate] = React.useState(""); // Used for rendering, don't remove + const [newWorkflowTags, setNewWorkflowTags] = React.useState(workflow.tags !== undefined && workflow.tags !== null ? JSON.parse(JSON.stringify(workflow.tags)) : []) + + if (workflow === undefined || workflow.id === undefined || setWorkflow === undefined || modalOpen !== true) { + return null + } + + var upload = ""; + var total_count = 0 + + return ( + { + setModalOpen(false); + }} + PaperProps={{ + style: { + backgroundColor: theme.palette.surfaceColor, + color: "white", + minWidth: isMobile ? "90%" : "800px", + maxWidth: isMobile ? "90%" : "800px", + }, + }} + > + +
+ {workflow.id !== undefined ? "Editing" : "New"} workflow + {showUpload === true ? +
+ + + +
+ : null} +
+
+ + + { + innerWorkflow.name = event.target.value + }} + InputProps={{ + style: { + color: "white", + }, + }} + color="primary" + placeholder="Name" + required + margin="dense" + defaultValue={innerWorkflow.name} + autoFocus + fullWidth + /> + { + innerWorkflow.description = event.target.value + setInnerWorkflow(innerWorkflow) + }} + InputProps={{ + style: { + color: "white", + }, + }} + color="primary" + defaultValue={innerWorkflow.description} + placeholder="Description" + multiline + margin="dense" + fullWidth + /> +
+ { + newWorkflowTags.push(chip); + setNewWorkflowTags(newWorkflowTags); + }} + onDelete={(chip, index) => { + newWorkflowTags.splice(index, 1); + setNewWorkflowTags(newWorkflowTags); + }} + /> + {usecases !== null && usecases !== undefined && usecases.length > 0 ? + + Usecases + + + : null} +
+ + {showMoreClicked === true ? + + { + if (event.target.value.toLowerCase() === "test") { + innerWorkflow.status = "test" + } else if (event.target.value.toLowerCase() === "production" || event.target.value.toLowerCase() === "prod") { + innerWorkflow.status = "production" + } else { + innerWorkflow.status = "test" + } + + setInnerWorkflow(innerWorkflow) + }} + InputProps={{ + style: { + color: "white", + }, + }} + color="primary" + defaultValue={innerWorkflow.status} + placeholder="The status of the workflow. Can be test or production." + rows="1" + margin="dense" + fullWidth + /> + { + innerWorkflow.blogpost = event.target.value + setInnerWorkflow(innerWorkflow) + }} + InputProps={{ + style: { + color: "white", + }, + }} + color="primary" + defaultValue={innerWorkflow.blogpost} + placeholder="A blogpost or other reference for how this work workflow was built, and what it's for." + rows="1" + margin="dense" + fullWidth + /> + { + innerWorkflow.video = event.target.value + setInnerWorkflow(innerWorkflow) + }} + InputProps={{ + style: { + color: "white", + }, + }} + color="primary" + defaultValue={innerWorkflow.video} + placeholder="A youtube or loom link to the video" + rows="1" + margin="dense" + fullWidth + /> + { + innerWorkflow.default_return_value = event.target.value + setInnerWorkflow(innerWorkflow) + }} + InputProps={{ + style: { + color: "white", + }, + }} + color="primary" + defaultValue={innerWorkflow.default_return_value} + placeholder="Default return value (used for Subflows if the subflow fails)" + rows="3" + multiline + margin="dense" + fullWidth + /> + + : null} + + { + setShowMoreClicked(!showMoreClicked); + }} + > + {showMoreClicked ? : } + + + +
+ + + + +
+
+ ) +} + +export default EditWorkflow; diff --git a/frontend/src/components/Header.js b/frontend/src/components/Header.js index 7a7ae609..82696474 100644 --- a/frontend/src/components/Header.js +++ b/frontend/src/components/Header.js @@ -39,6 +39,7 @@ import { } from "@mui/icons-material"; //import LogoutIcon from '@mui/icons-material/Logout'; import { useAlert } from "react-alert"; +import SearchField from '../components/Searchfield' const hoverColor = "#f85a3e"; const hoverOutColor = "#e8eaf6"; @@ -506,7 +507,7 @@ const Header = (props) => { //
const loginTextBrowser = !isLoggedIn ? (
- +
{ -
+ {!isLoaded ? null : + userdata.chat_disabled === true ? null : +
+ +
+ } +
{
) : (
-
+
- +
{
- +
{ */} - +
{ */}
+ {!isLoaded ? null : + userdata.chat_disabled === true ? null : +
+ +
+ }
{avatarMenu} {notificationMenu} diff --git a/frontend/src/components/Searchfield.js b/frontend/src/components/Searchfield.js new file mode 100644 index 00000000..502b16ec --- /dev/null +++ b/frontend/src/components/Searchfield.js @@ -0,0 +1,586 @@ +import React, {useState, useEffect, useRef} from 'react'; + +import { useNavigate, Link, useParams } from "react-router-dom"; +import { useTheme } from '@material-ui/core/styles'; +import SearchIcon from '@material-ui/icons/Search'; + +import { + Chip, + IconButton, + TextField, + InputAdornment, + List, + Card, + ListItem, + ListItemAvatar, + ListItemText, + Avatar, + Typography, + Tooltip, +} from '@material-ui/core'; + +import { + AvatarGroup, +} from "@mui/material" + +import {Close as CloseIcon, Folder as FolderIcon, Polymer as PolymerIcon, LibraryBooks as LibraryBooksIcon} from '@material-ui/icons' + +import algoliasearch from 'algoliasearch/lite'; +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 searchClient = algoliasearch("WIC466KTV8", "fff61b6ae3802ec2b47fed9ab50b532e") +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 searchClient = algoliasearch("L55H18ZINA", "a19be455e7e75ee8f20a93d26b9fc6d6") +const SearchField = props => { + const { serverside, } = props + const theme = useTheme(); + let navigate = useNavigate(); + const borderRadius = 3 + const node = useRef() + const [searchOpen, setSearchOpen] = useState(false) + const [oldPath, setOldPath] = useState("") + + 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, } ) => { + + /* + endAdornment: ( + { + event.preventDefault() + }}> + { + setSearchOpen(false) + }} /> + + ), + */ + + return ( +