diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index 659f6364..5191a1d0 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -20,7 +20,7 @@ require ( github.com/gorilla/mux v1.8.1 github.com/h2non/filetype v1.1.3 github.com/satori/go.uuid v1.2.0 - github.com/shuffle/shuffle-shared v0.6.40 + github.com/shuffle/shuffle-shared v0.6.46 golang.org/x/crypto v0.22.0 google.golang.org/api v0.176.1 google.golang.org/grpc v1.63.2 diff --git a/backend/go-app/go.sum b/backend/go-app/go.sum index 4c0d4206..13000a5c 100644 --- a/backend/go-app/go.sum +++ b/backend/go-app/go.sum @@ -303,6 +303,8 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= +github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= @@ -443,6 +445,8 @@ github.com/shuffle/shuffle-shared v0.6.27 h1:q4qZD6bGZFIvZ5Y10unGr3N3rZ7OryWyvva github.com/shuffle/shuffle-shared v0.6.27/go.mod h1:rWkh1eWdIx7OqQzJ1+JzF3Hck1X/Ty1WkUtjLrp+CU4= github.com/shuffle/shuffle-shared v0.6.31 h1:MK1SW1pwjIP7hznq+mMlTPM1R3LIOfi1/bUL5xFSg/8= github.com/shuffle/shuffle-shared v0.6.31/go.mod h1:rWkh1eWdIx7OqQzJ1+JzF3Hck1X/Ty1WkUtjLrp+CU4= +github.com/shuffle/shuffle-shared v0.6.46 h1:v/IXc+4V8DCWflGKGgaI37uuGnsylMjqKRylwNoXVrA= +github.com/shuffle/shuffle-shared v0.6.46/go.mod h1:rWkh1eWdIx7OqQzJ1+JzF3Hck1X/Ty1WkUtjLrp+CU4= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= diff --git a/backend/go-app/walkoff.go b/backend/go-app/walkoff.go index 3d417472..8ea1550a 100755 --- a/backend/go-app/walkoff.go +++ b/backend/go-app/walkoff.go @@ -3382,6 +3382,8 @@ func executeSingleAction(resp http.ResponseWriter, request *http.Request) { return } + debugUrl := fmt.Sprintf("/workflows/%s?execution_id=%s", workflowExecution.Workflow.ID, workflowExecution.ExecutionId) + resp.Header().Add("X-Debug-Url", debugUrl) workflowExecution.Priority = 11 environments, err := shuffle.GetEnvironments(ctx, user.ActiveOrg.Id) diff --git a/frontend/src/components/Files.jsx b/frontend/src/components/Files.jsx index 692d2e62..4ad5292a 100644 --- a/frontend/src/components/Files.jsx +++ b/frontend/src/components/Files.jsx @@ -44,9 +44,9 @@ const Files = (props) => { const { globalUrl, userdata, serverside, selectedOrganization, isCloud,isSelectedFiles } = props; const [files, setFiles] = React.useState([]); - const [selectedNamespace, setSelectedNamespace] = React.useState("default"); + const [selectedCategory, setSelectedCategory] = React.useState("default"); const [openFileId, setOpenFileId] = React.useState(false); - const [fileNamespaces, setFileNamespaces] = React.useState([]); + const [fileCategories, setFileCategories] = React.useState([]); const [fileContent, setFileContent] = React.useState(""); const [openEditor, setOpenEditor] = React.useState(false); const [renderTextBox, setRenderTextBox] = React.useState(false); @@ -57,6 +57,7 @@ const Files = (props) => { const [downloadUrl, setDownloadUrl] = React.useState("https://github.com/shuffle/standards") const [downloadBranch, setDownloadBranch] = React.useState("main"); const [downloadFolder, setDownloadFolder] = React.useState("translation_standards"); + const [contentLoading, setContentLoading] = React.useState(false) //const alert = useAlert(); const allowedFileTypes = ["txt", "py", "yaml", "yml","json", "html", "js", "csv", "log", "eml", "msg", "md", "xml", "sh", "bat", "ps1", "psm1", "psd1", "ps1xml", "pssc", "psc1", "response"] @@ -67,8 +68,8 @@ const Files = (props) => { console.log('do validate') console.log("new namespace name->",event.target.value); - fileNamespaces.push(event.target.value); - setSelectedNamespace(event.target.value); + fileCategories.push(event.target.value); + setSelectedCategory(event.target.value); setRenderTextBox(false); } @@ -107,12 +108,13 @@ const Files = (props) => { const getFiles = (namespace) => { var parsedurl = `${globalUrl}/api/v1/files` - if (namespace === undefined || namespace === "default") { + + if (namespace === undefined || namespace === null || namespace === "default") { } else if (namespace !== undefined && namespace !== null && namespace !== "") { parsedurl = `${globalUrl}/api/v1/files/namespaces/${namespace}?ids=true` - } else if (selectedNamespace !== undefined && selectedNamespace !== null && selectedNamespace !== "default" && selectedNamespace !== "") { - parsedurl = `${globalUrl}/api/v1/files/namespaces/${selectedNamespace}?ids=true` + } else if (selectedCategory !== undefined && selectedCategory !== null && selectedCategory !== "default" && selectedCategory !== "") { + parsedurl = `${globalUrl}/api/v1/files/namespaces/${selectedCategory}?ids=true` } fetch(parsedurl, { @@ -149,9 +151,11 @@ const Files = (props) => { setFiles([]); } - if (responseJson.namespaces !== undefined && responseJson.namespaces !== null && (fileNamespaces.length === 0 || responseJson.namespaces.length > fileNamespaces.length)) { - setFileNamespaces(responseJson.namespaces); - } + if (namespace === undefined || namespace === null || namespace === "default") { + if (responseJson.namespaces !== undefined && responseJson.namespaces !== null && (fileCategories.length === 0 || responseJson.namespaces.length > fileCategories.length)) { + setFileCategories(responseJson.namespaces) + } + } }) .catch((error) => { toast(error.toString()); @@ -159,7 +163,21 @@ const Files = (props) => { }; useEffect(() => { - getFiles(selectedNamespace) + getFiles("default") + + setTimeout(() => { + var category = selectedCategory + if (window.location.search.includes("category=")) { + const urlParams = new URLSearchParams(window.location.search) + category = urlParams.get("category") + } + + if (category !== undefined && category !== null && category.length > 0 && category !== "default") { + setSelectedCategory(category) + } + + getFiles(category) + }, 1000) }, []); const importStandardsFromUrl = (url, folder) => { @@ -393,7 +411,7 @@ const Files = (props) => { toast("Failed to delete file: " + responseJson.reason); } setTimeout(() => { - getFiles(); + getFiles(selectedCategory) }, 1500); }) .catch((error) => { @@ -402,6 +420,8 @@ const Files = (props) => { }; const readFileData = (file) => { + setContentLoading(true) + fetch(globalUrl + "/api/v1/files/" + file.id + "/content", { method: "GET", headers: { @@ -411,6 +431,7 @@ const Files = (props) => { credentials: "include", }) .then((response) => { + setContentLoading(false) if (response.status !== 200) { console.log("Status not 200 for file :O!"); return ""; @@ -428,12 +449,12 @@ const Files = (props) => { return respdata }) .then((responseData) => { - - setFileContent(responseData); - //console.log("filecontent state ",fileContent); + setFileContent(responseData); + //console.log("filecontent state ",fileContent); }) .catch((error) => { - toast(error.toString()); + setContentLoading(false) + toast(error.toString()) }); }; @@ -505,12 +526,12 @@ const Files = (props) => { }; if ( - selectedNamespace !== undefined && - selectedNamespace !== null && - selectedNamespace.length > 0 && - selectedNamespace !== "default" + selectedCategory !== undefined && + selectedCategory !== null && + selectedCategory.length > 0 && + selectedCategory !== "default" ) { - data.namespace = selectedNamespace; + data.namespace = selectedCategory; } fetch(globalUrl + "/api/v1/files/create", { @@ -689,9 +710,9 @@ const Files = (props) => { - {fileNamespaces !== undefined && - fileNamespaces !== null && - fileNamespaces.length > 1 ? ( + {fileCategories !== undefined && + fileCategories !== null && + fileCategories.length > 1 ? ( File Category { + if (e.target.value === "") { + return + } + + if (e.target.value === selectedEnvironment) { + return + } + + toast.info("Updating environment KMS runs on to " + e.target.value) + data.environment = e.target.value + const envIndex = environments.findIndex((env) => env.Name === e.target.value) + if (envIndex === -1) { + toast.error("Environment not found") + return + } + + environments[envIndex].environment = e.target.value + setEnvironments(environments) + setShowEnvironmentDropdown(false) + + saveAuthentication(data) + }} + > + {environments.map((env, index) => { + if (env.archived === true) { + return null + } + + return ( + + {env.default === true ? "Default - " : ""}{env.Name} + + ) + })} + + + : null} + + } + style={{ + minWidth: 225, + maxWidth: 225, + overflow: "hidden", + }} + /> + + {/* + + */} + + {/* + + */} + { + return data.key; + }) + .join(", ") + } + style={{ + minWidth: 125, + maxWidth: 125, + overflow: "auto", + marginRight: 10, + }} + /> + + + { + updateAppAuthentication(data); + }} + disabled={ + data.org_id !== selectedOrganization.id ? true : false + } + > + + + {data.defined ? ( + + { + editAuthenticationConfig(data.id); + }} + > + + + + ) : ( + + {}} + disabled={ + data.org_id !== selectedOrganization.id + ? true + : false + } + > + + + + )} + { + deleteAuthentication(data); + }} + > + + + + + {selectedOrganization.id !== undefined && + data.org_id !== selectedOrganization.id ? ( + + + + ) : ( + + { + changeDistribution(data, !isDistributed); + }} + /> + + )} + + + ) + } + const authenticationView = curTab === 2 ? ( @@ -5361,6 +5721,10 @@ If you're interested, please let me know a time that works for you, or set up a
{authentication.map((data, index) => { var checked = data.checked + if (data.label !== undefined && data.label !== null && data.label.toLowerCase() === "kms shuffle storage") { + return null + } + if (checked === undefined || checked === null) { checked = false } @@ -5476,11 +5840,6 @@ If you're interested, please let me know a time that works for you, or set up a {authentication === undefined || authentication === null ? null : authentication.map((data, index) => { - var bgColor = "#27292d"; - if (index % 2 === 0) { - bgColor = "#1f2023"; - } - //console.log("Auth data: ", data) if (data.type === "oauth2") { data.fields = [ @@ -5503,187 +5862,14 @@ If you're interested, please let me know a time that works for you, or set up a ]; } - const isDistributed = - data.suborg_distributed === true ? true : false; - return ( - - - style={{ minWidth: 75, maxWidth: 75 }} - /> - - - {/* - - */} - - {/* - - */} - { - return data.key; - }) - .join(", ") - } - style={{ - minWidth: 125, - maxWidth: 125, - overflow: "auto", - marginRight: 10, - }} - /> - - - { - updateAppAuthentication(data); - }} - disabled={ - data.org_id !== selectedOrganization.id ? true : false - } - > - - - {data.defined ? ( - - { - editAuthenticationConfig(data.id); - }} - > - - - - ) : ( - - {}} - disabled={ - data.org_id !== selectedOrganization.id - ? true - : false - } - > - - - - )} - { - deleteAuthentication(data); - }} - > - - - - - {selectedOrganization.id !== undefined && - data.org_id !== selectedOrganization.id ? ( - - - - ) : ( - - { - changeDistribution(data, !isDistributed); - }} - /> - - )} - - - ); + + ) + + })}
diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index f10ffae8..021cc0d6 100755 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -88,6 +88,7 @@ import { Error as ErrorIcon, Warning as WarningIcon, ArrowLeft as ArrowLeftIcon, + ArrowRight as ArrowRightIcon, Cached as CachedIcon, DirectionsRun as DirectionsRunIcon, FormatListNumbered as FormatListNumberedIcon, @@ -123,6 +124,8 @@ import { Add as AddIcon, ErrorOutline as ErrorOutlineIcon, + ArrowForward as ArrowForwardIcon, + } from "@mui/icons-material"; //import * as cytoscape from "cytoscape"; @@ -378,7 +381,7 @@ const svgSize = 24; const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240") const AngularWorkflow = (defaultprops) => { - const { globalUrl, setCookie, isLoggedIn, isLoaded, userdata, data_id } = defaultprops; + const { globalUrl, setCookie, isLoggedIn, isLoaded, userdata, data_id, ReactGA, } = defaultprops; const referenceUrl = globalUrl + "/api/v1/hooks/"; //const alert = useAlert() let navigate = useNavigate(); @@ -499,6 +502,7 @@ const AngularWorkflow = (defaultprops) => { const [selectedApp, setSelectedApp] = React.useState({}); const [selectedAction, setSelectedAction] = React.useState({}); const [selectedActionEnvironment, setSelectedActionEnvironment] = React.useState({}); + const [selectedMeta, setSelectedMeta] = React.useState(undefined); // Disabled streaming for now const [streamDisabled, setStreamDisabled] = React.useState(true) @@ -950,6 +954,10 @@ const AngularWorkflow = (defaultprops) => { }) .then((responseJson) => { if (responseJson.success === true) { + if (responseJson.meta !== undefined && responseJson.meta !== null && Object.getOwnPropertyNames(responseJson.meta).length > 0) { + setSelectedMeta(responseJson.meta) + } + if (responseJson.reason !== undefined && responseJson.reason !== undefined && responseJson.reason.length > 0) { if (!responseJson.reason.includes("404: Not Found") && responseJson.reason.length > 25) { // Translate into markdown ![]() @@ -8018,10 +8026,10 @@ const AngularWorkflow = (defaultprops) => { backgroundColor: theme.palette.surfaceColor, cursor: "pointer", display: "flex", - }; + } - const VariableItem = (props) => { - const { variable, index, type } = props; + const VariableItem = (props) => { + const { variable, index, type } = props; const [open, setOpen] = React.useState(false); const [anchorEl, setAnchorEl] = React.useState(null); @@ -8792,7 +8800,25 @@ const AngularWorkflow = (defaultprops) => { ? "cloud" : environments[defaultEnvironmentIndex] === undefined ? "cloud" - : environments[defaultEnvironmentIndex].Name; + : environments[defaultEnvironmentIndex].Name + + // Basic automatic auth mapping + var authId = "" + if (appAuthentication !== undefined && appAuthentication !== null && appAuthentication.length > 0) { + const appname = app.name.toLowerCase().replace(" ", "_") + for (var key in appAuthentication) { + const authKey = appAuthentication[key] + if (authKey.app.id === app.id) { + authId = authKey.id + break + } + + const appauthname = authKey.app.name.toLowerCase().replace(" ", "_") + if (appauthname === appname) { + authId = authKey.id + } + } + } // List other nodes in the workflow and see if they have an environment set. If they do, use that as the default if (cy !== undefined && cy !== null) { @@ -8840,7 +8866,7 @@ const AngularWorkflow = (defaultprops) => { app.categories.length > 0 ? app.categories[0] : "", - authentication_id: "", + authentication_id: authId, finished: false, template: app.template === true ? true : false, }; @@ -9722,30 +9748,49 @@ const AngularWorkflow = (defaultprops) => { } } - setSelectedAction(newSelectedAction); - setUpdate(Math.random()); + // Last fix for params + if (newSelectedAction.parameters !== undefined && newSelectedAction.parameters !== null && newSelectedAction.parameters.length > 0) { + for (let paramkey in newSelectedAction.parameters) { + const param = newSelectedAction.parameters[paramkey] + if (param.name !== "body") { + continue + } - const allNodes = cy.nodes().jsons(); - if (allNodes !== undefined && allNodes !== null) { - for (let nodekey in allNodes) { - const currentNode = allNodes[nodekey]; - if ( - currentNode.data.attachedTo === oldaction.id && - currentNode.data.isDescriptor - ) { - const foundnode = cy.getElementById(currentNode.data.id); - if (foundnode !== null && foundnode !== undefined) { - const iconInfo = GetIconInfo(newaction); - const svg_pin = ``; - const svgpin_Url = encodeURI("data:image/svg+xml;utf-8," + svg_pin); - foundnode.data("image", svgpin_Url); - foundnode.data("imageColor", iconInfo.iconBackgroundColor); - } - - break; + if (param.example !== undefined && param.example !== null && param.example.length > 0) { + if (param.value === undefined || param.value === null || param.value.length === 0) { + param.value = param.example } } + + newSelectedAction.parameters[paramkey] = param } + } + + console.log("New selected action: ", newSelectedAction) + setSelectedAction(newSelectedAction) + setUpdate(Math.random()) + + const allNodes = cy.nodes().jsons() + if (allNodes !== undefined && allNodes !== null) { + for (let nodekey in allNodes) { + const currentNode = allNodes[nodekey]; + if ( + currentNode.data.attachedTo === oldaction.id && + currentNode.data.isDescriptor + ) { + const foundnode = cy.getElementById(currentNode.data.id); + if (foundnode !== null && foundnode !== undefined) { + const iconInfo = GetIconInfo(newaction); + const svg_pin = ``; + const svgpin_Url = encodeURI("data:image/svg+xml;utf-8," + svg_pin); + foundnode.data("image", svgpin_Url); + foundnode.data("imageColor", iconInfo.iconBackgroundColor); + } + + break; + } + } + } // Send it in here, after all fields are filled // Disabled for now :( @@ -17673,6 +17718,71 @@ const AngularWorkflow = (defaultprops) => { ) } + const changeExecution = (data) => { + if ((data.result === undefined || data.result === null || data.result.length === 0) && data.status !== "FINISHED" && data.status !== "ABORTED") { + start() + setExecutionRunning(true) + setExecutionRequestStarted(false) + } + + var checkStarted = false + if (data.results !== undefined && data.results !== null && data.results.length > 0) { + if (data.execution_argument !== undefined && data.execution_argument !== null && data.execution_argument.includes("too large")) { + setExecutionData({}); + checkStarted = true + start(); + setExecutionRunning(true); + setExecutionRequestStarted(false); + } else { + if (data.results !== undefined && data.results !== null) { + for (let resultkey in data.results) { + if (data.results[resultkey].status !== "SUCCESS") { + continue + } + + if (data.results[resultkey].result.includes("too large")) { + setExecutionData({}); + checkStarted = true + start(); + setExecutionRunning(true); + setExecutionRequestStarted(false); + break + } + } + } + } + } + + const cur_execution = { + execution_id: data.execution_id, + authorization: data.authorization, + } + + setExecutionRequest(cur_execution) + setExecutionModalView(1) + + if (!checkStarted) { + handleUpdateResults(data, cur_execution) + + if (cy !== undefined && cy !== null) { + cy.elements().removeClass("success-highlight failure-highlight executing-highlight"); + for (let actionKey in data.workflow.actions) { + var actionitem = data.workflow.actions[actionKey] + + handleColoring(actionitem.id, "", actionitem.label) + } + + for (let resultKey in data.results) { + var item = data.results[resultKey] + + handleColoring(item.action.id, item.status, item.action.label) + } + } + + setExecutionData(data) + } + } + const ShowCopyingTooltip = () => { const [showCopying, setShowCopying] = React.useState(true) @@ -18101,7 +18211,7 @@ const AngularWorkflow = (defaultprops) => {

Details

@@ -18147,7 +18257,80 @@ const AngularWorkflow = (defaultprops) => { ) : null} - {isCloud ? ( + + + + + + + + + + + + + + {isCloud ? { - ) : null} + : null} + {executionData.workflow !== undefined && executionData.workflow !== null && executionData.workflow.actions !== undefined && executionData.workflow.actions !== null && executionData.workflow.actions.length > 0 ?
@@ -18801,21 +18985,49 @@ const AngularWorkflow = (defaultprops) => { validate.result = JSON.parse(validate.result) } - const AppResultVariable = ({ data }) => { + const AppResultVariable = ({ data, action }) => { const [open, setOpen] = React.useState(false) const showVariable = data.value.length < 60 // Check if it's valid JSON const checked = validateJson(data.value.trim()) + if (data.name === "shuffle_action_logs" && data.value !== undefined && data.value !== null && data.value.length > 0 && data.value.includes("add env SHUFFLE_LOGS_DISABLED")) { + return ( +
+ + Action Logs + + + Logs for an action are not available without an onprem environment with the SHUFFLE_LOGS_DISABLED environment variable set to false: SHUFFLE_LOGS_DISABLED=false. Logs are enabled by default, except in scale mode. + +
+ ) + } + + var showlink = false + if (data.name.endsWith("-Url")) { + //data.name = data.name.toLowerCase().replaceAll("-", "_") + if (data.value.startsWith(", ")) { + data.value = data.value.substring(2) + } + + if (!data.value.startsWith("http") || (data.value.startsWith("/") && data.value.includes("?"))) { + showlink = true + } + } + return (
{data.value.length > 60 || checked.valid ? { variant="body2" style={{ whiteSpace: 'pre-line', + color: showlink ? "#f85a3e" : "white", + cursor: showlink ? "pointer" : "default", }} - color="textSecondary" + onClick={(e) => { + if (showlink) { + e.preventDefault() + e.stopPropagation() + window.open(data.value, "_blank") + } + }} + color={showlink ? "inherit" : "textSecondary"} > {data.value} @@ -18981,6 +19202,10 @@ const AngularWorkflow = (defaultprops) => { return "Consider whether your Orborus environment can connect to a local IP or not." } + if (stringjson.includes("kms/")) { + return "KMS authentication most likely failed. Check your notifications for more details on this page: /admin?admin_tab=priorities. If you need help with KMS, please contact support@shuffler.io" + } + if (stringjson.includes("invalidurl")) { // IF count of "http" is more than one, 1, it's prolly invalid var additionalinfo = "" @@ -19006,7 +19231,7 @@ const AngularWorkflow = (defaultprops) => { if (stringjson.includes("connectionerror")) { if (stringjson.includes("kms")) { - return "KMS authentication failed. Check your notifications for more details." + return "KMS authentication most likely failed (2). Check your notifications for more details on this page: /admin?admin_tab=priorities&kms=true. If you need help with KMS, please contact support@shuffler.io" } return "The URL is incorrect, or Shuffle can't reach it. Set up a Shuffle Environment in the same VLAN, or whitelist Shuffle's IPs." @@ -19352,7 +19577,7 @@ const AngularWorkflow = (defaultprops) => { } return ( - + ); })}
@@ -20184,6 +20409,8 @@ const AngularWorkflow = (defaultprops) => { //if (configureWorkflowModalOpen) { // setSelectedAction({}); //} + // + setSelectedMeta(undefined) }} PaperProps={{ style: { @@ -20315,11 +20542,27 @@ const AngularWorkflow = (defaultprops) => { overflowY: "auto", overflowX: "hidden", }} + onLoad={() => { + /* + if (isCloud && ReactGA !== undefined) { + toast("Sending GA info") + // Google analytics info about what app people are looking at + ReactGA.event({ + category: "workflow", + action: `documentation_load`, + label: selectedApp.name, + }) + + } + */ + }} > {selectedApp.documentation === undefined || selectedApp.documentation === null || selectedApp.documentation.length === 0 ? ( - + { style={{ marginTop: 25, marginBottom: 25, - backgroundColor: theme.palette.inputColor, + backgroundColor: "rgba(255,255,255,0.6)", }} /> - - There is currently no extended documentation available for this - app. - + +
+ + There is no Shuffle-specific documentation for this app yet. Documentation is written custom for each app, and is a community effort. Hope to see your contribution + + +
+ - Want to help the making of, or imrpvoe this app?{" "} + Want to help the making of, or improve this app?{" "} +
{ )}
) : ( +
+ {selectedMeta !== undefined && selectedMeta !== null && Object.getOwnPropertyNames(selectedMeta).length > 0 && selectedMeta.name !== undefined && selectedMeta.name !== null ? +
+
+ {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} + + + ); + })} +
+ )} +
+
+ : null} { > {selectedApp.documentation} +
)}
diff --git a/frontend/src/views/Apps.jsx b/frontend/src/views/Apps.jsx index 1000f7a0..4c816623 100755 --- a/frontend/src/views/Apps.jsx +++ b/frontend/src/views/Apps.jsx @@ -955,7 +955,6 @@ const Apps = (props) => { : null - console.log("Sharing config: ", sharingConfiguration); const activateButton = selectedApp.generated && !selectedApp.activated ? (
@@ -1281,10 +1280,15 @@ const Apps = (props) => { {isCloud && !internalIds.includes(selectedApp.name.toLowerCase()) ? -

{passwordFormMessage}

- +

{passwordFormMessage}

{isCloud && ( <> diff --git a/frontend/src/views/Workflows.jsx b/frontend/src/views/Workflows.jsx index 99d4fca1..b742157a 100755 --- a/frontend/src/views/Workflows.jsx +++ b/frontend/src/views/Workflows.jsx @@ -2153,7 +2153,7 @@ const Workflows = (props) => { } return ( -
+
{selectedCategory !== "" ? @@ -2206,8 +2206,17 @@ const Workflows = (props) => { Edit '{data.name}' + +
+ + {isDistributed || hasSuborgs ? + + This is a parentorg-controlled workflow. + + : null}
} placement="right"> +