diff --git a/frontend/src/components/EditWorkflow.jsx b/frontend/src/components/EditWorkflow.jsx index 26d11604..dc0a324e 100644 --- a/frontend/src/components/EditWorkflow.jsx +++ b/frontend/src/components/EditWorkflow.jsx @@ -1,8 +1,8 @@ import React, { useEffect, useContext } from "react"; import theme from '../theme.jsx'; -import { isMobile } from "react-device-detect" +import { isMobile } from "react-device-detect" import { MuiChipsInput } from "mui-chips-input"; -import { toast } from "react-toastify" +import { toast } from "react-toastify" import UsecaseSearch from "../components/UsecaseSearch.jsx" import WorkflowGrid from "../components/WorkflowGrid.jsx" import dayjs from 'dayjs'; @@ -10,32 +10,32 @@ import WorkflowTemplatePopup from "./WorkflowTemplatePopup.jsx"; import WorkflowValidationTimeline from "../components/WorkflowValidationTimeline.jsx" import { - Badge, - Avatar, - Grid, - InputLabel, - Select, - ListSubheader, - Paper, - Tooltip, - Divider, - Button, - TextField, - IconButton, - Menu, - MenuItem, - Link, - FormControlLabel, - Chip, - Switch, - Typography, - Zoom, - CircularProgress, - Drawer, - Dialog, - DialogTitle, - DialogActions, - DialogContent, + Badge, + Avatar, + Grid, + InputLabel, + Select, + ListSubheader, + Paper, + Tooltip, + Divider, + Button, + TextField, + IconButton, + Menu, + MenuItem, + Link, + FormControlLabel, + Chip, + Switch, + Typography, + Zoom, + CircularProgress, + Drawer, + Dialog, + DialogTitle, + DialogActions, + DialogContent, OutlinedInput, Checkbox, ListItemText, @@ -47,8 +47,8 @@ import { } from "@mui/material"; -import { - DatePicker, +import { + DatePicker, LocalizationProvider, } from '@mui/x-date-pickers' @@ -56,39 +56,41 @@ import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs' import { useStyles } from '../views/AppCreator.jsx' import { - ExpandLess as ExpandLessIcon, - ExpandMore as ExpandMoreIcon, - Publish as PublishIcon, - OpenInNew as OpenInNewIcon, - Add as AddIcon, - Remove as RemoveIcon, - EditNote as EditNoteIcon, + ExpandLess as ExpandLessIcon, + ExpandMore as ExpandMoreIcon, + Publish as PublishIcon, + OpenInNew as OpenInNewIcon, + Add as AddIcon, + Remove as RemoveIcon, + EditNote as EditNoteIcon, } from "@mui/icons-material"; const EditWorkflow = (props) => { const { globalUrl, workflow, setWorkflow, modalOpen, setModalOpen, showUpload, usecases, setNewWorkflow, appFramework, isEditing, userdata, apps, saveWorkflow, expanded, scrollTo, setRealtimeMarkdown, boxWidth, setBoxWidth, } = props - const [_, setUpdate] = React.useState(""); // Used for rendering, don't remove + const [_, setUpdate] = React.useState(""); // Used for rendering, don't remove - const [submitLoading, setSubmitLoading] = React.useState(false); - const [showMoreClicked, setShowMoreClicked] = React.useState(expanded === true ? true : false); - const [innerWorkflow, setInnerWorkflow] = React.useState(workflow) + const [submitLoading, setSubmitLoading] = React.useState(false); + const [showMoreClicked, setShowMoreClicked] = React.useState(expanded === true ? true : false); - const [newWorkflowTags, setNewWorkflowTags] = React.useState(workflow.tags !== undefined && workflow.tags !== null ? JSON.parse(JSON.stringify(workflow.tags)) : []) - const [description, setDescription] = React.useState(workflow.description !== undefined ? workflow.description : "") + const [innerWorkflow, setInnerWorkflow] = React.useState(workflow) - const [selectedUsecases, setSelectedUsecases] = React.useState(workflow.usecase_ids !== undefined && workflow.usecase_ids !== null ? JSON.parse(JSON.stringify(workflow.usecase_ids)) : []); + const [newWorkflowTags, setNewWorkflowTags] = React.useState(workflow.tags !== undefined && workflow.tags !== null ? JSON.parse(JSON.stringify(workflow.tags)) : []) + const [description, setDescription] = React.useState(workflow.description !== undefined ? workflow.description : "") + + const [selectedUsecases, setSelectedUsecases] = React.useState(workflow.usecase_ids !== undefined && workflow.usecase_ids !== null ? JSON.parse(JSON.stringify(workflow.usecase_ids)) : []); const [foundWorkflowId, setFoundWorkflowId] = React.useState("") const [name, setName] = React.useState(workflow.name !== undefined ? workflow.name : "") - const [dueDate, setDueDate] = React.useState(workflow.due_date !== undefined && workflow.due_date !== null && workflow.due_date !== 0 ? dayjs(workflow.due_date*1000) : dayjs().subtract(1, 'day')) + const [workflowAsCode, setWorkflowAsCode] = React.useState(false) + const [dueDate, setDueDate] = React.useState(workflow.due_date !== undefined && workflow.due_date !== null && workflow.due_date !== 0 ? dayjs(workflow.due_date * 1000) : dayjs().subtract(1, 'day')) - const [inputQuestions, setInputQuestions] = React.useState(workflow.input_questions !== undefined && workflow.input_questions !== null ? JSON.parse(JSON.stringify(workflow.input_questions)) : []) + const [inputQuestions, setInputQuestions] = React.useState(workflow.input_questions !== undefined && workflow.input_questions !== null ? JSON.parse(JSON.stringify(workflow.input_questions)) : []) const [inputMarkdown, setInputMarkdown] = React.useState(workflow?.form_control?.input_markdown !== undefined && workflow?.form_control?.input_markdown !== null ? workflow?.form_control?.input_markdown : "") const [scrollDone, setScrollDone] = React.useState(false) const [selectedYieldActions, setSelectedYieldActions] = React.useState(workflow?.form_control?.output_yields !== undefined && workflow?.form_control?.output_yields !== null ? JSON.parse(JSON.stringify(workflow?.form_control?.output_yields)) : []) const [formWidth, setFormWidth] = React.useState(boxWidth === undefined || boxWidth === null ? 500 : boxWidth) - const classes = useStyles(); + const classes = useStyles(); useEffect(() => { if (setBoxWidth !== undefined && boxWidth !== formWidth) { @@ -96,79 +98,79 @@ const EditWorkflow = (props) => { } }, [formWidth]) - if (scrollTo !== undefined && scrollTo !== null && scrollTo.length > 0 && scrollDone === false) { - setTimeout(() => { - const foundScroll = document.getElementById(scrollTo) - if (foundScroll !== null) { - // Smooth scroll - foundScroll.scrollIntoView({ behavior: "smooth" }) - } - - }, 200) - setScrollDone(true) + if (scrollTo !== undefined && scrollTo !== null && scrollTo.length > 0 && scrollDone === false) { + setTimeout(() => { + const foundScroll = document.getElementById(scrollTo) + if (foundScroll !== null) { + // Smooth scroll + foundScroll.scrollIntoView({ behavior: "smooth" }) + } - } + }, 200) + setScrollDone(true) + + } // Gets the generated workflow - const getGeneratedWorkflow = (workflow_id) => { - const url = `${globalUrl}/api/v1/workflows/${workflow_id}` - fetch(url, { - method: "GET", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, - credentials: "include", - }) - .then((response) => { - if (response.status !== 200) { - console.log("Status not 200 when getting workflow"); - } + const getGeneratedWorkflow = (workflow_id) => { + const url = `${globalUrl}/api/v1/workflows/${workflow_id}` + fetch(url, { + method: "GET", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + credentials: "include", + }) + .then((response) => { + if (response.status !== 200) { + console.log("Status not 200 when getting workflow"); + } - return response.json(); - }) - .then((responseJson) => { - if (responseJson.id === workflow_id) { - console.log("GOT WORKFLOW: ", responseJson) - if (name === "") { - innerWorkflow.name = responseJson.name - setName(responseJson.name) - } + return response.json(); + }) + .then((responseJson) => { + if (responseJson.id === workflow_id) { + console.log("GOT WORKFLOW: ", responseJson) + if (name === "") { + innerWorkflow.name = responseJson.name + setName(responseJson.name) + } - if (description === "") { - innerWorkflow.description = responseJson.description - setDescription(description) - } + if (description === "") { + innerWorkflow.description = responseJson.description + setDescription(description) + } - if (newWorkflowTags === []) { - innerWorkflow.tags = responseJson.tags - setNewWorkflowTags(responseJson.tags) - } + if (newWorkflowTags === []) { + innerWorkflow.tags = responseJson.tags + setNewWorkflowTags(responseJson.tags) + } - if (selectedUsecases === []) { - selectedUsecases = responseJson.usecase_ids - } + if (selectedUsecases === []) { + selectedUsecases = responseJson.usecase_ids + } - innerWorkflow.id = responseJson.id - innerWorkflow.blogpost = responseJson.blogpost - innerWorkflow.actions = responseJson.actions - innerWorkflow.triggers = responseJson.triggers - innerWorkflow.branches = responseJson.branches - innerWorkflow.comments = responseJson.comments - innerWorkflow.workflow_variables = responseJson.workflow_variables - innerWorkflow.execution_variables = responseJson.execution_variables + innerWorkflow.id = responseJson.id + innerWorkflow.blogpost = responseJson.blogpost + innerWorkflow.actions = responseJson.actions + innerWorkflow.triggers = responseJson.triggers + innerWorkflow.branches = responseJson.branches + innerWorkflow.comments = responseJson.comments + innerWorkflow.workflow_variables = responseJson.workflow_variables + innerWorkflow.execution_variables = responseJson.execution_variables - setInnerWorkflow(innerWorkflow) - setUpdate(Math.random()) - } - }) - .catch((error) => { - //toast(error.toString()); - console.log("Get workflow error: ", error.toString()); - }) - } + setInnerWorkflow(innerWorkflow) + setUpdate(Math.random()) + } + }) + .catch((error) => { + //toast(error.toString()); + console.log("Get workflow error: ", error.toString()); + }) + } if (foundWorkflowId.length > 0) { getGeneratedWorkflow(foundWorkflowId) @@ -181,66 +183,66 @@ const EditWorkflow = (props) => { return null } - const newWorkflow = isEditing === true ? false : true - const priority = userdata === undefined || userdata === null ? null : userdata.priorities.find(prio => prio.type === "usecase" && prio.active === true) - var upload = ""; + const newWorkflow = isEditing === true ? false : true + const priority = userdata === undefined || userdata === null ? null : userdata.priorities.find(prio => prio.type === "usecase" && prio.active === true) + var upload = ""; var total_count = 0 return ( - { - setModalOpen(false); - }} - PaperProps={{ - style: { - color: "white", - minWidth: isMobile ? "90%" : 650, - maxWidth: isMobile ? "90%" : 650, - minHeight: 400, - paddingTop: 25, - paddingLeft: 50, - //minWidth: isMobile ? "90%" : newWorkflow === true ? 1000 : 550, - //maxWidth: isMobile ? "90%" : newWorkflow === true ? 1000 : 550, - }, - }} - > - -
-
-
- - {newWorkflow ? "New" : "Editing"} workflow - + { + setModalOpen(false); + }} + PaperProps={{ + style: { + color: "white", + minWidth: isMobile ? "90%" : 650, + maxWidth: isMobile ? "90%" : 650, + minHeight: 400, + paddingTop: 25, + paddingLeft: 50, + //minWidth: isMobile ? "90%" : newWorkflow === true ? 1000 : 550, + //maxWidth: isMobile ? "90%" : newWorkflow === true ? 1000 : 550, + }, + }} + > + +
+
+
+ + {newWorkflow ? "New" : "Editing"} workflow + + + {newWorkflow === true ? null : +
+ + + + + + + +
+ } - {newWorkflow === true ? null : -
- - - - - - -
- } + + Workflows can be built from scratch, or from templates. Usecases can help you discover next steps, and you can search for them directly. Learn more + -
- - Workflows can be built from scratch, or from templates. Usecases can help you discover next steps, and you can search for them directly. Learn more - - - {/* + {/*
{
*/} - {showUpload === true ? -
- - - -
- : null} -
+ {showUpload === true ? +
+ + + +
+ : null} +
{/*newWorkflow === true ?
@@ -276,10 +278,10 @@ const EditWorkflow = (props) => {
: null*/}
- - -
- {/* + + +
+ {/* */} - -
+ if (setWorkflow !== undefined) { + setWorkflow(innerWorkflow) + } + } else if (setNewWorkflow !== undefined) { + setNewWorkflow( + innerWorkflow.name, + innerWorkflow.description, + innerWorkflow.tags, + innerWorkflow.default_return_value, + innerWorkflow, + newWorkflow, + innerWorkflow.usecase_ids, + innerWorkflow.blogpost, + innerWorkflow.status, + workflowAsCode + ) + setWorkflow({}) + } else { + setWorkflow(innerWorkflow) + console.log("editing workflow: ", innerWorkflow) + } - -
- { - setName(event.target.value) - }} - InputProps={{ - style: { - color: "white", - }, - }} - color="primary" - placeholder="Name" + setSubmitLoading(true) + + // If new workflow, don't close it + if (isEditing) { + setModalOpen(false) + } + }} + color="primary" + > + {submitLoading ? : "Save Changes"} + +
+ + +
+ { + setName(event.target.value) + }} + InputProps={{ + style: { + color: "white", + }, + }} + color="primary" + placeholder="Name" required - margin="dense" - defaultValue={innerWorkflow.name} + margin="dense" + defaultValue={innerWorkflow.name} label="Name" - autoFocus - fullWidth - /> -
- {usecases !== null && usecases !== undefined && usecases.length > 0 ? - - Usecases - selected.join(', ')} + onChange={(event) => { + console.log("Changed: ", event) + }} + > + + None + {usecases.map((usecase, index) => { //console.log(usecase) return ( {usecase.name} @@ -437,22 +441,22 @@ const EditWorkflow = (props) => { selectedUsecases.push(subcase.name) } - setUpdate(Math.random()); + setUpdate(Math.random()); setSelectedUsecases(selectedUsecases) }}> - - + + ) })} ) })} - - - : null} + + + : null} { />
- {showMoreClicked === true ? -
- { - setDescription(event.target.value) - }} - InputProps={{ - style: { - color: "white", - }, - }} - multiLine - rows={3} - color="primary" - defaultValue={innerWorkflow.description} - placeholder="Description" - multiline - label="Description" - margin="dense" - fullWidth - /> + {showMoreClicked === true ? +
+ { + setDescription(event.target.value) + }} + InputProps={{ + style: { + color: "white", + }, + }} + multiLine + rows={3} + color="primary" + defaultValue={innerWorkflow.description} + placeholder="Description" + multiline + label="Description" + margin="dense" + fullWidth + /> -
- +
+ Status - { - console.log("Data: ", e.target.value) - - //innerWorkflow.workflow_type = e.target.value - innerWorkflow.status = e.target.value - setInnerWorkflow(innerWorkflow) - }} - > - } label="Test" /> - } label="Production" /> + { + console.log("Data: ", e.target.value) - + //innerWorkflow.workflow_type = e.target.value + innerWorkflow.status = e.target.value + setInnerWorkflow(innerWorkflow) + }} + > + } label="Test" /> + } label="Production" /> + + - { @@ -552,25 +556,25 @@ const EditWorkflow = (props) => {
- + Type - { - console.log("Data: ", e.target.value) - - innerWorkflow.workflow_type = e.target.value - setInnerWorkflow(innerWorkflow) - }} - > - } label="Trigger" /> - } label="Subflow" /> - } label="Standalone" /> + { + console.log("Data: ", e.target.value) - + innerWorkflow.workflow_type = e.target.value + setInnerWorkflow(innerWorkflow) + }} + > + } label="Trigger" /> + } label="Subflow" /> + } label="Standalone" /> + + @@ -631,127 +635,127 @@ const EditWorkflow = (props) => { fullWidth /> - + - + MSSP controls - + MSSP Suborg Distribution (beta - contact support@shuffler.io for more info) {userdata !== undefined && userdata !== null && userdata.orgs !== undefined && userdata.orgs !== null && userdata.orgs.length > 0 ? userdata.orgs.filter(org => org.creator_org === userdata.active_org.id).length === 0 ? userdata.active_org.creator_org === undefined || userdata.active_org.creator_org === null || userdata.active_org.creator_org === "" ? - - Your organization does not have any suborgs yet OR your user may not have access to available suborgs. Please make one or get access to suborgs by another admin, then try again. + + Your organization does not have any suborgs yet OR your user may not have access to available suborgs. Please make one or get access to suborgs by another admin, then try again. : - - {innerWorkflow.parentorg_workflow !== undefined && innerWorkflow.parentorg_workflow !== null && innerWorkflow.parentorg_workflow.length > 0 ? This workflow is distributed from your parent workflow (you may not have access). : null} + + {innerWorkflow.parentorg_workflow !== undefined && innerWorkflow.parentorg_workflow !== null && innerWorkflow.parentorg_workflow.length > 0 ? This workflow is distributed from your parent workflow (you may not have access). : null}

You can only distribute to suborgs from a parent org.
+ : + : - - : - - + + Create a sub-org to distribute workflows to suborgs. } - + {/**/} - - + + Git Backup Repository - Decide where this workflow is backed up in a Git repository. Will create logs and notifications if upload fails. The repository and branch must already have been initialized. Files will show up in the root folder in the format 'orgid/workflow status/workflow id.json' without images. Overrides your default backup repository. Credentials are encrypted. Creates notifications if it fails. + Decide where this workflow is backed up in a Git repository. Will create logs and notifications if upload fails. The repository and branch must already have been initialized. Files will show up in the root folder in the format 'orgid/workflow status/workflow id.json' without images. Overrides your default backup repository. Credentials are encrypted. Creates notifications if it fails. @@ -772,7 +776,7 @@ const EditWorkflow = (props) => { margin="normal" variant="outlined" placeholder="github/com/shuffle/workflowbackup " - defaultValue={innerWorkflow.backup_config === undefined || innerWorkflow.backup_config.upload_repo === undefined || innerWorkflow.backup_config.upload_repo === null || innerWorkflow.backup_config.upload_repo === "" ? "" : innerWorkflow.backup_config.upload_repo} + defaultValue={innerWorkflow.backup_config === undefined || innerWorkflow.backup_config.upload_repo === undefined || innerWorkflow.backup_config.upload_repo === null || innerWorkflow.backup_config.upload_repo === "" ? "" : innerWorkflow.backup_config.upload_repo} onChange={(e) => { //setUploadRepo(e.target.value); innerWorkflow.backup_config.upload_repo = e.target.value @@ -806,7 +810,7 @@ const EditWorkflow = (props) => { multiline={true} rows={1} placeholder="The branch to use (default: master)" - defaultValue={innerWorkflow.backup_config === undefined || innerWorkflow.backup_config.upload_branch === undefined || innerWorkflow.backup_config.upload_branch === null || innerWorkflow.backup_config.upload_branch === "" ? "" : innerWorkflow.backup_config.upload_branch} + defaultValue={innerWorkflow.backup_config === undefined || innerWorkflow.backup_config.upload_branch === undefined || innerWorkflow.backup_config.upload_branch === null || innerWorkflow.backup_config.upload_branch === "" ? "" : innerWorkflow.backup_config.upload_branch} onChange={(e) => { innerWorkflow.backup_config.upload_branch = e.target.value setInnerWorkflow(innerWorkflow) @@ -823,7 +827,7 @@ const EditWorkflow = (props) => { - + Username @@ -840,8 +844,8 @@ const EditWorkflow = (props) => { id="outlined-with-placeholder" margin="normal" variant="outlined" - placeholder="Username to use" - defaultValue={innerWorkflow.backup_config === undefined || innerWorkflow.backup_config.upload_username === undefined || innerWorkflow.backup_config.upload_username === null || innerWorkflow.backup_config.upload_username === "" ? "" : innerWorkflow.backup_config.upload_username} + placeholder="Username to use" + defaultValue={innerWorkflow.backup_config === undefined || innerWorkflow.backup_config.upload_username === undefined || innerWorkflow.backup_config.upload_username === null || innerWorkflow.backup_config.upload_username === "" ? "" : innerWorkflow.backup_config.upload_username} onChange={(e) => { innerWorkflow.backup_config.upload_username = e.target.value setInnerWorkflow(innerWorkflow) @@ -873,8 +877,8 @@ const EditWorkflow = (props) => { variant="outlined" multiline={true} rows={1} - placeholder="Your API token. Required." - defaultValue={innerWorkflow.backup_config === undefined || innerWorkflow.backup_config.upload_token === undefined || innerWorkflow.backup_config.upload_token === null || innerWorkflow.backup_config.upload_token === "" ? "" : innerWorkflow.backup_config.upload_token} + placeholder="Your API token. Required." + defaultValue={innerWorkflow.backup_config === undefined || innerWorkflow.backup_config.upload_token === undefined || innerWorkflow.backup_config.upload_token === null || innerWorkflow.backup_config.upload_token === "" ? "" : innerWorkflow.backup_config.upload_token} onChange={(e) => { innerWorkflow.backup_config.upload_token = e.target.value setInnerWorkflow(innerWorkflow) @@ -893,311 +897,327 @@ const EditWorkflow = (props) => { - + -
- - Form Control - - - Form Control is used to control how the Form for the workflow is shown to users. You can add input fields, markdown, and more. This is the first step in the workflow, and is required for all workflows. - +
+ + Form Control + + + Form Control is used to control how the Form for the workflow is shown to users. You can add input fields, markdown, and more. This is the first step in the workflow, and is required for all workflows. + - - Input fields - + + Input fields + - - - - - - - + + + + + + + -
- - - Input fields are fields that will be used during the startup of the workflow. These will be formatted in JSON and is most commonly used from the Form page for this workflow. If chosen in the User Input node, these will be required fields. Use Semi-Colon ";" to create dropdown options. The first key will be the name shown, and subsequent keys will be the available values. - - - - {inputQuestions.map((data, index) => { - var showListinfo = false - if (data.value !== undefined && data.value !== null && data.value.length > 0) { - if (data.value.includes(";")) { - showListinfo = true - } - } - - return ( -
- { - inputQuestions[index].name = e.target.value - setInputQuestions(inputQuestions) - setUpdate(Math.random()); - }} - InputProps={{ - classes: { - notchedOutline: classes.notchedOutline, - }, - }} - /> - { - // Replace multiple semicolon with one - e.target.value = e.target.value.replace(";;", ";") - - inputQuestions[index].value = e.target.value - setInputQuestions(inputQuestions) - setUpdate(Math.random()); - }} - InputProps={{ - classes: { - notchedOutline: classes.notchedOutline, - }, - }} - /> -
- ) - })} - - -
- - Input Markdown - - - Markdown will be shown on the Form page. The first image added will be used in your Form Toolbox list. Output for a Workflow is shown in Markdown, and is controlled by the LAST action that runs. Supports HTML. - - { - //console.log("KEY: ", e.key) - if (e.key === "Tab") { - e.preventDefault() - } - }} - onChange={(e) => { - if (setRealtimeMarkdown !== undefined) { - setRealtimeMarkdown(e.target.value) + {inputQuestions.map((data, index) => { + var showListinfo = false + if (data.value !== undefined && data.value !== null && data.value.length > 0) { + if (data.value.includes(";")) { + showListinfo = true + } } - setInputMarkdown(e.target.value) - workflow.form_control.input_markdown = e.target.value - setWorkflow(workflow) - setUpdate(Math.random()) - }} - /> -
- -
- - Form Size - - - Control the width of the form. It will grow vertically as needed. - - { - setFormWidth(value) - }} - /> -
- -
- - Output Control ({selectedYieldActions.length === 0 ? "No Returns" : selectedYieldActions.length === 1 ? "Returning 1 node" : `Returning ${selectedYieldActions.length} nodes`}) - - - - When running this workflow, the output will be shown as a Markdown object by default, with JSON objects being rendered. By adding nodes below, they will be shown while the workflow is running as soon as they get a result. Failing/Skipped nodes are not shown. This makes it possible to track progress for more complex usecases. - - - - - -
+ + + +
+ + Input Markdown + + + Markdown will be shown on the Form page. The first image added will be used in your Form Toolbox list. Output for a Workflow is shown in Markdown, and is controlled by the LAST action that runs. Supports HTML. + + { + //console.log("KEY: ", e.key) + if (e.key === "Tab") { + e.preventDefault() + } + }} + + onChange={(e) => { + if (setRealtimeMarkdown !== undefined) { + setRealtimeMarkdown(e.target.value) + } + + setInputMarkdown(e.target.value) + workflow.form_control.input_markdown = e.target.value + setWorkflow(workflow) + setUpdate(Math.random()) + }} + /> +
+ +
+ + Form Size + + + Control the width of the form. It will grow vertically as needed. + + { + setFormWidth(value) + }} + /> +
+ +
+ + Output Control ({selectedYieldActions.length === 0 ? "No Returns" : selectedYieldActions.length === 1 ? "Returning 1 node" : `Returning ${selectedYieldActions.length} nodes`}) + + + + When running this workflow, the output will be shown as a Markdown object by default, with JSON objects being rendered. By adding nodes below, they will be shown while the workflow is running as soon as they get a result. Failing/Skipped nodes are not shown. This makes it possible to track progress for more complex usecases. + + + + + +
+
+ : null} + + {/*!isEditing ? <> +
+ } + label="Create workflow as code" + style={{ marginTop: '12px' }} + onChange={(e) => { + setWorkflowAsCode(e.target.checked) + workflow.workflow_as_code = e.target.checked + setWorkflow(workflow) + setInnerWorkflow(workflow) + }} + /> + +
+ : null*/} + + + + +
- : null} + + + {newWorkflow === true ? + + + Relevant Workflows + - -
+ } - {priority === null || priority === undefined ? null : -
- + : null} - srcapp={priority.description.split("&").length > 2 ? priority.description.split("&")[0] : ""} - img1={priority.description.split("&").length > 2 ? priority.description.split("&")[1] : ""} - - dstapp={priority.description.split("&").length > 3 ? priority.description.split("&")[2] : ""} - img2={priority.description.split("&").length > 3 ? priority.description.split("&")[3] : ""} - title={priority.name} - description={priority.description.split("&").length > 4 ? priority.description.split("&")[4] : ""} - - apps={apps} - /> -
- } - - - : null} - - {/*newWorkflow === true && name.length > 2 ? + {/*newWorkflow === true && name.length > 2 ?
{ />
: null*/} - - + + ) } diff --git a/frontend/src/components/ShuffleCodeEditor1.jsx b/frontend/src/components/ShuffleCodeEditor1.jsx index d089f587..40d807bc 100644 --- a/frontend/src/components/ShuffleCodeEditor1.jsx +++ b/frontend/src/components/ShuffleCodeEditor1.jsx @@ -114,6 +114,7 @@ const CodeEditor = (props) => { editorData, setAiQueryModalOpen, + fullScreenMode } = props const [localcodedata, setlocalcodedata] = React.useState(codedata === undefined || codedata === null || codedata.length === 0 ? "" : codedata); @@ -999,6 +1000,55 @@ const CodeEditor = (props) => { } } + if (fullScreenMode) { + return ( + { + // setlocalcodedata(value) + // expectedOutput(value) + // highlight_variables(value,editor) + setlocalcodedata(value) + setcodedata(value) + }} + name="python-editor" + fontSize={14} + width="100%" + height="100%" + showPrintMargin={false} + showGutter={true} + markers={markers} + highlightActiveLine={false} + + enableBasicAutocompletion={true} + completers={[customCompleter]} + + style={{ + wordBreak: "break-word", + marginTop: 0, + paddingBottom: 10, + overflowY: "auto", + whiteSpace: "pre-wrap", + wordWrap: "break-word", + backgroundColor: "rgba(40,40,40,1)", + zIndex: activeDialog === "codeeditor" ? 1200 : 1100, + }} + + setOptions={{ + enableBasicAutocompletion: true, + enableLiveAutocompletion: true, + enableSnippets: true, + showLineNumbers: true, + tabSize: 4, + fontFamily: "'JetBrains Mono', Consolas, monospace", + useSoftTabs: true + }} + /> + ) + } + return ( { */} { isFileEditor ? null :
- {selectedAction.name === "execute_python" ? + {selectedAction?.name === "execute_python" ? Run Python Code @@ -1300,7 +1350,7 @@ const CodeEditor = (props) => { maxHeight: 650, }} > - {actionlist.map((innerdata) => { + {actionlist?.map((innerdata) => { const icon = innerdata.type === "action" ? ( diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index 7d9d2d0c..afd1fc7b 100755 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -1,5 +1,5 @@ /* eslint-disable react/no-multi-comp */ -import React, { useState, useEffect, useLayoutEffect, memo, useMemo, useRef } from "react"; +import React, { useState, useEffect, useLayoutEffect, memo, useMemo, useRef, useContext } from "react"; import ReactDOM from "react-dom" import theme from "../theme.jsx"; @@ -72,6 +72,8 @@ import { ButtonGroup, } from "@mui/material"; +import CodeIcon from '@mui/icons-material/Code'; + import { Folder as FolderIcon, VerifiedUser as VerifiedUserIcon, @@ -151,6 +153,8 @@ import PaperComponent from "../components/PaperComponent.jsx" import ExtraApps from "../components/ExtraApps.jsx" import EditWorkflow from "../components/EditWorkflow.jsx" import { act } from "react"; +import { Context } from "../context/ContextApi.jsx"; +import transitions from "@material-ui/core/styles/transitions.js"; // import AppStats from "../components/AppStats.jsx"; const noImage = "/public/no_image.png"; @@ -396,6 +400,9 @@ const AngularWorkflow = (defaultprops) => { props.match = {} props.match.params = params + const { leftSideBarOpenByClick, windowWidth } = useContext(Context) + const [workflowAsCode, setWorkflowAsCode] = useState(false); + var to_be_copied = ""; const [firstrequest, setFirstrequest] = React.useState(true); const [cystyle] = useState(cytoscapestyle); @@ -801,6 +808,15 @@ const releaseToConnectLabel = "Release to Connect" }) } + useEffect(() => { + console.log("Loaded workflow: ", workflow) + if (workflow.actions?.length == 1) { + if (workflow.actions[0].app_id == "3e320a20966d33c9b7e6790b2705f0bf") { + setWorkflowAsCode(true); + } + } + }, [workflow]); + // Event for making sure app is correct useEffect(() => { if (selectedApp === undefined || selectedApp === null && selectedApp.app_name === undefined) { @@ -3226,6 +3242,10 @@ const releaseToConnectLabel = "Release to Connect" } cy.add(nodeData) + + if (workflowAsCode) { + setWorkflowAsCode(false) + } // Wait 100ms then add a style for it setTimeout(() => { @@ -6318,6 +6338,10 @@ const releaseToConnectLabel = "Release to Connect" } } + if (nodedata.app_id == "3e320a20966d33c9b7e6790b2705f0bf") { + setWorkflowAsCode(true); + } + if (nodedata.decorator !== true && nodedata.attachedTo === undefined) { var newdata = JSON.parse(JSON.stringify(nodedata)) newdata.large_image = "" @@ -9002,7 +9026,7 @@ const releaseToConnectLabel = "Release to Connect" }); }; - const parsedHeight = isMobile ? bodyHeight - appBarSize * 4 : bodyHeight - appBarSize - 50 + const parsedHeight = isMobile ? bodyHeight - appBarSize * 4 : userdata?.support ? bodyHeight - 80 : bodyHeight - appBarSize - 50 const appViewStyle = { marginLeft: 5, marginRight: 5, @@ -9342,6 +9366,7 @@ const releaseToConnectLabel = "Release to Connect" minHeight: parsedHeight, maxHeight: parsedHeight, overflow: "hidden", + paddingTop: userdata?.support ? 10 : 0, }} > {thisview} @@ -11016,11 +11041,14 @@ const releaseToConnectLabel = "Release to Connect" iterations += 1; } + // Remove on the end as we don't want to remove everything results = results.filter((data) => data.id !== action.id) results = results.filter((data) => data.type === "ACTION" || data.app_name === "Shuffle Workflow" || data.app_name === "User Input") results.push({ label: "Execution Argument", type: "INTERNAL" }) + console.log(results) + return results } @@ -11050,9 +11078,9 @@ const releaseToConnectLabel = "Release to Connect" const minSize = 370 var rightsidebarStyle = { position: "fixed", - top: appBarSize + 25, + top: userdata?.support ? appBarSize - 35 : appBarSize + 25, right: 25, - height: "80vh", + height: userdata?.support ? "90vh": "80vh", width: isMobile ? "100%" : minSize, minWidth: minSize, maxWidth: 600, @@ -15961,24 +15989,33 @@ const releaseToConnectLabel = "Release to Connect" const cytoscapeViewWidths = isMobile ? 50 : 950; const bottomBarStyle = { position: "fixed", - right: isMobile ? 20 : 20, - bottom: isMobile ? undefined : 0, - top: isMobile ? appBarSize + 55 : undefined, - left: isMobile ? undefined : leftBarSize, - minWidth: cytoscapeViewWidths, - maxWidth: cytoscapeViewWidths, + transition: "all 0.3s ease", + minWidth: userdata?.support && windowWidth < 1600 && leftSideBarOpenByClick ? 800 : cytoscapeViewWidths, + maxWidth: userdata?.support && windowWidth < 1600 && leftSideBarOpenByClick ? 800 : cytoscapeViewWidths, marginLeft: 20, - marginBottom: 20, + marginBottom: 30, zIndex: 10, - }; + transform: isMobile + ? `translateX(20px)` + : `translateX(${userdata?.support + ? leftSideBarOpenByClick ? 340 : 330 + : leftBarSize}px)`, + top: isMobile ? appBarSize + 55 : undefined, + bottom: isMobile ? undefined : 0, +}; const topBarStyle = { position: "fixed", - right: 0, - left: isMobile ? 20 : leftBarSize + 20, - top: isMobile ? 30 : appBarSize + 20, - pointerEvents: "none", - } + top: isMobile ? 30 : userdata?.support ? appBarSize - 20 : appBarSize + 20, + pointerEvents: "none", + transition: "transform 0.3s ease", + transform: isMobile + ? `translateX(20px)` + : userdata?.support + ? `translateX(${leftSideBarOpenByClick ? 340 : 330}px)` + : `translateX(${leftBarSize + 20}px)`, +}; + @@ -15988,9 +16025,9 @@ const releaseToConnectLabel = "Release to Connect" return null } - if (userdata.active_org === undefined || userdata.active_org === null) { - return null - } + if (userdata.active_org === undefined || userdata.active_org === null) { + return null + } const isCorrectOrg = workflow.public === true || userdata.active_org.id === undefined || userdata.active_org.id === null || workflow.org_id === null || workflow.org_id === undefined || workflow.org_id.length === 0 || userdata.active_org.id === workflow.org_id @@ -16028,6 +16065,26 @@ const releaseToConnectLabel = "Release to Connect" pointerEvents: "none", }}>{workflow.name} + {workflowAsCode && ( + + + + )} {!distributedFromParent ? isCorrectOrg ? null : @@ -16706,11 +16763,12 @@ const releaseToConnectLabel = "Release to Connect" style={{ border: "1px solid rgba(255,255,255,0.1)", position: "absolute", - bottom: 130, - left: leftBarSize+20, + bottom: 140, + left: userdata?.support ? leftSideBarOpenByClick ? 620 : 435 : leftBarSize + 20, color: "white", padding: 10, borderRadius: theme.palette?.borderRadius, + transition: "left 0.3s ease, top 0.3s ease", }} > @@ -17488,7 +17546,7 @@ const releaseToConnectLabel = "Release to Connect" // console.log(allowList, userdata.public_username) const leftView = workflow.public === true ? -
+
+
@@ -20330,7 +20388,7 @@ const releaseToConnectLabel = "Release to Connect" wheelSensitivity={0.25} style={{ width: cytoscapeWidth, - height: bodyHeight - appBarSize - 5, + height: userdata?.support ? bodyHeight - 20 : bodyHeight - appBarSize - 5, backgroundColor: theme.palette.surfaceColor, }} stylesheet={cystyle} @@ -22225,7 +22283,7 @@ const releaseToConnectLabel = "Release to Connect" const loadedCheck = isLoaded && workflowDone ? ( -
+
{newView} @@ -22359,7 +22417,7 @@ const releaseToConnectLabel = "Release to Connect" />
) : ( -
+
Loading Workflow & Apps... diff --git a/frontend/src/views/SetAuthentication.jsx b/frontend/src/views/SetAuthentication.jsx index bb99e037..63aec23c 100755 --- a/frontend/src/views/SetAuthentication.jsx +++ b/frontend/src/views/SetAuthentication.jsx @@ -302,7 +302,7 @@ const SetAuthentication = (props) => { } return ( -
+
{ return { - datagrid: { - border: 0, - "& .MuiDataGrid-columnsContainer": { - backgroundColor: - theme?.palette?.type === "light" ? "#fafafa" : theme?.palette?.inputColor, - }, - "& .MuiDataGrid-iconSeparator": { - display: "none", - }, - "& .MuiDataGrid-colCell, .MuiDataGrid-cell": { - borderRight: `1px solid ${ - theme?.palette?.type === "light" ? "white" : "#303030" - }`, - }, - "& .MuiDataGrid-columnsContainer, .MuiDataGrid-cell": { - borderBottom: `1px solid ${ - theme?.palette?.type === "light" ? "#f0f0f0" : "#303030" - }`, - }, - "& .MuiDataGrid-cell": { - color: - theme?.palette?.type === "light" ? "white" : "rgba(255,255,255,0.65)", - }, - "& .MuiPaginationItem-root, .MuiTablePagination-actions, .MuiTablePagination-caption": - { - borderRadius: 0, - color: "white", - }, - }, - } + datagrid: { + border: 0, + "& .MuiDataGrid-columnsContainer": { + backgroundColor: + theme?.palette?.type === "light" ? "#fafafa" : theme?.palette?.inputColor, + }, + "& .MuiDataGrid-iconSeparator": { + display: "none", + }, + "& .MuiDataGrid-colCell, .MuiDataGrid-cell": { + borderRight: `1px solid ${theme?.palette?.type === "light" ? "white" : "#303030" + }`, + }, + "& .MuiDataGrid-columnsContainer, .MuiDataGrid-cell": { + borderBottom: `1px solid ${theme?.palette?.type === "light" ? "#f0f0f0" : "#303030" + }`, + }, + "& .MuiDataGrid-cell": { + color: + theme?.palette?.type === "light" ? "white" : "rgba(255,255,255,0.65)", + }, + "& .MuiPaginationItem-root, .MuiTablePagination-actions, .MuiTablePagination-caption": + { + borderRadius: 0, + color: "white", + }, + }, + } }) // Takes an action in Shuffle and @@ -157,7 +155,7 @@ export const GetIconInfo = (action) => { "preview", ], }, - { key: "add", values: ["add", "accept", ] }, + { key: "add", values: ["add", "accept",] }, { key: "delete", values: ["delete", "remove", "clear", "clean", "dismiss",] }, { key: "send", @@ -175,7 +173,7 @@ export const GetIconInfo = (action) => { }, { key: "repeat", - values: ["repeat", "retry", "pause", "skip", "copy", "replicat", "demo", ], + values: ["repeat", "retry", "pause", "skip", "copy", "replicat", "demo",], }, { key: "execute", values: ["execute", "run", "play", "raise"] }, { key: "extract", values: ["extract", "unpack", "decompress", "open"] }, @@ -208,8 +206,8 @@ export const GetIconInfo = (action) => { var selectedKey = "" if (action.app_name == "Integration Framework") { - selectedKey = "magic" - }else if (action.name === undefined || action.name === null) { + selectedKey = "magic" + } else if (action.name === undefined || action.name === null) { } else { const actionname = action.name.toLowerCase() for (var key in iconList) { @@ -231,27 +229,27 @@ export const GetIconInfo = (action) => { const defaultColor = "#f76b1c"; const defaultGradient = ["#fad961", "#f76b1c"]; const parsedIcons = { - magic: { + magic: { icon: "M7.5 5.6 10 7 8.6 4.5 10 2 7.5 3.4 5 2l1.4 2.5L5 7zm12 9.8L17 14l1.4 2.5L17 19l2.5-1.4L22 19l-1.4-2.5L22 14zM22 2l-2.5 1.4L17 2l1.4 2.5L17 7l2.5-1.4L22 7l-1.4-2.5zm-7.63 5.29a.9959.9959 0 0 0-1.41 0L1.29 18.96c-.39.39-.39 1.02 0 1.41l2.34 2.34c.39.39 1.02.39 1.41 0L16.7 11.05c.39-.39.39-1.02 0-1.41zm-1.03 5.49-2.12-2.12 2.44-2.44 2.12 2.12z", iconColor: "white", iconBackgroundColor: "red", originalIcon: "", - fillGradient: ["#FF0000", "#FF7F00", "#FFFF00", "#00FF00", "#0000FF", "#4B0082", "#8A2BE2"], - }, - communication: { + fillGradient: ["#FF0000", "#FF7F00", "#FFFF00", "#00FF00", "#0000FF", "#4B0082", "#8A2BE2"], + }, + communication: { icon: "M9.89516 7.71433H8.60945V5.1429H9.89516V7.71433ZM9.89516 10.2858H8.60945V9.00004H9.89516V10.2858ZM14.3952 2.57147H4.10944C3.76845 2.57147 3.44143 2.70693 3.20031 2.94805C2.95919 3.18917 2.82373 3.51619 2.82373 3.85719V15.4286L5.39516 12.8572H14.3952C14.7362 12.8572 15.0632 12.7217 15.3043 12.4806C15.5454 12.2395 15.6809 11.9125 15.6809 11.5715V3.85719C15.6809 3.14361 15.1023 2.57147 14.3952 2.57147Z", iconColor: "white", iconBackgroundColor: "#8acc3f", originalIcon: "", fillGradient: ["#8acc3f", "#459622"], - }, - cases: { + }, + cases: { icon: "M15.6408 8.39233H18.0922V10.0287H15.6408V8.39233ZM0.115234 8.39233H2.56663V10.0287H0.115234V8.39233ZM9.92083 0.21051V2.66506H8.28656V0.21051H9.92083ZM3.31839 2.25596L5.05889 4.00687L3.89856 5.16051L2.15807 3.42596L3.31839 2.25596ZM13.1485 3.99869L14.8808 2.25596L16.0493 3.42596L14.3088 5.16051L13.1485 3.99869ZM9.10369 4.30142C10.404 4.30142 11.651 4.81863 12.5705 5.73926C13.4899 6.65989 14.0065 7.90854 14.0065 9.21051C14.0065 11.0269 13.0178 12.6141 11.5551 13.4651V14.9378C11.5551 15.1548 11.469 15.3629 11.3158 15.5163C11.1625 15.6698 10.9547 15.756 10.738 15.756H7.46943C7.25271 15.756 7.04487 15.6698 6.89163 15.5163C6.73839 15.3629 6.6523 15.1548 6.6523 14.9378V13.4651C5.18963 12.6141 4.2009 11.0269 4.2009 9.21051C4.2009 7.90854 4.71744 6.65989 5.63689 5.73926C6.55635 4.81863 7.80339 4.30142 9.10369 4.30142ZM10.738 16.5741V17.3923C10.738 17.6093 10.6519 17.8174 10.4986 17.9709C10.3454 18.1243 10.1375 18.2105 9.92083 18.2105H8.28656C8.06984 18.2105 7.862 18.1243 7.70876 17.9709C7.55552 17.8174 7.46943 17.6093 7.46943 17.3923V16.5741H10.738ZM8.28656 14.1196H9.92083V12.3769C11.3345 12.0169 12.3722 10.7323 12.3722 9.21051C12.3722 8.34253 12.0279 7.5101 11.4149 6.89634C10.8019 6.28259 9.97056 5.93778 9.10369 5.93778C8.23683 5.93778 7.40546 6.28259 6.79249 6.89634C6.17953 7.5101 5.83516 8.34253 5.83516 9.21051C5.83516 10.7323 6.87292 12.0169 8.28656 12.3769V14.1196Z", iconColor: "white", iconBackgroundColor: "#8acc3f", originalIcon: "", fillGradient: ["#8acc3f", "#459622"], - }, + }, cache_add: { icon: "M11 3C6.58 3 3 4.79 3 7C3 9.21 6.58 11 11 11C15.42 11 19 9.21 19 7C19 4.79 15.42 3 11 3ZM3 9V12C3 14.21 6.58 16 11 16C15.42 16 19 14.21 19 12V9C19 11.21 15.42 13 11 13C6.58 13 3 11.21 3 9ZM3 14V17C3 19.21 6.58 21 11 21C12.41 21 13.79 20.81 15 20.46V17.46C13.79 17.81 12.41 18 11 18C6.58 18 3 16.21 3 14ZM20 14V17H17V19H20V22H22V19H25V17H22V14", iconColor: "white", @@ -421,67 +419,67 @@ const chipStyle = { }; export const collapseField = (field) => { - if (field === undefined || field === null) { - return true - } + if (field === undefined || field === null) { + return true + } - if (field.name === "headers" || field.name === "cookies") { - return true - } + if (field.name === "headers" || field.name === "cookies") { + return true + } - if (field.type === "array") { - return true - } + if (field.type === "array") { + return true + } - // If more than 10 keys in object, collapse - if (field.type === "object") { - if (Object.keys(field.src).length > 7) { - return true - } - } + // If more than 10 keys in object, collapse + if (field.type === "object") { + if (Object.keys(field.src).length > 7) { + return true + } + } - return false + return false } export const validateJson = (showResult) => { - if (showResult === undefined || showResult === null) { - return { - valid: false, - result: "", - } - } + if (showResult === undefined || showResult === null) { + return { + valid: false, + result: "", + } + } - if (typeof showResult === 'string') { - showResult = showResult.split(" False").join(" false") - showResult = showResult.split(" True").join(" true") + if (typeof showResult === 'string') { + showResult = showResult.split(" False").join(" false") + showResult = showResult.split(" True").join(" true") - showResult.replaceAll("False,", "false,") - showResult.replaceAll("True,", "true,") - } + showResult.replaceAll("False,", "false,") + showResult.replaceAll("True,", "true,") + } - if (typeof showResult === "object" || typeof showResult === "array") { - return { - valid: true, - result: showResult, - } - } + if (typeof showResult === "object" || typeof showResult === "array") { + return { + valid: true, + result: showResult, + } + } - if (showResult[0] === "\"") { - return { - valid: false, - result: showResult, - } - } + if (showResult[0] === "\"") { + return { + valid: false, + result: showResult, + } + } var jsonvalid = true try { if (!showResult.includes("{") && !showResult.includes("[")) { jsonvalid = false - return { - valid: jsonvalid, - result: showResult, - }; + return { + valid: jsonvalid, + result: showResult, + }; } } catch (e) { @@ -498,94 +496,94 @@ export const validateJson = (showResult) => { var result = showResult; try { - result = jsonvalid ? JSON.parse(showResult, {"storeAsString": true}) : showResult; + result = jsonvalid ? JSON.parse(showResult, { "storeAsString": true }) : showResult; } catch (e) { ////console.log("Failed parsing JSON even though its valid: ", e) jsonvalid = false; } - if (jsonvalid === false) { + if (jsonvalid === false) { - if (typeof showResult === 'string') { - showResult = showResult.trim() - } + if (typeof showResult === 'string') { + showResult = showResult.trim() + } - try { - var newstr = showResult.replaceAll("'", '"') + try { + var newstr = showResult.replaceAll("'", '"') - // Basic workarounds for issues with Python Dicts -> JSON - if (newstr.includes(": None")) { - newstr = newstr.replaceAll(": None", ': null') - } + // Basic workarounds for issues with Python Dicts -> JSON + if (newstr.includes(": None")) { + newstr = newstr.replaceAll(": None", ': null') + } - if (newstr.includes("[\"{") && newstr.includes("}\"]")) { - newstr = newstr.replaceAll("[\"{", '[{') - newstr = newstr.replaceAll("}\"]", '}]') - } + if (newstr.includes("[\"{") && newstr.includes("}\"]")) { + newstr = newstr.replaceAll("[\"{", '[{') + newstr = newstr.replaceAll("}\"]", '}]') + } - if (newstr.includes("{\"[") && newstr.includes("]\"}")) { - newstr = newstr.replaceAll("{\"[", '[{') - newstr = newstr.replaceAll("]\"}", '}]') - } + if (newstr.includes("{\"[") && newstr.includes("]\"}")) { + newstr = newstr.replaceAll("{\"[", '[{') + newstr = newstr.replaceAll("]\"}", '}]') + } - result = JSON.parse(newstr) - jsonvalid = true - } catch (e) { + result = JSON.parse(newstr) + jsonvalid = true + } catch (e) { - //console.log("Failed parsing JSON even though its valid (2): ", e) - jsonvalid = false - } - } + //console.log("Failed parsing JSON even though its valid (2): ", e) + jsonvalid = false + } + } - if (jsonvalid && typeof result === "number") { - jsonvalid = false - } + if (jsonvalid && typeof result === "number") { + jsonvalid = false + } - // This is where we start recursing - if (jsonvalid) { - // Check fields if they can be parsed too - try { - for (const [key, value] of Object.entries(result)) { - if (typeof value === "string" && (value.startsWith("{") || value.startsWith("["))) { - //console.log("CHECKING STRING: ", value) + // This is where we start recursing + if (jsonvalid) { + // Check fields if they can be parsed too + try { + for (const [key, value] of Object.entries(result)) { + if (typeof value === "string" && (value.startsWith("{") || value.startsWith("["))) { + //console.log("CHECKING STRING: ", value) - const inside_result = validateJson(value) - if (inside_result.valid) { - //console.log("INSIDE RESULT: ", inside_result.result) + const inside_result = validateJson(value) + if (inside_result.valid) { + //console.log("INSIDE RESULT: ", inside_result.result) - if (typeof inside_result.result === "string") { - const newres = JSON.parse(inside_result.result) + if (typeof inside_result.result === "string") { + const newres = JSON.parse(inside_result.result) - result[key] = newres - } else { - result[key] = inside_result.result - } - } - } else { + result[key] = newres + } else { + result[key] = inside_result.result + } + } + } else { - // Usually only reaches here if raw array > dict > value - if (typeof showResult !== "array") { - for (const [subkey, subvalue] of Object.entries(value)) { - if (typeof subvalue === "string" && (subvalue.startsWith("{") || subvalue.startsWith("["))) { - const inside_result = validateJson(subvalue) - if (inside_result.valid) { - if (typeof inside_result.result === "string") { - const newres = JSON.parse(inside_result.result) - result[key][subkey] = newres - } else { - result[key][subkey] = inside_result.result - } - } - } + // Usually only reaches here if raw array > dict > value + if (typeof showResult !== "array") { + for (const [subkey, subvalue] of Object.entries(value)) { + if (typeof subvalue === "string" && (subvalue.startsWith("{") || subvalue.startsWith("["))) { + const inside_result = validateJson(subvalue) + if (inside_result.valid) { + if (typeof inside_result.result === "string") { + const newres = JSON.parse(inside_result.result) + result[key][subkey] = newres + } else { + result[key][subkey] = inside_result.result + } + } + } - } - } - } - } - } catch (e) { - //console.log("Failed parsing inside json subvalues: ", e) - } - } + } + } + } + } + } catch (e) { + //console.log("Failed parsing inside json subvalues: ", e) + } + } return { valid: jsonvalid, @@ -608,7 +606,7 @@ const useDropzoneStyles = () => { //Wrapper for the dropzone component const DropzoneWrapper = memo(({ onDrop, WorkflowView }) => { - const dropzoneStyles = useDropzoneStyles(); + const dropzoneStyles = useDropzoneStyles(); return ( @@ -621,7 +619,7 @@ const Workflows = (props) => { const { globalUrl, isLoggedIn, isLoaded, userdata, checkLogin } = props; document.title = "Shuffle - Workflows"; - let navigate = useNavigate(); + let navigate = useNavigate(); const classes = useStyles(theme) const imgSize = 60; @@ -665,14 +663,14 @@ const Workflows = (props) => { const [view, setView] = React.useState("grid"); const [filters, setFilters] = React.useState([]); const [submitLoading, setSubmitLoading] = React.useState(false); - const [actionImageList, setActionImageList] = React.useState([{"large_image": ""}]) + const [actionImageList, setActionImageList] = React.useState([{ "large_image": "" }]) const [firstLoad, setFirstLoad] = React.useState(true); const [showMoreClicked, setShowMoreClicked] = React.useState(false); const [usecases, setUsecases] = React.useState([]); const [allUsecases, setAllUsecases] = React.useState({ - "success": false, - }); + "success": false, + }); const [appFramework, setAppFramework] = React.useState({}); const [drawerOpen, setDrawerOpen] = React.useState(false) const [videoViewOpen, setVideoViewOpen] = React.useState(false) @@ -682,54 +680,54 @@ const Workflows = (props) => { const [apps, setApps] = React.useState([]); - const drawerWidth = drawerOpen ? 325 : 0 + const drawerWidth = drawerOpen ? 325 : 0 - const sidebarKey = "getting_started_sidebar" - if (isLoggedIn === true && gettingStartedItems.length === 0 && (userdata.tutorials !== undefined && userdata.tutorials !== null && userdata.tutorials.length > 0) && workflowDone === true) { + const sidebarKey = "getting_started_sidebar" + if (isLoggedIn === true && gettingStartedItems.length === 0 && (userdata.tutorials !== undefined && userdata.tutorials !== null && userdata.tutorials.length > 0) && workflowDone === true) { - const activeFiltered = userdata.tutorials.filter((item) => item.active === true) - if (activeFiltered.length > 0) { - var newfiltered = [] - for (var key in activeFiltered) { - if (activeFiltered[key].name === "Discover Usecases") { - if (workflows.length > 1) { - activeFiltered[key].done = true - activeFiltered[key].description = `${workflows.length} workflows created` - } - } + const activeFiltered = userdata.tutorials.filter((item) => item.active === true) + if (activeFiltered.length > 0) { + var newfiltered = [] + for (var key in activeFiltered) { + if (activeFiltered[key].name === "Discover Usecases") { + if (workflows.length > 1) { + activeFiltered[key].done = true + activeFiltered[key].description = `${workflows.length} workflows created` + } + } - newfiltered.push(activeFiltered[key]) - } - setGettingStartedItems(activeFiltered) + newfiltered.push(activeFiltered[key]) + } + setGettingStartedItems(activeFiltered) - /* - const sidebar = localStorage.getItem(sidebarKey) - if (sidebar === null || sidebar === undefined) { - console.log("No sidebar defined") - - localStorage.setItem(sidebarKey, "open"); - setDrawerOpen(true) - } else { - if (sidebar === "open") { - setDrawerOpen(true) - } else { - setDrawerOpen(false) - } - } - */ - } + /* + const sidebar = localStorage.getItem(sidebarKey) + if (sidebar === null || sidebar === undefined) { + console.log("No sidebar defined") + + localStorage.setItem(sidebarKey, "open"); + setDrawerOpen(true) + } else { + if (sidebar === "open") { + setDrawerOpen(true) + } else { + setDrawerOpen(false) + } + } + */ + } - } + } const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; const findWorkflow = (filters) => { - console.log("Using filters: ", filters) + console.log("Using filters: ", filters) if (filters.length === 0) { setFilteredWorkflows(workflows); - handleKeysetting(allUsecases, workflows) + handleKeysetting(allUsecases, workflows) return; } @@ -744,10 +742,10 @@ const Workflows = (props) => { ); } - if (curWorkflow.tags !== undefined && curWorkflow.tags !== null && curWorkflow.tags.length > 0) { - // Make them all lowercase - curWorkflow.tags = curWorkflow.tags.map((tag) => tag.toLowerCase()) - } + if (curWorkflow.tags !== undefined && curWorkflow.tags !== null && curWorkflow.tags.length > 0) { + // Make them all lowercase + curWorkflow.tags = curWorkflow.tags.map((tag) => tag.toLowerCase()) + } if (found.every((v) => v !== true)) { @@ -767,18 +765,18 @@ const Workflows = (props) => { } else if (curWorkflow.org_id === filter) { return true; } else if (curWorkflow.usecase_ids !== undefined && curWorkflow.usecase_ids !== null && curWorkflow.usecase_ids.length > 0) { - // Check if the usecase is the right category - for (var key in usecases) { - if (usecases[key].name.toLowerCase() !== newfilter) { - continue - } + // Check if the usecase is the right category + for (var key in usecases) { + if (usecases[key].name.toLowerCase() !== newfilter) { + continue + } - for (var subkey in usecases[key].list) { - if (curWorkflow.usecase_ids.includes(usecases[key].list[subkey].name)) { - return true - } - } - } + for (var subkey in usecases[key].list) { + if (curWorkflow.usecase_ids.includes(usecases[key].list[subkey].name)) { + return true + } + } + } } else if ( curWorkflow.actions !== null && curWorkflow.actions !== undefined @@ -793,7 +791,7 @@ const Workflows = (props) => { return true; } } - } + } return false; }); @@ -805,59 +803,59 @@ const Workflows = (props) => { } } - console.log("Changing workflow filter, and finding new usecase mappings!") + console.log("Changing workflow filter, and finding new usecase mappings!") if (newWorkflows.length !== workflows.length) { - handleKeysetting(allUsecases, newWorkflows) + handleKeysetting(allUsecases, newWorkflows) setFilteredWorkflows(newWorkflows); } }; - const getApps = () => { - try { - const appstorage = localStorage.getItem("apps") - const privateapps = JSON.parse(appstorage) - setApps(privateapps) - } catch (e) { - //console.log("Failed to get apps from localstorage: ", e) - } + const getApps = () => { + try { + const appstorage = localStorage.getItem("apps") + const privateapps = JSON.parse(appstorage) + setApps(privateapps) + } catch (e) { + //console.log("Failed to get apps from localstorage: ", e) + } - fetch(`${globalUrl}/api/v1/apps`, { - method: "GET", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, - credentials: "include", - }) - .then((response) => { - if (response.status !== 200) { - console.log("Status not 200 for apps :O!"); - } - - return response.json(); - }) - .then((responseJson) => { - setApps(responseJson); - }) - .catch((error) => { - console.log("App loading error: "+error.toString()); - }); - } + fetch(`${globalUrl}/api/v1/apps`, { + method: "GET", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + credentials: "include", + }) + .then((response) => { + if (response.status !== 200) { + console.log("Status not 200 for apps :O!"); + } + + return response.json(); + }) + .then((responseJson) => { + setApps(responseJson); + }) + .catch((error) => { + console.log("App loading error: " + error.toString()); + }); + } const addFilter = (data) => { if (data === null || data === undefined) { - console.log("No filter data") + console.log("No filter data") return; } if (data.includes("<") && data.includes(">")) { - console.log("Filter includes < or >") + console.log("Filter includes < or >") return; } if (filters.includes(data) || filters.includes(data.toLowerCase())) { - console.log("Filter already has the data") + console.log("Filter already has the data") return; } @@ -972,7 +970,7 @@ const Workflows = (props) => { you, randomize ID's and remove your authentication. - The published workflow is yours, and you can always change your public workflows after they are released. + The published workflow is yours, and you can always change your public workflows after they are released.
- + + + {view === "list" && ( + { - // ); - // } + // ] + + // function TourButton() { + // const tour = useContext(ShepherdTourContext); + + // return ( + // + // ); + // } const WorkflowView = memo(() => { if (workflows.length === 0) { } - var workflowDelay = -150 - var appDelay = -75 + var workflowDelay = -150 + var appDelay = -75 - const foundPriority = userdata === undefined || userdata === null || userdata.priorities === undefined || userdata.priorities === null ? null : userdata.priorities.find(prio => prio.type === "usecase" && prio.active === true) + const foundPriority = userdata === undefined || userdata === null || userdata.priorities === undefined || userdata.priorities === null ? null : userdata.priorities.find(prio => prio.type === "usecase" && prio.active === true) return (
- -
- {!isMobile && !hasWorkflows && usecases !== null && usecases !== undefined && usecases.length > 0 ? -
- {usecases.map((usecase, index) => { - if (usecase.name === "5. Verify") { - return null - } - const percentDone = usecase.matches.length > 0 ? parseInt(usecase.matches.length/usecase.list.length*100) : 0 - if (percentDone === 0) { - usecase = findMatches(usecase, workflows) - } +
+ {!isMobile && !hasWorkflows && usecases !== null && usecases !== undefined && usecases.length > 0 ? +
+ {usecases.map((usecase, index) => { + if (usecase.name === "5. Verify") { + return null + } - return ( - { - console.log("Filters: ", filters, usecase.name.toLowerCase()) - if (!filters.includes(usecase.name.toLowerCase())) { - addFilter(usecase.name) - } else { - removeFilter(filters.indexOf(usecase.name.toLowerCase())) - } + const percentDone = usecase.matches.length > 0 ? parseInt(usecase.matches.length / usecase.list.length * 100) : 0 + if (percentDone === 0) { + usecase = findMatches(usecase, workflows) + } - }} - > - - - {usecase.name} - - - {usecase.matches.length}/{usecase.list.length} - - - - ) - })} -
- : null} -
+ return ( + { + console.log("Filters: ", filters, usecase.name.toLowerCase()) + if (!filters.includes(usecase.name.toLowerCase())) { + addFilter(usecase.name) + } else { + removeFilter(filters.indexOf(usecase.name.toLowerCase())) + } + + }} + > + + + {usecase.name} + + + {usecase.matches.length}/{usecase.list.length} + + + + ) + })} +
+ : null} +
{!isMobile && - actionImageList !== undefined && + actionImageList !== undefined && actionImageList !== null && actionImageList.length > 0 ? (
{ //data.large_image = theme.palette.defaultImage } - if (data.app_name.toLowerCase() === "integration framework") { - return null - } + if (data.app_name.toLowerCase() === "integration framework") { + return null + } - const returnData = - - { - console.log("FILTER: ", data); - addFilter(data.app_name); - }} - > - - -
- {data.app_name} -
-
-
-
-
+ const returnData = + + { + console.log("FILTER: ", data); + addFilter(data.app_name); + }} + > + + +
+ {data.app_name} +
+
+
+
+
- if (firstLoad) { - appDelay += 75 - } else { - //appDelay = 0 - return returnData - } + if (firstLoad) { + appDelay += 75 + } else { + //appDelay = 0 + return returnData + } return ( - - {/**/} - {returnData} - {/**/} - - ); - })} + + {/**/} + {returnData} + {/**/} + + ); + })}
) : null} - {userdata.priorities !== undefined && userdata.priorities !== null && userdata.priorities.length > 0 && userdata.priorities[0].name.includes("CPU") && userdata.priorities[0].active === true ? -
0 && userdata.priorities[0].name.includes("CPU") && userdata.priorities[0].active === true ? +
-
- - {userdata.priorities[0].name} - -
- - +
+ + {userdata.priorities[0].name} + +
+ + {userdata.priorities[0].description}
-
-
- - {/* +
+
+ + {/* */} -
-
- : null} +
+
+ : null} - {foundPriority != null && workflows.length < 6 ? - - : null} + {foundPriority != null && workflows.length < 6 ? + + : null} -
- {view === "grid" ? ( - - {/**/} - - {/**/} +
+ {view === "grid" ? ( + + {/**/} + + {/**/} - {filteredWorkflows.map((data, index) => { - // Shouldn't be a part of this list - if (data.public === true) { - return null - } + {filteredWorkflows.map((data, index) => { + // Shouldn't be a part of this list + if (data.public === true) { + return null + } - if (firstLoad) { - workflowDelay += 75 - } else { - return ( - - - - ) - } + if (firstLoad) { + workflowDelay += 75 + } else { + return ( + + + + ) + } - return ( - - {/**/} - - - - {/**/} - - ) - })} - - ) : ( - - )} -
+ return ( + + {/**/} + + + + {/**/} + + ) + })} +
+ ) : ( + + )} +
- {foundPriority != null && filteredWorkflows.length > 6 ? - - : null} + {foundPriority != null && filteredWorkflows.length > 6 ? + + : null} -
-
-
+
+
+
); }); @@ -3994,7 +4004,7 @@ const Workflows = (props) => { const workflowDownloadModalOpen = loadWorkflowsModalOpen ? ( {}} + onClose={() => { }} PaperProps={{ style: { backgroundColor: theme.palette.surfaceColor, @@ -4121,166 +4131,166 @@ const Workflows = (props) => { ) : null; - //const + //const //const [percentDone, setPercentDone] = React.useState(0) - const percentDone = gettingStartedItems.filter((item) => item.done).length / gettingStartedItems.length * 100 + const percentDone = gettingStartedItems.filter((item) => item.done).length / gettingStartedItems.length * 100 - const GettingStartedItem = ({item, index}) => { - const [clicked, setClicked] = React.useState(false) - const doneIcon = item.done ? : + const GettingStartedItem = ({ item, index }) => { + const [clicked, setClicked] = React.useState(false) + const doneIcon = item.done ? : - return ( -
setClicked(true)} - > - - {doneIcon} {index + 1}. {item.name} - - {clicked ? - - - {item.description} - - - - - - : - null - } -
- ) - } + return ( +
setClicked(true)} + > + + {doneIcon} {index + 1}. {item.name} + + {clicked ? + + + {item.description} + + + + + + : + null + } +
+ ) + } - const gettingStartedDrawer = true == true ? null : - -
- - Getting Started - - - { - e.preventDefault(); - setDrawerOpen(false) + const gettingStartedDrawer = true == true ? null : + +
+ + Getting Started + + + { + e.preventDefault(); + setDrawerOpen(false) - localStorage.setItem(sidebarKey, "closed"); - }} - > - - - -
-
- - Setup progress: {isNaN(percentDone) ? 0 : percentDone}% - + localStorage.setItem(sidebarKey, "closed"); + }} + > + + + +
+
+ + Setup progress: {isNaN(percentDone) ? 0 : percentDone}% + - + - - Follow these steps to get you up and running! - - - { - setVideoViewOpen(true) - }}> - Watch 2-min introduction video - -
-
- {gettingStartedItems.map((item, index) => { - return ( - - ) - })} -
-
- - const videoView = - { - setVideoViewOpen(false) - }} - PaperProps={{ - style: { - backgroundColor: theme.palette.surfaceColor, - color: "white", - minWidth: 560, - minHeight: 415, - textAlign: "center", - }, - }} - > - - Welcome to Shuffle! - + + Follow these steps to get you up and running! + + + { + setVideoViewOpen(true) + }}> + Watch 2-min introduction video + +
+
+ {gettingStartedItems.map((item, index) => { + return ( + + ) + })} +
+
- - { - e.preventDefault(); - setVideoViewOpen(false) - }} - > - - - + const videoView = + { + setVideoViewOpen(false) + }} + PaperProps={{ + style: { + backgroundColor: theme.palette.surfaceColor, + color: "white", + minWidth: 560, + minHeight: 415, + textAlign: "center", + }, + }} + > + + Welcome to Shuffle! + - - + + { + e.preventDefault(); + setVideoViewOpen(false) + }} + > + + + + + +
const loadedCheck = isLoaded && isLoggedIn && workflowDone ? (
- {/* + {/* */} - + {/*modalView*/} {deleteModal} {exportVerifyModal} @@ -4298,34 +4308,34 @@ const Workflows = (props) => {
: null*/} - {isMobile ? null : gettingStartedDrawer} - {videoView} + {isMobile ? null : gettingStartedDrawer} + {videoView} - {modalOpen === true ? - - : null} - {/*
+ workflows={workflows} + apps={apps} + setWorkflows={setWorkflows} + /> + : null} + {/*
Need assistance? Ask our support team (it's free!).
*/} -
+
) : (