diff --git a/.env b/.env index 4bd36483..f574d03d 100755 --- a/.env +++ b/.env @@ -59,9 +59,9 @@ SHUFFLE_ORBORUS_STARTUP_DELAY= # Used for setting up a startup delay for Orbor SHUFFLE_SKIPSSL_VERIFY=true IS_KUBERNETES=false # Used for controlling if the environment should run in kubernetes or not -SHUFFLE_BASE_IMAGE_NAME=shuffle -SHUFFLE_BASE_IMAGE_REGISTRY=ghcr.io -SHUFFLE_BASE_IMAGE_TAG_SUFFIX="-1.3.1" +#SHUFFLE_BASE_IMAGE_NAME=shuffle +#SHUFFLE_BASE_IMAGE_REGISTRY=ghcr.io +#SHUFFLE_BASE_IMAGE_TAG_SUFFIX="-1.3.1" # The eth0 interface inside a container corresponds # to the virtual Ethernet interface that connects diff --git a/backend/app_sdk/app_base.py b/backend/app_sdk/app_base.py index 9e90ec62..538d3afe 100755 --- a/backend/app_sdk/app_base.py +++ b/backend/app_sdk/app_base.py @@ -466,8 +466,8 @@ class AppBase: "success": True, "status": request.status_code, "url": request.url, - "headers": parsedheaders, "body": jsondata, + "headers": parsedheaders, "cookies":cookies, }) except Exception as e: diff --git a/frontend/src/components/Header.jsx b/frontend/src/components/Header.jsx index dbf68fe2..4f1d47ef 100644 --- a/frontend/src/components/Header.jsx +++ b/frontend/src/components/Header.jsx @@ -615,7 +615,7 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho
- +
diff --git a/frontend/src/components/NewHeader.jsx b/frontend/src/components/NewHeader.jsx index 558d0c72..c0cf173d 100644 --- a/frontend/src/components/NewHeader.jsx +++ b/frontend/src/components/NewHeader.jsx @@ -741,7 +741,7 @@ const Header = (props) => { >
- +
- +
{ }, "fields": parsedFields, "type": "oauth2-app", - "reference_workflow": workflowId, + //"reference_workflow": workflowId, } if (setNewAppAuth !== undefined) { diff --git a/frontend/src/components/ParsedAction.jsx b/frontend/src/components/ParsedAction.jsx index 68366fe4..7d9f2ac0 100755 --- a/frontend/src/components/ParsedAction.jsx +++ b/frontend/src/components/ParsedAction.jsx @@ -1644,13 +1644,11 @@ const ParsedAction = (props) => { multiline={data.name.startsWith("${") && data.name.endsWith("}") ? true : multiline} helperText={returnHelperText(data.name, data.value)} onClick={() => { - console.log("Clicked field: ", clickedFieldId, data.name) /* setExpansionModalOpen(false); */ if (setScrollConfig !== undefined && scrollConfig !== null && scrollConfig !== undefined && scrollConfig.selected !== clickedFieldId) { - console.log("IN SCROLL CONFIG!") scrollConfig.selected = clickedFieldId setScrollConfig(scrollConfig) @@ -3737,6 +3735,11 @@ const ParsedAction = (props) => { break } } + + // Check if it starts with "Get List" and method is "Get" + if (params.inputProps.value.startsWith("Get List")) { + console.log("Get List") + } } return ( diff --git a/frontend/src/components/SearchData.jsx b/frontend/src/components/SearchData.jsx index 10d58c7a..148b5bab 100644 --- a/frontend/src/components/SearchData.jsx +++ b/frontend/src/components/SearchData.jsx @@ -2,6 +2,7 @@ import React, { useState, useEffect, useRef } from 'react'; import theme from '../theme.jsx'; import { useNavigate, Link, useParams } from "react-router-dom"; +import { toast } from "react-toastify" import { Chip, @@ -41,7 +42,7 @@ const chipStyle = { const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240") const SearchData = props => { - const { serverside, userdata, setModalOpen, modalOpen } = props + const { serverside, globalUrl, userdata, setModalOpen, modalOpen } = props let navigate = useNavigate(); const borderRadius = 3 const node = useRef() @@ -326,6 +327,57 @@ const SearchData = props => { ) } + const activateApp = (name, appid, type) => { + if (globalUrl === undefined || globalUrl === null) { + console.log(`Global URL not set`) + return + } + + if (name === undefined || name === null) { + name = "" + } + + name = name.replaceAll("_", " ") + + if (userdata === undefined || userdata === null || userdata.id === undefined) { + toast(`You need to be logged in to activate the ${name} app. Redirecting`) + + setTimeout(() => { + navigate(`/register?message=You need to be logged in to use the ${name} app.`) + }, 500) + return + } + + const url = `${globalUrl}/api/v1/apps/${appid}/${type}` + + fetch(url, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + }, + credentials: "include", + }) + .then((response) => { + if (response.status !== 200) { + toast(`Failed to ${type} the app for your organization. Please try again or contact support@shuffler.io`) + } + + return response.json() + }) + .then((responseJson) => { + if (responseJson.success === false) { + toast(`Failed to ${type} the app for your organization. Please try again or contact support@shuffler.io for more info`) + } else { + toast(`App successfully ${type}d. Please refresh the page to use it.`) + } + }) + .catch(error => { + //toast(error.toString()) + console.log("Activate app error: ", error.toString()) + }); + } + const AppHits = ({ hits }) => { const [mouseHoverIndex, setMouseHoverIndex] = useState(0) @@ -431,7 +483,6 @@ const SearchData = props => { return ( { setSearchOpen(true) - setModalOpen(false) aa('init', { appId: searchClient.appId, @@ -474,6 +525,29 @@ const SearchData = props => { */} + ) diff --git a/frontend/src/components/Searchfield.jsx b/frontend/src/components/Searchfield.jsx index 4e292661..1652c55d 100644 --- a/frontend/src/components/Searchfield.jsx +++ b/frontend/src/components/Searchfield.jsx @@ -98,7 +98,7 @@ const SearchField = props => {
: null} - + diff --git a/frontend/src/components/ShuffleCodeEditor.jsx b/frontend/src/components/ShuffleCodeEditor.jsx index 0c86d0ee..15aec446 100644 --- a/frontend/src/components/ShuffleCodeEditor.jsx +++ b/frontend/src/components/ShuffleCodeEditor.jsx @@ -236,8 +236,6 @@ const CodeEditor = (props) => { //var newitem = JSON.parse(base); var newitem = validateJson(base).result - - to_be_copied = "$" + base_node_name.toLowerCase().replaceAll(" ", "_"); for (let copykey in copy.namespace) { if (copy.namespace[copykey].includes("Results for")) { @@ -825,7 +823,7 @@ const CodeEditor = (props) => { console.log("ERR IN INPUT: ", e) } - console.log("Got output for: ", fullpath, new_input, actionlist[k].example, typeof new_input) + //console.log("Got output for: ", fullpath, new_input, actionlist[k].example, typeof new_input) if (typeof new_input === "object") { new_input = JSON.stringify(new_input) @@ -1078,114 +1076,122 @@ const CodeEditor = (props) => { */} { isFileEditor ? null :
- - { - setAnchorEl(null); - }} - MenuListProps={{ - 'aria-labelledby': 'basic-button', - }} - > - {liquidFilters.map((item, index) => { - return ( - { - handleClick(item) - }}>{item.name} - ) - })} - - - { - setAnchorEl2(null); - }} - MenuListProps={{ - 'aria-labelledby': 'basic-button', - }} - > - {mathFilters.map((item, index) => { - return ( - { - handleClick(item) - }}>{item.name} - ) - })} - - - { - setAnchorEl3(null); - }} - MenuListProps={{ - 'aria-labelledby': 'basic-button', - }} - > - {pythonFilters.map((item, index) => { - return ( - { - handleClick(item) - }}>{item.name} - ) - })} - + {selectedAction.name === "execute_python" ? + + Run Python Code + + : +
+ + { + setAnchorEl(null); + }} + MenuListProps={{ + 'aria-labelledby': 'basic-button', + }} + > + {liquidFilters.map((item, index) => { + return ( + { + handleClick(item) + }}>{item.name} + ) + })} + + + { + setAnchorEl2(null); + }} + MenuListProps={{ + 'aria-labelledby': 'basic-button', + }} + > + {mathFilters.map((item, index) => { + return ( + { + handleClick(item) + }}>{item.name} + ) + })} + + + { + setAnchorEl3(null); + }} + MenuListProps={{ + 'aria-labelledby': 'basic-button', + }} + > + {pythonFilters.map((item, index) => { + return ( + { + handleClick(item) + }}>{item.name} + ) + })} + +
+ } */} - - - - - ); - })} + } + + setMatchingOrganizations(active); + } + }} + > + + + {/**/} + + + + + ); + })}
) : null; @@ -4131,6 +4251,46 @@ If you're interested, please let me know a time that works for you, or set up a
) : null; + const getLogs = async (ip, userId) => { + setLogsLoading(true); + console.log("logs loading: ", logsLoading); + fetch(`${globalUrl}/api/v1/users/${userId}/audit?user_ip=${ip}`, { + mode: "cors", + method: "GET", + credentials: "include", + crossDomain: true, + withCredentials: true, + headers: { + "Content-Type": "application/json; charset=utf-8", + }, + }) + .then((response) => { + return response.json(); + }) + .then((responseJson) => { + console.log("ResponseJSON: ", responseJson); + if (responseJson.success === true) { + setLogs(responseJson.logs); + } else { + if (responseJson.success === false || responseJson.reason !== undefined) { + console.log("Reason given: ", responseJson.reason) + toast("Failed getting logs: " + responseJson.reason) + setLogs([]) + } else { + toast("Failed getting logs"); + } + } + console.log("logs loading now: ", logsLoading); + setLogsLoading(false); + }) + .catch((error) => { + console.log("Error: ", error); + toast("Failed getting logs. Please contact: ", error); + console.log("logs loading now: ", logsLoading); + setLogsLoading(false); + }); + }; + const changeRecommendation = (recommendation, action) => { const data = { action: action, diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index f22ee36b..2f461690 100755 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -17,7 +17,7 @@ import { ToastContainer, toast } from "react-toastify" import { isMobile } from "react-device-detect" import aa from 'search-insights' import Drift from "react-driftjs"; -import ShuffleCodeEditor from "../components/ShuffleCodeEditor.jsx"; +import { CodeHandler, Img, OuterLink, } from "../views/Docs.jsx"; import { InstantSearch, Configure, connectSearchBox, connectHits, Index } from 'react-instantsearch-dom'; import algoliasearch from 'algoliasearch/lite'; @@ -116,17 +116,12 @@ import { AutoAwesome as AutoAwesomeIcon, } from "@mui/icons-material"; - import * as cytoscape from "cytoscape"; import * as edgehandles from "cytoscape-edgehandles"; -//import * as clipboard from "cytoscape-clipboard"; -//import undoRedo from "cytoscape-undo-redo"; -//import cxtmenu from "cytoscape-cxtmenu"; import CytoscapeComponent from "react-cytoscapejs"; - import Draggable from "react-draggable"; - import cytoscapestyle from "../defaultCytoscapeStyle.jsx"; +import ShuffleCodeEditor from "../components/ShuffleCodeEditor.jsx"; import { validateJson, GetIconInfo } from "../views/Workflows.jsx"; import { GetParsedPaths, internalIds, } from "../views/Apps.jsx"; @@ -842,48 +837,6 @@ const AngularWorkflow = (defaultprops) => { }); }; - function OuterLink(props) { - if (props.href.includes("http") || props.href.includes("mailto")) { - return ( - - {props.children} - - ); - } - return ( - - {props.children} - - ); - } - - function Img(props) { - return {props.alt}; - } - - function CodeHandler(props) { - return ( -
-        {props.value}
-      
- ); - } - function Heading(props) { const element = React.createElement( `h${props.level}`, @@ -1021,8 +974,6 @@ const AngularWorkflow = (defaultprops) => { return response.json(); }) .then((responseJson) => { - console.log("GOT A RESPONSE??") - // getWorkflowExecutionCount(id); if (responseJson !== undefined && responseJson !== null && responseJson.executions !== undefined && responseJson.executions !== null) { // - means it's opposite @@ -1035,8 +986,6 @@ const AngularWorkflow = (defaultprops) => { tmpView = execution_id; } - console.log("EXECUTION ID: ", tmpView) - // Compare with currently selected item if (tmpView !== undefined && tmpView !== null && tmpView.length > 0) { // Don't clean up if it's already open @@ -1820,7 +1769,6 @@ const AngularWorkflow = (defaultprops) => { } } - console.log("FOUNDMISSING: ", foundmissing) if (foundmissing) { //toast("This workflow contains a node that requires an execution argument. Please provide one.") setExecutionRequestStarted(false) @@ -2788,11 +2736,17 @@ const AngularWorkflow = (defaultprops) => { if (response.status >= 500) { toast("Something went wrong while loading the workflow. Please reload.") } else { - toast("You don't access to this workflow or loading failed. Redirecting to workflows in a few seconds..") - setTimeout(() => { - window.location.pathname = "/workflows"; - }, 2000); + // Check for execution_id in URL + // don't redirect if it exists + const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search; + var execFound = new URLSearchParams(cursearch).get("execution_id"); + if (execFound === null) { + toast(`You don't access to this workflow or loading failed. Redirecting to workflows in a few seconds..`) + setTimeout(() => { + window.location.pathname = "/workflows"; + }, 2000); + } } } @@ -8518,8 +8472,6 @@ const AngularWorkflow = (defaultprops) => { } if (param.name === "headers") { - console.log("Swap header? For now, yes. File found: ", fileid_found) - if (fileid_found) { newSelectedAction.parameters[paramkey].value = "" newSelectedAction.parameters[paramkey].autocompleted = true @@ -8614,7 +8566,6 @@ const AngularWorkflow = (defaultprops) => { if (workflow.actions !== undefined && workflow.actions !== null && workflow.actions.length > 0) { const foundActionIndex = workflow.actions.findIndex(actiondata => actiondata.id === newSelectedAction.id) - console.log("Found action on index ", foundActionIndex) if (foundActionIndex >= 0) { workflow.actions[foundActionIndex] = newSelectedAction setWorkflow(workflow) @@ -9444,14 +9395,18 @@ const AngularWorkflow = (defaultprops) => { bottom: 10, left: 10, color: "rgba(255,255,255,0.6)", + zIndex: 10000, }} > Conditions can't be used for loops [ .# ]{" "} Learn more @@ -15935,7 +15890,10 @@ const AngularWorkflow = (defaultprops) => { setSelectedResult(data); setCodeModalOpen(true); } else { - toast("Please wait until the workflow is loaded and try again") + toast("Please wait until the workflow is loaded and try again") + setCodeModalOpen(true) + setSelectedResult(data) + } }} > @@ -16197,6 +16155,25 @@ const AngularWorkflow = (defaultprops) => { }} > + + { + e.preventDefault() + }} + > + + + { right: 170, }} onClick={(e) => { - e.preventDefault(); + e.preventDefault() - if (workflowExecutions !== null) { + if (workflowExecutions !== null) { for (let execkey in workflowExecutions) { const execution = workflowExecutions[execkey]; - if (execution.execution_argument.includes("too large")) { - continue - } + if (execution.execution_argument.includes("too large")) { + continue + } const result = execution.results.find((data) => data.status === "SUCCESS" && data.action.id === selectedResult.action.id) - if (result !== undefined) { - const oldstartnode = cy.getElementById(selectedResult.action.id); - if (oldstartnode !== undefined && oldstartnode !== null) { - const foundname = oldstartnode.data("label") - if (foundname !== undefined && foundname !== null) { - result.action.label = foundname - } - } + if (result !== undefined) { + const oldstartnode = cy.getElementById(selectedResult.action.id); + if (oldstartnode !== undefined && oldstartnode !== null) { + const foundname = oldstartnode.data("label") + if (foundname !== undefined && foundname !== null) { + result.action.label = foundname + } + } - setSelectedResult(result); - setUpdate(Math.random()); - break; - } + setSelectedResult(result); + setUpdate(Math.random()); + break; + } } - } + } }} > diff --git a/frontend/src/views/AppCreator.jsx b/frontend/src/views/AppCreator.jsx index 71625903..12e81862 100755 --- a/frontend/src/views/AppCreator.jsx +++ b/frontend/src/views/AppCreator.jsx @@ -264,7 +264,7 @@ export const appCategories = [ "name": "IAM", "color": "#FFC107", "icon": "iam", - "action_labels": ["Reset Password", "Enable user", "Disable user", "Get Identity", "Get Asset", "Search Identity", ], + "action_labels": ["Reset Password", "Enable user", "Disable user", "Get Identity", "Get Asset", "Search Identity", "Get KMS Key",], }, { "name": "Network", "color": "#FFC107", @@ -1240,7 +1240,7 @@ const AppCreator = (defaultprops) => { if (methodvalue.responses.default.content["text/plain"]["schema"]["format"] === "binary" && methodvalue.responses.default.content["text/plain"]["schema"]["type"] === "string") { newaction.example_response = "shuffle_file_download" - } + } } } } @@ -2174,6 +2174,10 @@ const AppCreator = (defaultprops) => { queryitem.name.toLowerCase() == "ssl_verify" || queryitem.name.toLowerCase() == "queries" || queryitem.name.toLowerCase() == "headers" || + queryitem.name.toLowerCase() == "list" || + queryitem.name.toLowerCase() == "dict" || + queryitem.name.toLowerCase() == "str" || + queryitem.name.toLowerCase() == "int" || queryitem.name.toLowerCase() == "access_token") { /* @@ -3748,7 +3752,7 @@ const AppCreator = (defaultprops) => { }} > - +
New action
diff --git a/frontend/src/views/Docs.jsx b/frontend/src/views/Docs.jsx index 37bcec0a..027aad59 100755 --- a/frontend/src/views/Docs.jsx +++ b/frontend/src/views/Docs.jsx @@ -3,10 +3,12 @@ import React, { useEffect, useState } from "react"; import { toast } from 'react-toastify'; import Markdown from 'react-markdown' +import theme from '../theme.jsx'; +import ReactJson from "react-json-view"; +import { isMobile } from "react-device-detect"; import { BrowserView, MobileView } from "react-device-detect"; import { useParams, useNavigate, Link } from "react-router-dom"; -import { isMobile } from "react-device-detect"; -import theme from '../theme.jsx'; +import { validateJson, GetIconInfo } from "../views/Workflows.jsx"; import { Grid, @@ -30,6 +32,7 @@ import { Edit as EditIcon, KeyboardArrowRight as KeyboardArrowRightIcon, ExpandMore as ExpandMoreIcon, + FileCopy as FileCopyIcon } from "@mui/icons-material"; const Body = { @@ -60,6 +63,120 @@ const innerHrefStyle = { textDecoration: "none", }; + +export const CopyToClipboard = (props) => { + const {text, style, onCopy} = props; + const parsedstyle = style !== undefined ? style : { + position: "absolute", + right: 0, + top: -10, + } + + return ( +
+ { + navigator.clipboard.writeText(text); + toast("Copied to clipboard") + }} + > + + +
+ ) +} + +export const OuterLink = (props) => { + if (props.href.includes("http") || props.href.includes("mailto")) { + return ( + + {props.children} + + ); + } + + return ( + + {props.children} + + ); + } + + +export const Img = (props) => { + return {props.alt}; +} + +export const CodeHandler = (props) => { + const propvalue = props.value !== undefined && props.value !== null ? props.value : props.children !== undefined && props.children !== null && props.children.length > 0 ? props.children[0] : "" + + + + const validate = validateJson(propvalue) + + var newprop = propvalue + if (validate.valid === false) { + // Check if https://shuffler.io in the url + // if so, then we change it for the current url + if (propvalue.includes("https://shuffler.io")) { + newprop = propvalue.replace("https://shuffler.io", window.location.origin) + } + + // Check if it contains Bearer APIKEY + // If so, replace apikey + //if (newprop.includes("Bearer APIKEY")) { + // newprop = newprop.replace("Bearer APIKEY", "Bearer API + //} + } + + return ( +
+ {validate.valid === true ? + + : +
+ + {newprop} + + +
+ } +
+ ) +} + const Docs = (defaultprops) => { const { globalUrl, selectedDoc, serverside, serverMobile } = defaultprops; @@ -121,6 +238,123 @@ const Docs = (defaultprops) => { //height: "50vh", }; + const Heading = (props) => { + const element = React.createElement( + `h${props.level}`, + { style: { marginTop: props.level === 1 ? 20 : 50 } }, + props.children + ); + const [hover, setHover] = useState(false); + + var extraInfo = ""; + if (props.level === 1) { + extraInfo = ( +
+
+ {isMobile ? null : ( + + + + + + )} + {isMobile ? null : ( +
+ )} + + {selectedMeta.read_time} minute + {selectedMeta.read_time === 1 ? "" : "s"} to read + +
+
+ {isMobile || + selectedMeta.contributors === undefined || + selectedMeta.contributors === null ? ( + "" + ) : ( +
+ {selectedMeta.contributors.slice(0, 7).map((data, index) => { + return ( + + + {data.url} + + + ); + })} +
+ )} +
+
+ ); + } + + if (extraInfo !== "" && props.level === 1 && props.children !== undefined && props.children !== null && props.children.length > 0) { + if (props.children[0].toLowerCase().includes("privacy") || props.children[0].toLowerCase().includes("terms")) { + extraInfo = "" + } + } + + return ( + { + setHover(true); + }} + > + {props.level !== 1 ? ( + + ) : null} + {element} + {extraInfo} + + ) + } + const SideBar = { minWidth: 300, width: "20%", @@ -382,190 +616,7 @@ const Docs = (defaultprops) => { fontSize: isMobile ? "1.3rem" : "1.1rem", }; - function OuterLink(props) { - if (props.href.includes("http") || props.href.includes("mailto")) { - return ( - - {props.children} - - ); - } - return ( - - {props.children} - - ); - } - - function Img(props) { - return {props.alt}; - } - - function CodeHandler(props) { - //console.log("Codehandler PROPS: ", props) - - const propvalue = props.value !== undefined && props.value !== null ? props.value : props.children !== undefined && props.children !== null && props.children.length > 0 ? props.children[0] : "" - - return ( -
- {propvalue} -
- ); - } - - const Heading = (props) => { - const element = React.createElement( - `h${props.level}`, - { style: { marginTop: props.level === 1 ? 20 : 50 } }, - props.children - ); - const [hover, setHover] = useState(false); - - var extraInfo = ""; - if (props.level === 1) { - extraInfo = ( -
-
- {mobile ? null : ( - - - - - - )} - {mobile ? null : ( -
- )} - - {selectedMeta.read_time} minute - {selectedMeta.read_time === 1 ? "" : "s"} to read - -
-
- {mobile || - selectedMeta.contributors === undefined || - selectedMeta.contributors === null ? ( - "" - ) : ( -
- {selectedMeta.contributors.slice(0, 7).map((data, index) => { - return ( - - - {data.url} - - - ); - })} -
- )} -
-
- ); - } - - if (extraInfo !== "" && props.level === 1 && props.children !== undefined && props.children !== null && props.children.length > 0) { - if (props.children[0].toLowerCase().includes("privacy") || props.children[0].toLowerCase().includes("terms")) { - extraInfo = "" - } - } - - return ( - { - setHover(true); - }} - > - {props.level !== 1 ? ( - - ) : null} - {element} - {/*hover ? {setHover(true)}} style={{cursor: "pointer", display: "inline", }} onClick={() => { - window.location.href += "#hello" - console.log(window.location) - //window.history.pushState('page2', 'Title', '/page2.php'); - //window.history.replaceState('page2', 'Title', '/page2.php'); - }} /> - : "" - */} - {extraInfo} - - ); - }; - //React.createElement("p", {style: {color: "red", backgroundColor: "blue"}}, this.props.paragraph) - - //function unicodeToChar(text) { - // return text.replace(/\\u[\dA-F]{4}/gi, - // function (match) { - // return String.fromCharCode(parseInt(match.replace(/\\u/g, ''), 16)); - // } - // ); - //} + const CustomButton = (props) => { diff --git a/frontend/src/views/Workflows.jsx b/frontend/src/views/Workflows.jsx index 7f78244a..0cff5f0f 100755 --- a/frontend/src/views/Workflows.jsx +++ b/frontend/src/views/Workflows.jsx @@ -484,7 +484,6 @@ export const validateJson = (showResult) => { // This is where we start recursing if (jsonvalid) { // Check fields if they can be parsed too - //console.log("In this window for the data. Should look for list in result! Does recursion.") try { for (const [key, value] of Object.entries(result)) { if (typeof value === "string" && (value.startsWith("{") || value.startsWith("["))) { @@ -1649,6 +1648,7 @@ const Workflows = (props) => { addFilter(e.target.innerHTML); }; + const hasWorkflows = workflows === undefined || workflows === null || workflows.length === 0 const NewWorkflowPaper = () => { const [hover, setHover] = React.useState(false); @@ -1659,17 +1659,17 @@ const Workflows = (props) => { minWidth: paperAppStyle.width, color: innerColor, padding: paperAppStyle.padding, - borderRadius: paperAppStyle.borderRadius, display: "flex", boxSizing: "border-box", position: "relative", - border: `2px solid ${innerColor}`, + border: hasWorkflows ? `2px solid #f85a3e` : `2px solid ${innerColor}`, cursor: "pointer", backgroundColor: hover ? "rgba(39,41,45,0.5)" : "rgba(39,41,45,1)", + borderRadius: paperAppStyle.borderRadius, }; return ( - + { }} > - + + + New Workflow + @@ -3328,8 +3331,8 @@ const Workflows = (props) => {
-
- {!isMobile && usecases !== null && usecases !== undefined && usecases.length > 0 ? +
+ {!isMobile && !hasWorkflows && usecases !== null && usecases !== undefined && usecases.length > 0 ?
{usecases.map((usecase, index) => { //console.log(usecase)