Synced 2.0 updates

This commit is contained in:
Frikky
2025-01-30 15:21:45 +01:00
parent e5ac2e3ae7
commit 4b8c860569
20 changed files with 15790 additions and 15012 deletions
+129 -4
View File
@@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react'; import React, { useCallback, useEffect, useState } from 'react';
import { useNavigate } from 'react-router'; import { useNavigate } from 'react-router';
import { import {
@@ -23,14 +23,14 @@ import ForkRightIcon from '@mui/icons-material/ForkRight';
import OpenInNewIcon from '@mui/icons-material/OpenInNew'; import OpenInNewIcon from '@mui/icons-material/OpenInNew';
import LaunchIcon from '@mui/icons-material/Launch'; import LaunchIcon from '@mui/icons-material/Launch';
import CheckCircleIcon from '@mui/icons-material/CheckCircle'; import CheckCircleIcon from '@mui/icons-material/CheckCircle';
import { CloudDownloadOutlined } from '@mui/icons-material'; import { CloudDownloadOutlined, Delete } from '@mui/icons-material';
import { findSpecificApp } from '../components/AppFramework.jsx'; import { findSpecificApp } from '../components/AppFramework.jsx';
import theme from "../theme.jsx"; import theme from "../theme.jsx";
import YAML from 'yaml'; import YAML from 'yaml';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
const AppModal = ({ open, onClose, app, globalUrl }) => { const AppModal = ({ open, onClose, app, globalUrl, getApps }) => {
const [frameworkData, setFrameworkData] = useState({}) const [frameworkData, setFrameworkData] = useState({})
const [userdata, setUserdata] = useState({}) const [userdata, setUserdata] = useState({})
@@ -41,6 +41,8 @@ const AppModal = ({ open, onClose, app, globalUrl }) => {
const [latestUsecase, setLatestUsecase] = useState([]) const [latestUsecase, setLatestUsecase] = useState([])
const [foundAppUsecase, setFoundAppUsecase] = useState({}) const [foundAppUsecase, setFoundAppUsecase] = useState({})
const [usecaseLoading, setUsecaseLoading] = useState(false) const [usecaseLoading, setUsecaseLoading] = useState(false)
const [deleteModalOpen, setDeleteModalOpen] = useState(false)
const [sharingConfiguration, setSharingConfiguration] = React.useState("you");
const navigate = useNavigate(); const navigate = useNavigate();
const parseUsecase = (subcase) => { const parseUsecase = (subcase) => {
const srcdata = findSpecificApp(frameworkData, subcase.type) const srcdata = findSpecificApp(frameworkData, subcase.type)
@@ -294,8 +296,101 @@ const AppModal = ({ open, onClose, app, globalUrl }) => {
setUsecaseLoading(true) setUsecaseLoading(true)
getAvailableWorkflows() getAvailableWorkflows()
getFramework() getFramework()
handleUpdateSharingConfiguration()
}, [app]) }, [app])
const handleUpdateSharingConfiguration = useCallback(() => {
if (app?.sharing === true) {
setSharingConfiguration("public")
}else {
setSharingConfiguration("you")
}
}, [app?.id])
const deleteApp = (appId) => {
toast("Attempting to delete app");
fetch(globalUrl + "/api/v1/apps/" + appId, {
method: "DELETE",
headers: {
Accept: "application/json",
},
credentials: "include",
})
.then((response) => {
if (response.status === 200) {
toast("Successfully deleted app");
setTimeout(() => {
//delete apps from local storage
localStorage.removeItem("apps");
getApps();
}, 1000);
} else {
toast("Failed deleting app. Does it still exist?");
}
})
.catch((error) => {
toast(error.toString());
});
};
const deleteModal = deleteModalOpen ? (
<Dialog
open={deleteModalOpen}
onClose={() => {
setDeleteModalOpen(false);
}}
PaperProps={{
sx: {
borderRadius: theme?.palette?.DialogStyle?.borderRadius,
border: theme?.palette?.DialogStyle?.border,
fontFamily: theme?.typography?.fontFamily,
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
zIndex: 1000,
minWidth: "500px",
overflow: "hidden",
'& .MuiDialogContent-root': {
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
},
'& .MuiDialogTitle-root': {
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
},
}
}}
>
<DialogTitle>
<div style={{ textAlign: "center", color: "rgba(255,255,255,0.9)" }}>
Are you sure? <div />
Some workflows may stop working.
</div>
</DialogTitle>
<DialogContent
style={{ color: "rgba(255,255,255,0.65)", textAlign: "center" }}
>
<Button
style={{}}
onClick={() => {
deleteApp(app.id);
setDeleteModalOpen(false);
}}
color="primary"
>
Yes
</Button>
<Button
variant="outlined"
style={{marginLeft: 5}}
onClick={() => {
setDeleteModalOpen(false);
}}
color="primary"
>
No
</Button>
</DialogContent>
</Dialog>
) : null;
const downloadApp = (inputdata) => { const downloadApp = (inputdata) => {
const id = inputdata.id; const id = inputdata.id;
@@ -425,6 +520,7 @@ const AppModal = ({ open, onClose, app, globalUrl }) => {
} }
}} }}
> >
{deleteModal}
<DialogTitle <DialogTitle
sx={{ sx={{
display: 'flex', display: 'flex',
@@ -457,7 +553,7 @@ const AppModal = ({ open, onClose, app, globalUrl }) => {
<div style={{ display: "flex", flexDirection: "row", gap: 10, fontFamily: theme?.typography?.fontFamily }}> <div style={{ display: "flex", flexDirection: "row", gap: 10, fontFamily: theme?.typography?.fontFamily }}>
<img <img
alt={app?.name} alt={app?.name}
src={app?.large_image || app?.image_url} src={app?.large_image || app?.image_url || "/images/no_image.png"}
style={{ style={{
borderRadius: 4, borderRadius: 4,
maxWidth: 100, maxWidth: 100,
@@ -536,6 +632,35 @@ const AppModal = ({ open, onClose, app, globalUrl }) => {
</Button> </Button>
</Tooltip> </Tooltip>
) : null} ) : null}
{(userdata?.id === app?.owner)? (
<Tooltip title={"Delete app (confirm box will show)"}>
<Button
variant="outlined"
component="label"
color="primary"
sx={{
bgcolor: '#494949',
'&:hover': { bgcolor: '#494949', border: 'none' },
textTransform: 'none',
borderRadius: 1,
minWidth: '45px',
width: '45px',
height: '40px',
padding: 2,
color: "#fff",
fontFamily: theme?.typography?.fontFamily,
border: 'none'
}}
onClick={() => {
setDeleteModalOpen(true);
}}
disabled={(sharingConfiguration === undefined || sharingConfiguration === null || sharingConfiguration === "public") }
>
<Delete />
</Button>
</Tooltip>
): null}
<Button <Button
variant="contained" variant="contained"
sx={{ sx={{
+3
View File
@@ -50,6 +50,9 @@ const Appsearch = props => {
return ( return (
<form noValidate action="" role="search"> <form noValidate action="" role="search">
<TextField <TextField
autoFocus
autoComplete="off"
autocomplete="off"
fullWidth fullWidth
style={{backgroundColor: "#2F2F2F", borderRadius: borderRadius, width: "100%",}} style={{backgroundColor: "#2F2F2F", borderRadius: borderRadius, width: "100%",}}
InputProps={{ InputProps={{
+14 -3
View File
@@ -390,6 +390,10 @@ const CacheView = memo((props) => {
variant="contained" variant="contained"
style={{ borderRadius: "2px", backgroundColor: "#ff8544",color: "#1a1a1a", textTransform:"none" }} style={{ borderRadius: "2px", backgroundColor: "#ff8544",color: "#1a1a1a", textTransform:"none" }}
onClick={() => { onClick={() => {
if (value === "") {
toast("Key or Value can not be empty");
return;
}
{editCache ? editOrgCache(orgId) : addOrgCache(orgId)} {editCache ? editOrgCache(orgId) : addOrgCache(orgId)}
setKey("") setKey("")
setValue("") setValue("")
@@ -503,7 +507,7 @@ const CacheView = memo((props) => {
> >
<DialogTitle> <DialogTitle>
<div style={{ color: "rgba(255,255,255,0.9)" }}> <div style={{ color: "rgba(255,255,255,0.9)" }}>
Select sub-org to distribute files Select sub-org to distribute Datastore key
</div> </div>
</DialogTitle> </DialogTitle>
<DialogContent style={{ color: "rgba(255,255,255,0.65)" }}> <DialogContent style={{ color: "rgba(255,255,255,0.65)" }}>
@@ -714,7 +718,9 @@ const CacheView = memo((props) => {
display: "table-cell", display: "table-cell",
overflow: "hidden", overflow: "hidden",
verticalAlign: "middle", verticalAlign: "middle",
padding: "8px 8px 8px 15px" padding: "8px 8px 8px 15px",
maxWidth: 200,
overflowX: "auto",
}} }}
primary={data.key} primary={data.key}
/> />
@@ -872,6 +878,8 @@ const CacheView = memo((props) => {
style={{display: "table-cell", textAlign: 'center', verticalAlign: 'middle', }} style={{display: "table-cell", textAlign: 'center', verticalAlign: 'middle', }}
/> />
: :
<ListItemText
primary={
<Tooltip <Tooltip
title="Distributed to sub-organizations. This means the sub organizations can use this datastore key, but can not modify it." title="Distributed to sub-organizations. This means the sub organizations can use this datastore key, but can not modify it."
placement="top" placement="top"
@@ -879,7 +887,7 @@ const CacheView = memo((props) => {
<Checkbox <Checkbox
disabled={ userdata?.active_org?.role !== "admin" || (selectedOrganization.creator_org !== undefined && selectedOrganization.creator_org !== null && selectedOrganization.creator_org !== "" )? true : false} disabled={ userdata?.active_org?.role !== "admin" || (selectedOrganization.creator_org !== undefined && selectedOrganization.creator_org !== null && selectedOrganization.creator_org !== "" )? true : false}
checked={isDistributed} checked={isDistributed}
style={{ }} style={{ margin: "auto" }}
color="secondary" color="secondary"
onClick={() => { onClick={() => {
setShowDistributionPopup(true) setShowDistributionPopup(true)
@@ -893,6 +901,9 @@ const CacheView = memo((props) => {
/> />
</Tooltip> </Tooltip>
} }
style={{display: "table-cell", textAlign: 'center', verticalAlign: 'middle', }}
/>
}
</ListItem> </ListItem>
); );
})} })}
+16 -4
View File
@@ -799,12 +799,16 @@ const ConfigureWorkflow = (props) => {
> >
<div <div
style={{ style={{
border: filled ? `1px solid ${theme.palette.green}` : "1px solid rgba(255,255,255,0.3)", borderRadius: theme.palette?.borderRadius, width: "100%", padding: 12, cursor: "pointer", border: filled ? `1px solid ${theme.palette.green}` : "1px solid rgba(255,255,255,0.3)", borderRadius: theme.palette?.borderRadius, width: "100%", padding: 12, cursor: filled ? "default" : "pointer",
}} }}
id="app-config" id="app-config"
> >
<div style={{display: "flex", }} <div style={{display: "flex", }}
onClick={() => { onClick={() => {
if (filled) {
return
}
setOpened(!opened); setOpened(!opened);
// Scroll to it // Scroll to it
@@ -865,6 +869,7 @@ const ConfigureWorkflow = (props) => {
isLoggedIn={true} isLoggedIn={true}
getAppAuthentication={undefined} getAppAuthentication={undefined}
workflow={workflow}
setFinalized={setFinalized} setFinalized={setFinalized}
/> />
</div> </div>
@@ -1377,7 +1382,7 @@ const ConfigureWorkflow = (props) => {
return ( return (
<div> <div>
<div style={{margin: setConfigureWorkflowModalOpen !== undefined ? "0px 50px 0px 50px" : "35px 0px 0px 0px", maxHeight: 475, }}> <div style={{margin: setConfigureWorkflowModalOpen !== undefined ? "0px 50px 0px 50px" : "25px 0px 0px 0px", maxHeight: 475, }}>
{setConfigureWorkflowModalOpen !== undefined ? {setConfigureWorkflowModalOpen !== undefined ?
@@ -1387,7 +1392,7 @@ const ConfigureWorkflow = (props) => {
: null : null
} }
<div style={{marginTop: 10, }} /> <div style={{marginTop: setConfigureWorkflowModalOpen !== undefined ? 10 : 0, }} />
{/* {/*
<WorkflowValidationTimeline <WorkflowValidationTimeline
@@ -1404,7 +1409,7 @@ const ConfigureWorkflow = (props) => {
{requiredActions.length > 0 ? ( {requiredActions.length > 0 ? (
<span> <span>
<Typography variant="body2" color="textSecondary"> <Typography variant="body2" color="textSecondary">
Please configure the following steps to help us complete your workflow. This can also be done later. To complete the workflow setup, please configure the following steps.
</Typography> </Typography>
{setConfigureWorkflowModalOpen !== undefined ? {setConfigureWorkflowModalOpen !== undefined ?
@@ -1434,6 +1439,13 @@ const ConfigureWorkflow = (props) => {
) )
})} })}
</List> </List>
{/*
<Typography variant="body2" color="textSecondary">
Once done, you may continue to the workflow.
</Typography>
*/}
</span> </span>
) : null} ) : null}
+90 -77
View File
@@ -71,7 +71,8 @@ const EditWorkflow = (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 [submitLoading, setSubmitLoading] = React.useState(false);
const [showMoreClicked, setShowMoreClicked] = React.useState(expanded === true ? true : false); //const [showMoreClicked, setShowMoreClicked] = React.useState(expanded === true ? true : false);
const [showMoreClicked, setShowMoreClicked] = React.useState(true);
const [innerWorkflow, setInnerWorkflow] = React.useState(workflow) const [innerWorkflow, setInnerWorkflow] = React.useState(workflow)
@@ -244,7 +245,6 @@ const EditWorkflow = (props) => {
Workflows can be built from scratch, or from templates. <a href="/usecases2" rel="noopener noreferrer" target="_blank" style={{ textDecoration: "none", color: "#f86a3e" }}>Usecases</a> can help you discover next steps, and you can <a href="/search?tab=workflows" rel="noopener noreferrer" target="_blank" style={{ textDecoration: "none", color: "#f86a3e" }}>search</a> for them directly. <a href="/docs/workflows" rel="noopener noreferrer" target="_blank" style={{ textDecoration: "none", color: "#f86a3e" }}>Learn more</a> Workflows can be built from scratch, or from templates. <a href="/usecases2" rel="noopener noreferrer" target="_blank" style={{ textDecoration: "none", color: "#f86a3e" }}>Usecases</a> can help you discover next steps, and you can <a href="/search?tab=workflows" rel="noopener noreferrer" target="_blank" style={{ textDecoration: "none", color: "#f86a3e" }}>search</a> for them directly. <a href="/docs/workflows" rel="noopener noreferrer" target="_blank" style={{ textDecoration: "none", color: "#f86a3e" }}>Learn more</a>
</Typography> </Typography>
{/*
<div style={{marginTop: 10, marginBottom: 10, marginRight: 50, }}> <div style={{marginTop: 10, marginBottom: 10, marginRight: 50, }}>
<WorkflowValidationTimeline <WorkflowValidationTimeline
@@ -252,7 +252,6 @@ const EditWorkflow = (props) => {
workflow={workflow} workflow={workflow}
/> />
</div> </div>
*/}
{showUpload === true ? {showUpload === true ?
<div style={{ float: "right" }}> <div style={{ float: "right" }}>
@@ -290,7 +289,7 @@ const EditWorkflow = (props) => {
bottom: 0, bottom: 0,
zIndex: 1002, zIndex: 1002,
backgroundColor: theme.palette.backgroundColor, backgroundColor: theme.palette.backgroundColor,
height: 50, height: 75,
paddingTop: 20, paddingTop: 20,
paddingLeft: 75, paddingLeft: 75,
}}> }}>
@@ -327,12 +326,17 @@ const EditWorkflow = (props) => {
innerWorkflow.name = name innerWorkflow.name = name
innerWorkflow.description = description innerWorkflow.description = description
if (newWorkflowTags.length > 0) { if (newWorkflowTags.length > 0) {
innerWorkflow.tags = newWorkflowTags innerWorkflow.tags = newWorkflowTags
} else {
innerWorkflow.tags = []
} }
if (selectedUsecases.length > 0) { if (selectedUsecases.length > 0) {
innerWorkflow.usecase_ids = selectedUsecases innerWorkflow.usecase_ids = selectedUsecases
} else {
innerWorkflow.usecase_ids = []
} }
if (dueDate > 0) { if (dueDate > 0) {
@@ -361,7 +365,6 @@ const EditWorkflow = (props) => {
setWorkflow({}) setWorkflow({})
} else { } else {
setWorkflow(innerWorkflow) setWorkflow(innerWorkflow)
console.log("editing workflow: ", innerWorkflow)
} }
setSubmitLoading(true) setSubmitLoading(true)
@@ -505,7 +508,7 @@ const EditWorkflow = (props) => {
color: "white", color: "white",
}, },
}} }}
multiLine multiline
rows={3} rows={3}
color="primary" color="primary"
defaultValue={innerWorkflow.description} defaultValue={innerWorkflow.description}
@@ -537,81 +540,10 @@ const EditWorkflow = (props) => {
</RadioGroup> </RadioGroup>
</FormControl> </FormControl>
<LocalizationProvider dateAdapter={AdapterDayjs}>
<DatePicker
sx={{
marginTop: 3,
marginLeft: 3,
}}
value={dueDate}
label="Due Date"
format="YYYY-MM-DD"
onChange={(newValue) => {
setDueDate(newValue)
}}
/>
</LocalizationProvider>
</div> </div>
<div /> <div />
<FormControl style={{ marginTop: 15, }}>
<FormLabel id="demo-row-radio-buttons-group-label">Type</FormLabel>
<RadioGroup
row
aria-labelledby="demo-row-radio-buttons-group-label"
name="row-radio-buttons-group"
defaultValue={innerWorkflow.workflow_type}
onChange={(e) => {
console.log("Data: ", e.target.value)
innerWorkflow.workflow_type = e.target.value
setInnerWorkflow(innerWorkflow)
}}
>
<FormControlLabel value="trigger" control={<Radio />} label="Trigger" />
<FormControlLabel value="subflow" control={<Radio />} label="Subflow" />
<FormControlLabel value="standalone" control={<Radio />} label="Standalone" />
</RadioGroup>
</FormControl>
<TextField
onBlur={(event) => {
innerWorkflow.blogpost = event.target.value
setInnerWorkflow(innerWorkflow)
}}
InputProps={{
style: {
color: "white",
},
}}
color="primary"
defaultValue={innerWorkflow.blogpost}
placeholder="A blogpost or other reference for how this work workflow was built, and what it's for."
rows="1"
label="blogpost"
margin="dense"
fullWidth
/>
<TextField
onBlur={(event) => {
innerWorkflow.video = event.target.value
setInnerWorkflow(innerWorkflow)
}}
InputProps={{
style: {
color: "white",
},
}}
color="primary"
defaultValue={innerWorkflow.video}
placeholder="A youtube or loom link to the video"
rows="1"
label="Video"
margin="dense"
fullWidth
/>
<TextField <TextField
onBlur={(event) => { onBlur={(event) => {
@@ -1166,6 +1098,87 @@ const EditWorkflow = (props) => {
</div> </div>
</> : null*/} </> : null*/}
<Typography variant="h4" style={{ marginTop: 100, }}>
Publishing
</Typography>
<Typography variant="body1" color="textSecondary" style={{ marginTop: 10, }}>
Publishing is related to making the workflow itself public. When publishing a workflow, all the details (except sensitive info) become available to everyone. The details below will help a user understand this better. When a workflow is published, you keep the original, and a copy enters the workflow search, and is associated with your <a href="/creators" style={{ textDecoration: "none", color: "#f86a3e" }} target="_blank">creator account</a>, if you have one. You can always unpublish the workflow after. To publish it, click the three dots next to the workflow.
</Typography>
<LocalizationProvider style={{marginLeft: 0, }} dateAdapter={AdapterDayjs}>
<DatePicker
sx={{
marginTop: 3,
marginLeft: 3,
}}
value={dueDate}
label="Due Date"
format="YYYY-MM-DD"
onChange={(newValue) => {
setDueDate(newValue)
}}
/>
</LocalizationProvider>
<FormControl style={{ marginTop: 15, }}>
<FormLabel id="demo-row-radio-buttons-group-label">Type</FormLabel>
<RadioGroup
row
aria-labelledby="demo-row-radio-buttons-group-label"
name="row-radio-buttons-group"
defaultValue={innerWorkflow.workflow_type}
onChange={(e) => {
console.log("Data: ", e.target.value)
innerWorkflow.workflow_type = e.target.value
setInnerWorkflow(innerWorkflow)
}}
>
<FormControlLabel value="trigger" control={<Radio />} label="Trigger" />
<FormControlLabel value="subflow" control={<Radio />} label="Subflow" />
<FormControlLabel value="standalone" control={<Radio />} label="Standalone" />
</RadioGroup>
</FormControl>
<TextField
onBlur={(event) => {
innerWorkflow.blogpost = event.target.value
setInnerWorkflow(innerWorkflow)
}}
InputProps={{
style: {
color: "white",
},
}}
color="primary"
defaultValue={innerWorkflow.blogpost}
placeholder="A blogpost or other reference for how this work workflow was built, and what it's for."
rows="1"
label="blogpost"
margin="dense"
fullWidth
/>
<TextField
onBlur={(event) => {
innerWorkflow.video = event.target.value
setInnerWorkflow(innerWorkflow)
}}
InputProps={{
style: {
color: "white",
},
}}
color="primary"
defaultValue={innerWorkflow.video}
placeholder="A youtube or loom link to the video"
rows="1"
label="Video"
margin="dense"
fullWidth
/>
<Tooltip color="primary" title={"Add more details"} placement="top"> <Tooltip color="primary" title={"Add more details"} placement="top">
<Button <Button
style={{ margin: "auto", marginTop: 50, marginBottom: 10, textAlign: "center", textTransform: "none", }} style={{ margin: "auto", marginTop: 50, marginBottom: 10, textAlign: "center", textTransform: "none", }}
@@ -644,6 +644,7 @@ const FixWorkflowValidationErrors = (props) => {
console.log("Workflow validation: ", workflow.validation) console.log("Workflow validation: ", workflow.validation)
return ( return (
<div> <div>
{/*
{workflow.errors !== undefined && workflow.errors !== null ? {workflow.errors !== undefined && workflow.errors !== null ?
<div> <div>
General errors: {workflow.errors.length} General errors: {workflow.errors.length}
@@ -656,11 +657,8 @@ const FixWorkflowValidationErrors = (props) => {
})} })}
</div> </div>
: null} : null}
<Divider style={{marginTop: 15, marginBottom: 15, }}/> <Divider style={{marginTop: 15, marginBottom: 15, }}/>
workflow.validation.errors !== undefined && workflow.validation.errors !== null ?
{workflow.validation.errors !== undefined && workflow.validation.errors !== null ?
<div> <div>
Validation errors: {workflow.validation.errors.length} Validation errors: {workflow.validation.errors.length}
{workflow.validation.errors.map((error, index) => { {workflow.validation.errors.map((error, index) => {
@@ -675,10 +673,12 @@ const FixWorkflowValidationErrors = (props) => {
) )
})} })}
</div> </div>
: null} : null*/}
{/*
<Divider style={{marginTop: 15, marginBottom: 15, }} /> <Divider style={{marginTop: 15, marginBottom: 15, }} />
Apps loaded: {apps.length} Apps loaded: {apps.length}
*/}
</div> </div>
) )
+155 -58
View File
@@ -7,7 +7,9 @@ import {
Add as AddIcon, Add as AddIcon,
BorderColor, BorderColor,
Close as CloseIcon, Close as CloseIcon,
ConstructionOutlined, ConstructionOutlined as ConstructionOutlinedIcon,
Toc as TocIcon,
Settings as SettingsIcon
} from "@mui/icons-material"; } from "@mui/icons-material";
import SearchBox from "./SearchData.jsx"; import SearchBox from "./SearchData.jsx";
import { import {
@@ -29,9 +31,6 @@ import {
Collapse, Collapse,
} from "@mui/material"; } from "@mui/material";
import theme from "../theme.jsx"; import theme from "../theme.jsx";
import {
Settings as SettingsIcon
} from "@mui/icons-material";
import RecentWorkflow from "../components/RecentWorkflow.jsx"; import RecentWorkflow from "../components/RecentWorkflow.jsx";
import { useNavigate } from "react-router"; import { useNavigate } from "react-router";
@@ -143,8 +142,6 @@ useEffect(() => {
> >
<Box <Box
sx={{ sx={{
maxHeight: 250,
overflowY: "auto",
scrollbarWidth: "thin", scrollbarWidth: "thin",
scrollbarColor: "#494949 transparent", scrollbarColor: "#494949 transparent",
"& .MuiAutocomplete-listbox": { "& .MuiAutocomplete-listbox": {
@@ -238,7 +235,7 @@ useEffect(() => {
setOpenautomateTab(true); setOpenautomateTab(true);
setOpenSecurityTab(false); setOpenSecurityTab(false);
setCurrentOpenTab("workflows"); setCurrentOpenTab("workflows");
} else if ((lastTabOpenByUser === "apps" && currentPath.includes("/search")) || currentPath.includes("/search")) { } else if ((lastTabOpenByUser === "apps" && currentPath.includes("/apps")) || currentPath.includes("/apps")) {
setOpenautomateTab(true); setOpenautomateTab(true);
setOpenSecurityTab(false); setOpenSecurityTab(false);
setCurrentOpenTab("apps"); setCurrentOpenTab("apps");
@@ -500,16 +497,6 @@ useEffect(() => {
}) })
</MenuItem> </MenuItem>
</Link> </Link>
<Link to="/workflows" style={hrefStyle}>
<MenuItem
onClick={(event) => {
handleClose();
}}
style={{fontSize: 18}}
>
<LightbulbIcon style={{ marginRight: 5 }} /> Use Cases
</MenuItem>
</Link>
<Divider style={{ marginTop: 10, marginBottom: 10, }} /> <Divider style={{ marginTop: 10, marginBottom: 10, }} />
@@ -537,7 +524,7 @@ useEffect(() => {
<Divider style={{ marginBottom: 10, }} /> <Divider style={{ marginBottom: 10, }} />
<Typography color="textSecondary" align="center" style={{ marginTop: 5, marginBottom: 5, fontSize: 18 }}> <Typography color="textSecondary" align="center" style={{ marginTop: 5, marginBottom: 5, fontSize: 18 }}>
Version: 2.0.0-rc3 Version: 2.0.0-rc4
</Typography> </Typography>
</Menu> </Menu>
</span> </span>
@@ -704,16 +691,48 @@ useEffect(() => {
const CheckOrgStates = useCallback(() => { const CheckOrgStates = useCallback(() => {
setOrgOptions( setOrgOptions(
userdata?.orgs?.map((org) => ({ userdata?.orgs?.map((org) => {
let skipOrg = false;
if (
org.creator_org !== undefined &&
org.creator_org !== null &&
org.creator_org.length > 0
) {
// Finds the parent org
for (let key in userdata.child_orgs) {
if (userdata.child_orgs[key].id === org.creator_org) {
skipOrg = true;
break;
}
}
if (skipOrg) {
return null; // Skip this org
}
}
return {
id: org.id, id: org.id,
name: org.name, name: org.name,
image: org.image, image: org.image,
region_url: getRegionTag(org.region_url), region_url: getRegionTag(org.region_url),
})) || [] margin_left:
org.creator_org !== undefined &&
org.creator_org !== null &&
org.creator_org.length > 0
? org.id === userdata.active_org.id
? 0
: 20
: 0,
};
}) || []
); );
setActiveOrgName(userdata?.active_org?.name || "Select Organization"); setActiveOrgName(userdata?.active_org?.name || "Select Organization");
setSelectedOrg(userdata?.active_org?.name || "Select Organization"); setSelectedOrg(userdata?.active_org?.name || "Select Organization");
},[orgOptions, activeOrgName, selectedOrg]); }, [userdata]);
useEffect(() => { useEffect(() => {
if (typeof userdata?.id === "string" && userdata?.id?.length > 0) { if (typeof userdata?.id === "string" && userdata?.id?.length > 0) {
@@ -870,7 +889,7 @@ useEffect(() => {
</Button> </Button>
</Box> </Box>
</Box> </Box>
<Box sx={{ display: "flex", flexDirection: "column", width:"100%", height: "100%", overflowY: "auto", overflowX: "hidden",transition: 'display 0.3s ease',paddingTop: 0.5 }} onMouseOver={()=>{!leftSideBarOpenByClick && setExpandLeftNav(true);}} onMouseLeave={()=>{!leftSideBarOpenByClick && setExpandLeftNav(false);setOpenAutocomplete(false);}}> <Box sx={{ display: "flex", flexDirection: "column", width:"100%", height: "100%", overflowY: "auto", overflowX: "hidden",transition: 'display 0.3s ease',paddingTop: 0.5 }} onMouseOver={()=>{!leftSideBarOpenByClick && setExpandLeftNav(true)}} onMouseLeave={()=>{!leftSideBarOpenByClick && setExpandLeftNav(false);setOpenAutocomplete(false)}}>
<Box <Box
sx={{ sx={{
display: "flex", display: "flex",
@@ -1144,7 +1163,7 @@ useEffect(() => {
color: currentOpenTab === "apps" && currentPath.includes("/apps") ? "#FFFFFF" : "#C8C8C8", color: currentOpenTab === "apps" && currentPath.includes("/apps") ? "#FFFFFF" : "#C8C8C8",
justifyContent: "flex-start", justifyContent: "flex-start",
textTransform: "none", textTransform: "none",
backgroundColor: currentOpenTab === "apps" && expandLeftNav && currentPath.includes("/apps2") ? "#2f2f2f": "transparent", backgroundColor: currentOpenTab === "apps" && expandLeftNav && currentPath.includes("/apps") ? "#2f2f2f": "transparent",
marginLeft: 16, marginLeft: 16,
fontSize: 18 fontSize: 18
}} }}
@@ -1152,7 +1171,7 @@ useEffect(() => {
event.currentTarget.style.backgroundColor = "#2f2f2f"; event.currentTarget.style.backgroundColor = "#2f2f2f";
}} }}
onMouseOut={(event)=>{ onMouseOut={(event)=>{
event.currentTarget.style.backgroundColor = currentOpenTab === "apps" && expandLeftNav && currentPath.includes("/search") ? "#2f2f2f": "transparent"; event.currentTarget.style.backgroundColor = currentOpenTab === "apps" && expandLeftNav && currentPath.includes("/apps") ? "#2f2f2f": "transparent";
}} }}
disableRipple={expandLeftNav ? false : true} disableRipple={expandLeftNav ? false : true}
> >
@@ -1205,7 +1224,7 @@ useEffect(() => {
: "transparent"; : "transparent";
}} }}
> >
<ShieldOutlinedIcon <TocIcon
style={{ style={{
width: 18, width: 18,
height: 18, height: 18,
@@ -1223,7 +1242,7 @@ useEffect(() => {
: "#C8C8C8" : "#C8C8C8"
}} }}
> >
Discover Content
</span> </span>
</Button> </Button>
</Link> </Link>
@@ -1262,7 +1281,7 @@ useEffect(() => {
<Collapse in={openSecurityTab} timeout="auto" unmountOnExit> <Collapse in={openSecurityTab} timeout="auto" unmountOnExit>
<Box <Box
style={{ style={{
maxHeight: openSecurityTab && expandLeftNav ? 100 : 0, maxHeight: openSecurityTab && expandLeftNav ? 135 : 0,
overflow: "hidden", overflow: "hidden",
transition: "max-height 0.3s ease, opacity 0.3s ease", transition: "max-height 0.3s ease, opacity 0.3s ease",
display: "flex", display: "flex",
@@ -1276,19 +1295,18 @@ useEffect(() => {
to={"/forms"} to={"/forms"}
style={{ style={{
...hrefStyle, ...hrefStyle,
pointerEvents: userdata?.support ? "auto" : "none", pointerEvents: "auto",
}} }}
> >
<Button <Button
onClick={(event) => { onClick={(event) => {
if (!userdata?.support) return;
setCurrentOpenTab("detection"); setCurrentOpenTab("detection");
localStorage.setItem("lastTabOpenByUser", "detection"); localStorage.setItem("lastTabOpenByUser", "detection");
}} }}
sx={{ sx={{
width: "100%", width: "100%",
height: 35, height: 35,
color: userdata?.support ? "#C8C8C8" : "#6F6F6F", color: "#C8C8C8",
justifyContent: "flex-start", justifyContent: "flex-start",
textTransform: "none", textTransform: "none",
backgroundColor: backgroundColor:
@@ -1296,11 +1314,10 @@ useEffect(() => {
? "#2f2f2f" ? "#2f2f2f"
: "transparent", : "transparent",
"&:hover": { "&:hover": {
backgroundColor: userdata?.support ? "#2f2f2f" : "transparent", backgroundColor: "#2f2f2f",
}, },
cursor: userdata?.support ? "pointer" : "not-allowed", cursor: "pointer",
}} }}
disabled={userdata?.support === false}
> >
<span style={{ position: "relative", left: !expandLeftNav ? 10 : 0, marginRight: 10, fontSize: 18 }}> <span style={{ position: "relative", left: !expandLeftNav ? 10 : 0, marginRight: 10, fontSize: 18 }}>
@@ -1312,11 +1329,9 @@ useEffect(() => {
transition: "opacity 0.3s ease", transition: "opacity 0.3s ease",
fontSize: 18, fontSize: 18,
color: color:
userdata?.support && currentOpenTab === "detection" && currentPath.includes("/detection") currentOpenTab === "detection" && currentPath.includes("/detection")
? "#F1F1F1" ? "#F1F1F1"
: userdata?.support : "#C8C8C8"
? "#C8C8C8"
: "#6F6F6F",
}} }}
> >
Forms Forms
@@ -1329,19 +1344,18 @@ useEffect(() => {
to={"/admin?tab=datastore"} to={"/admin?tab=datastore"}
style={{ style={{
...hrefStyle, ...hrefStyle,
pointerEvents: userdata?.support ? "auto" : "none", pointerEvents: "auto",
}} }}
> >
<Button <Button
onClick={(event) => { onClick={(event) => {
if (!userdata?.support) return;
setCurrentOpenTab("response"); setCurrentOpenTab("response");
localStorage.setItem("lastTabOpenByUser", "response"); localStorage.setItem("lastTabOpenByUser", "response");
}} }}
sx={{ sx={{
width: "100%", width: "100%",
height: 35, height: 35,
color: userdata?.support ? "#C8C8C8" : "#6F6F6F", color: "#C8C8C8",
justifyContent: "flex-start", justifyContent: "flex-start",
textTransform: "none", textTransform: "none",
backgroundColor: backgroundColor:
@@ -1349,9 +1363,9 @@ useEffect(() => {
? "#2f2f2f" ? "#2f2f2f"
: "transparent", : "transparent",
"&:hover": { "&:hover": {
backgroundColor: userdata?.support ? "#2f2f2f" : "transparent", backgroundColor: "#2f2f2f",
}, },
cursor: userdata?.support ? "pointer" : "not-allowed", cursor: "pointer",
}} }}
> >
<span style={{ position: "relative", left: !expandLeftNav ? 10 : 0, marginRight: 10, fontSize: 18 }}> <span style={{ position: "relative", left: !expandLeftNav ? 10 : 0, marginRight: 10, fontSize: 18 }}>
@@ -1364,11 +1378,9 @@ useEffect(() => {
transition: "opacity 0.3s ease", transition: "opacity 0.3s ease",
fontSize: 18, fontSize: 18,
color: color:
userdata?.support && currentOpenTab === "response" && currentPath.includes("/response") currentOpenTab === "response" && currentPath.includes("/response")
? "#F1F1F1" ? "#F1F1F1"
: userdata?.support : "#C8C8C8"
? "#C8C8C8"
: "#6F6F6F",
}} }}
> >
Datastore Datastore
@@ -1376,24 +1388,24 @@ useEffect(() => {
</Button> </Button>
</Link> </Link>
</span> </span>
<span style={{ display: "inline-block", width: "100%" }}> <span style={{ display: "inline-block", width: "100%" }}>
<Link <Link
to={"/admin?tab=files"} to={"/admin?tab=files"}
style={{ style={{
...hrefStyle, ...hrefStyle,
pointerEvents: userdata?.support ? "auto" : "none", pointerEvents: "auto",
}} }}
> >
<Button <Button
onClick={(event) => { onClick={(event) => {
if (!userdata?.support) return;
setCurrentOpenTab("response"); setCurrentOpenTab("response");
localStorage.setItem("lastTabOpenByUser", "response"); localStorage.setItem("lastTabOpenByUser", "response");
}} }}
sx={{ sx={{
width: "100%", width: "100%",
height: 35, height: 35,
color: userdata?.support ? "#C8C8C8" : "#6F6F6F", color: "#C8C8C8",
justifyContent: "flex-start", justifyContent: "flex-start",
textTransform: "none", textTransform: "none",
backgroundColor: backgroundColor:
@@ -1401,9 +1413,9 @@ useEffect(() => {
? "#2f2f2f" ? "#2f2f2f"
: "transparent", : "transparent",
"&:hover": { "&:hover": {
backgroundColor: userdata?.support ? "#2f2f2f" : "transparent", backgroundColor: "#2f2f2f",
}, },
cursor: userdata?.support ? "pointer" : "not-allowed", cursor: "pointer",
}} }}
> >
<span style={{ position: "relative", left: !expandLeftNav ? 10 : 0, marginRight: 10, fontSize: 18 }}> <span style={{ position: "relative", left: !expandLeftNav ? 10 : 0, marginRight: 10, fontSize: 18 }}>
@@ -1416,11 +1428,9 @@ useEffect(() => {
transition: "opacity 0.3s ease", transition: "opacity 0.3s ease",
fontSize: 18, fontSize: 18,
color: color:
userdata?.support && currentOpenTab === "response" && currentPath.includes("/response") currentOpenTab === "response" && currentPath.includes("/response")
? "#F1F1F1" ? "#F1F1F1"
: userdata?.support : "#C8C8C8"
? "#C8C8C8"
: "#6F6F6F",
}} }}
> >
Files Files
@@ -1428,8 +1438,59 @@ useEffect(() => {
</Button> </Button>
</Link> </Link>
</span> </span>
<span style={{ display: "inline-block", width: "100%" }}>
<Link
to={"/admin?tab=locations"}
style={{
...hrefStyle,
pointerEvents: "auto",
}}
>
<Button
onClick={(event) => {
setCurrentOpenTab("response");
localStorage.setItem("lastTabOpenByUser", "response");
}}
sx={{
width: "100%",
height: 35,
color: "#C8C8C8",
justifyContent: "flex-start",
textTransform: "none",
backgroundColor:
currentOpenTab === "response" && currentPath.includes("/response")
? "#2f2f2f"
: "transparent",
"&:hover": {
backgroundColor: "#2f2f2f",
},
cursor: "pointer",
}}
>
<span style={{ position: "relative", left: !expandLeftNav ? 10 : 0, marginRight: 10, fontSize: 18 }}>
</span>
<span
style={{
display: expandLeftNav ? "inline" : "none",
opacity: expandLeftNav ? 1 : 0,
transition: "opacity 0.3s ease",
fontSize: 18,
color:
currentOpenTab === "response" && currentPath.includes("/response")
? "#F1F1F1"
: "#C8C8C8"
}}
>
Shuffle Agent
</span>
</Button>
</Link>
</span>
</Box> </Box>
</Collapse> </Collapse>
<Link to="/docs" style={hrefStyle}> <Link to="/docs" style={hrefStyle}>
<Button <Button
onClick={(event) => { onClick={(event) => {
@@ -1464,6 +1525,38 @@ useEffect(() => {
</span> </span>
</Button> </Button>
</Link> </Link>
<Link to="/admin?admin_tab=billingstats" style={hrefStyle}>
<Button
onClick={(event) => {
setCurrentOpenTab("admin");
localStorage.setItem("lastTabOpenByUser", "admin");
}}
style={{
...ButtonStyle,
marginTop: 8,
marginTop: 8,
backgroundColor: currentOpenTab === "docs" && currentPath.includes("/admin") ? "#2f2f2f": "transparent",
}}
onMouseOver={(event)=>{
event.currentTarget.style.backgroundColor = "#2f2f2f";
}}
onMouseOut={(event)=>{
event.currentTarget.style.backgroundColor = currentOpenTab === "docs" && currentPath.includes("/admin") ? "#2f2f2f": "transparent";
}}
>
<BusinessIcon style={{ width: 16, height: 16, marginRight: expandLeftNav ? 10 : 0, color: "rgba(255,255,255,0.5)", }} />
<span
style={{
display: expandLeftNav ? "inline" : "none",
color: currentOpenTab === "admin" && currentPath.includes("/admin") ? "#F1F1F1" : "#C8C8C8",
}}
>
Admin
</span>
</Button>
</Link>
</Box> </Box>
{recentworkflows?.length > 0 ? {recentworkflows?.length > 0 ?
@@ -1549,6 +1642,7 @@ useEffect(() => {
padding: option.id === "add_suborg" ? "0" : "12px 16px", padding: option.id === "add_suborg" ? "0" : "12px 16px",
marginTop: index !== 0 ? 8 : 0, marginTop: index !== 0 ? 8 : 0,
borderRadius: 6, borderRadius: 6,
marginLeft: option.margin_left ? option.margin_left : 0,
}} }}
onMouseOver={(e) => { onMouseOver={(e) => {
e.currentTarget.style.backgroundColor = "#444444"; e.currentTarget.style.backgroundColor = "#444444";
@@ -1618,12 +1712,15 @@ useEffect(() => {
setAutocompleteValue(newInputValue); setAutocompleteValue(newInputValue);
}} }}
filterOptions={(options, params) => { filterOptions={(options, params) => {
const normalize = (str) => str.toLowerCase().replace(/[\s-]+/g, "");
const input = normalize(params.inputValue);
return options.filter((option) => return options.filter((option) =>
option.name normalize(option.name).includes(input) ||
.toLowerCase() normalize(option.region_url).includes(input)
.includes(params.inputValue.toLowerCase())
); );
}} }}
value={userOrgs} value={userOrgs}
renderInput={(params) => ( renderInput={(params) => (
<Box <Box
+1 -1
View File
@@ -431,7 +431,7 @@ const AuthenticationOauth2 = (props) => {
const authentication_url = authenticationType.token_uri; const authentication_url = authenticationType.token_uri;
const redirectUri = `${window.location.protocol}//${window.location.host}/set_authentication`; const redirectUri = `${window.location.protocol}//${window.location.host}/set_authentication`;
const workflowId = workflow !== undefined ? workflow.id : ""; const workflowId = workflow !== undefined ? workflow.id : ""
var state = `workflow_id%3D${workflowId}%26reference_action_id%3d${selectedAction.app_id}%26app_name%3d${selectedAction.app_name}%26app_id%3d${selectedAction.app_id}%26app_version%3d${selectedAction.app_version}%26authentication_url%3d${authentication_url}%26scope%3d${resources}%26client_id%3d${client_id}%26client_secret%3d${client_secret}`; var state = `workflow_id%3D${workflowId}%26reference_action_id%3d${selectedAction.app_id}%26app_name%3d${selectedAction.app_name}%26app_id%3d${selectedAction.app_id}%26app_version%3d${selectedAction.app_version}%26authentication_url%3d${authentication_url}%26scope%3d${resources}%26client_id%3d${client_id}%26client_secret%3d${client_secret}`;
+98 -5
View File
@@ -200,6 +200,7 @@ const ParsedAction = (props) => {
const [menuPosition, setMenuPosition] = useState(null); const [menuPosition, setMenuPosition] = useState(null);
const [uiBox, setUiBox] = useState(null); const [uiBox, setUiBox] = useState(null);
const isIntegration = selectedAction.app_id === "integration" const isIntegration = selectedAction.app_id === "integration"
const [distributeAuthToSuborgs, setDistributeAuthToSuborgs] = useState(selectedAction?.selectedAuthentication?.suborg_distributed || false)
useEffect(() => { useEffect(() => {
if (setLastSaved !== undefined) { if (setLastSaved !== undefined) {
@@ -454,7 +455,50 @@ const ParsedAction = (props) => {
}); });
}; };
const changeDistribution = (data) => {
editAuthenticationConfig(data.id, "suborg_distribute")
}
const editAuthenticationConfig = (id, parentAction) => {
const data = {
id: id,
action: parentAction !== undefined && parentAction !== null ? parentAction : "assign_everywhere",
}
const url = globalUrl + "/api/v1/apps/authentication/" + id + "/config";
fetch(url, {
mode: "cors",
method: "POST",
body: JSON.stringify(data),
credentials: "include",
crossDomain: true,
withCredentials: true,
headers: {
"Content-Type": "application/json; charset=utf-8",
},
})
.then((response) =>
response.json().then((responseJson) => {
if (responseJson["success"] === false) {
toast("Failed overwriting appauth");
} else {
if (distributeAuthToSuborgs) {
toast.success("Successfully updated auth");
} else {
toast.success("Successfully distributed auth to suborgs");
}
setTimeout(() => {
getAppAuthentication();
setDistributeAuthToSuborgs(!distributeAuthToSuborgs)
}, 1000);
}
})
)
.catch((error) => {
toast("Err: " + error.toString());
});
};
const defineStartnode = () => { const defineStartnode = () => {
if (cy === undefined) { if (cy === undefined) {
@@ -725,7 +769,7 @@ const ParsedAction = (props) => {
} }
} }
if (param?.configuration) { if (param?.configuration && param?.name !== "url") {
let regex = /(^|[^\\])\$/; let regex = /(^|[^\\])\$/;
if (regex.test(paramvalue)) { if (regex.test(paramvalue)) {
if (message.length > 0) { if (message.length > 0) {
@@ -1498,7 +1542,12 @@ const ParsedAction = (props) => {
if (newAppname === undefined || newAppname === null) { if (newAppname === undefined || newAppname === null) {
newAppname = "" newAppname = ""
} else { } else {
newAppname = newAppname.replaceAll("_", " ") try {
newAppname = newAppname?.replaceAll("_", " ")
} catch (e) {
console.log("Error in replace newappname: ", e)
}
} }
var optionalFound = false var optionalFound = false
@@ -1987,8 +2036,31 @@ const ParsedAction = (props) => {
{selectedAction.authentication !== undefined && {selectedAction.authentication !== undefined &&
selectedAction.authentication !== null && selectedAction.authentication !== null &&
selectedAction.authentication.length > 0 ? ( selectedAction.authentication.length > 0 ? (
<div style={{ marginTop: 15 }}>
<div style={{ marginTop: 15, position: "relative", }}>
<Typography style={{ color: "rgba(255,255,255,0.7)" }}>Authentication</Typography> <Typography style={{ color: "rgba(255,255,255,0.7)" }}>Authentication</Typography>
<Tooltip
title={
workflow?.suborg_distribution?.length > 0 && Object.getOwnPropertyNames(selectedAction?.selectedAuthentication).length !== 0 ? (
<React.Fragment>
<div style={{padding: 10, backgroundColor: theme.palette.inputColor, borderRadius: theme.palette.borderRadius, border: "1px solid rgba(255,255,255,0)"}}>
<FormControlLabel
control={
<Checkbox
checked={distributeAuthToSuborgs}
onChange={(event) => {
changeDistribution(selectedAction?.selectedAuthentication)
}}
name="distributeAuth"
color="primary"
/>
}
label="Distribute auth to suborgs"
/>
</div>
</React.Fragment>
) : null
} placement="left">
<div style={{ display: "flex" }}> <div style={{ display: "flex" }}>
<Select <Select
MenuProps={{ MenuProps={{
@@ -2008,6 +2080,7 @@ const ParsedAction = (props) => {
}} }}
fullWidth fullWidth
onChange={(e) => { onChange={(e) => {
if (e.target.value === "No selection") { if (e.target.value === "No selection") {
selectedAction.selectedAuthentication = {}; selectedAction.selectedAuthentication = {};
selectedAction.authentication_id = ""; selectedAction.authentication_id = "";
@@ -2033,6 +2106,7 @@ const ParsedAction = (props) => {
selectedAction.parameters[key].value = "" selectedAction.parameters[key].value = ""
} }
} }
setSelectedAction(selectedAction) setSelectedAction(selectedAction)
setUpdate(Math.random()) setUpdate(Math.random())
@@ -2064,6 +2138,9 @@ const ParsedAction = (props) => {
} else { } else {
selectedAction.selectedAuthentication = e.target.value; selectedAction.selectedAuthentication = e.target.value;
selectedAction.authentication_id = e.target.value.id; selectedAction.authentication_id = e.target.value.id;
setDistributeAuthToSuborgs(e.target.value?.suborg_distributed || false)
setSelectedAction(selectedAction) setSelectedAction(selectedAction)
setUpdate(Math.random()) setUpdate(Math.random())
} }
@@ -2165,6 +2242,7 @@ const ParsedAction = (props) => {
</IconButton> </IconButton>
</Tooltip> </Tooltip>
</div> </div>
</Tooltip>
</div> </div>
) : null} ) : null}
@@ -4222,17 +4300,32 @@ const ParsedAction = (props) => {
data.variant = "STATIC_VALUE" data.variant = "STATIC_VALUE"
} }
const isFirstOptional = optionalFound === false && data.configuration === false && data.required === false ? true : false var isFirstOptional = optionalFound === false && data.configuration === false && data.required === false ? true : false
if (optionalFound === false && data.configuration === false && data.required === false) { if (optionalFound === false && data.configuration === false && data.required === false) {
optionalFound = true optionalFound = true
} }
if (isFirstOptional) {
// Check if any required fields are found
var foundRequired = false
for (var key in selectedActionParameters) {
if (selectedActionParameters[key]?.required === true) {
foundRequired = true
break
}
}
if (!foundRequired) {
isFirstOptional = false
}
}
return ( return (
<div key={data.name} style={{ marginTop: isFirstOptional ? 55 : 5, }}> <div key={data.name} style={{ marginTop: isFirstOptional ? 55 : 5, }}>
{isFirstOptional ? <Divider style={{ backgroundColor: "rgba(255,255,255,0.1)", marginBottom: 20, }} /> : null} {isFirstOptional ? <Divider style={{ backgroundColor: "rgba(255,255,255,0.1)", marginBottom: 20, }} /> : null}
{showButtonField === true ? hideBodyButtonValue : null} {showButtonField === true ? hideBodyButtonValue : null}
<div <div
style={{ marginTop: 20, marginBottom: 0, display: "flex" }} style={{ marginTop: 18, marginBottom: 0, display: "flex" }}
> >
{data.configuration === true ? ( {data.configuration === true ? (
<Tooltip <Tooltip
+320 -4
View File
@@ -13,8 +13,11 @@ import {
Card, Card,
Chip, Chip,
Switch, Switch,
Skeleton, Autocomplete,
TextField,
MenuItem,
} from "@mui/material"; } from "@mui/material";
import { makeStyles } from "@mui/styles";
import { Context } from "../context/ContextApi.jsx"; import { Context } from "../context/ContextApi.jsx";
import { useNavigate, Link } from "react-router-dom"; import { useNavigate, Link } from "react-router-dom";
@@ -22,8 +25,15 @@ import Priority from "../components/Priority.jsx";
import { constrainMatrix } from "reaviz"; import { constrainMatrix } from "reaviz";
//import { useAlert //import { useAlert
const useStyles = makeStyles({
notchedOutline: {
borderColor: "#f85a3e !important",
},
});
const Priorities = memo((props) => { const Priorities = memo((props) => {
const { globalUrl, userdata,clickedFromOrgTab, serverside, billingInfo, stripeKey, checkLogin, setAdminTab, setCurTab, notifications, setNotifications, } = props; const { globalUrl, userdata,clickedFromOrgTab,selectedOrganization, handleEditOrg, serverside, billingInfo, stripeKey, checkLogin, setAdminTab, setCurTab, notifications, setNotifications, } = props;
const [showDismissed, setShowDismissed] = React.useState(false); const [showDismissed, setShowDismissed] = React.useState(false);
const [showRead, setShowRead] = React.useState(false); const [showRead, setShowRead] = React.useState(false);
@@ -32,7 +42,21 @@ const Priorities = memo((props) => {
const [selectedExecutionId, setSelectedExecutionId] = React.useState("NO HIGHLIGHT"); const [selectedExecutionId, setSelectedExecutionId] = React.useState("NO HIGHLIGHT");
const [highlightKMS, setHighlightKMS] = React.useState(false) const [highlightKMS, setHighlightKMS] = React.useState(false)
const [workflows, setWorkflows] = React.useState([])
const [openNotification, setOpenNotification] = React.useState(false);
const [workflow, setWorkflow] = React.useState({})
const [notificationWorkflow, setNotificationWorkflow] = React.useState(
selectedOrganization.defaults === undefined
? ""
: selectedOrganization.defaults.notification_workflow === undefined ||
selectedOrganization.defaults.notification_workflow.length === 0
? ""
: selectedOrganization.defaults.notification_workflow
);
let navigate = useNavigate(); let navigate = useNavigate();
const classes = useStyles();
useEffect(() => { useEffect(() => {
getFramework() getFramework()
@@ -60,6 +84,20 @@ const Priorities = memo((props) => {
} }
}, []) }, [])
useEffect(() => {
if (selectedOrganization === undefined || selectedOrganization === null || selectedOrganization?.id === undefined || selectedOrganization?.id === null || selectedOrganization?.id.length === 0) {
return
}
if(workflows?.length === 0) {
getAvailableWorkflows()
}
if (notificationWorkflow !== selectedOrganization?.defaults?.notification_workflow) {
setNotificationWorkflow(selectedOrganization?.defaults?.notification_workflow)
}
}, [selectedOrganization])
if (userdata === undefined || userdata === null) { if (userdata === undefined || userdata === null) {
return return
} }
@@ -220,11 +258,287 @@ const Priorities = memo((props) => {
const imagesize = 22 const imagesize = 22
const boxColor = "#86c142" const boxColor = "#86c142"
const getAvailableWorkflows = () => {
fetch(globalUrl + "/api/v1/workflows", {
method: "GET",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
credentials: "include",
})
.then((response) => {
if (response.status !== 200) {
console.log("Status not 200 for workflows :O!");
return;
}
return response.json();
})
.then((responseJson) => {
if (responseJson !== undefined) {
// Add parent notification workflow if it's a child org
// selectedOrganization,
if (selectedOrganization.creator_org !== undefined && selectedOrganization.creator_org !== null && selectedOrganization.creator_org.length > 0) {
// Add to start of the list
responseJson.unshift({
"name": "Parent-Org's Notification Workflow",
"id": "parent",
})
}
setWorkflows(responseJson)
if (selectedOrganization.defaults !== undefined && selectedOrganization.defaults.notification_workflow !== undefined) {
const workflow = responseJson.find((workflow) => workflow.id === selectedOrganization.defaults.notification_workflow)
if (workflow !== undefined && workflow !== null) {
setWorkflow(workflow)
}
}
}
})
.catch((error) => {
console.log("Error getting workflows: " + error);
})
}
const handleWorkflowSelectionUpdate = (e, isUserinput) => {
if (e.target.value === undefined || e.target.value === null || e.target.value.id === undefined) {
console.log("Returning as there's no id")
return null
}
setOpenNotification(false)
setWorkflow(e.target.value)
setNotificationWorkflow(e.target.value.id)
handleEditOrg(
selectedOrganization?.name,
selectedOrganization.description,
selectedOrganization.id,
selectedOrganization.image,
{
app_download_repo: selectedOrganization?.defaults?.app_download_repo,
app_download_branch: selectedOrganization?.defaults?.app_download_branch,
workflow_download_repo: selectedOrganization?.defaults?.workflow_download_repo,
workflow_download_branch: selectedOrganization?.defaults?.workflow_download_branch,
notification_workflow: e.target.value.id,
documentation_reference: selectedOrganization?.defaults?.documentation_reference,
workflow_upload_repo: selectedOrganization?.defaults?.workflow_upload_repo,
workflow_upload_branch: selectedOrganization?.defaults?.workflow_upload_branch,
workflow_upload_username: selectedOrganization?.defaults?.workflow_upload_username,
workflow_upload_token: selectedOrganization?.defaults?.workflow_upload_token,
newsletter: selectedOrganization?.defaults?.newsletter,
weekly_recommendations: selectedOrganization?.defaults?.weekly_recommendations,
},
{
sso_entrypoint: selectedOrganization?.sso_config?.sso_entrypoint,
sso_certificate: selectedOrganization?.sso_config?.sso_certificate,
client_id: selectedOrganization?.sso_config?.client_id,
client_secret: selectedOrganization?.sso_config?.client_secret,
openid_authorization: selectedOrganization?.sso_config?.openid_authorization,
openid_token: selectedOrganization?.sso_config?.openid_token,
SSORequired: selectedOrganization?.sso_config?.SSORequired,
auto_provision: selectedOrganization?.sso_config?.auto_provision,
}
)
}
return ( return (
<div style={{width: "100%", height: "100%", boxSizing: 'border-box', transition: 'width 0.3s ease', padding: clickedFromOrgTab ? "27px 10px 19px 27px":null, height: clickedFromOrgTab ? "auto":null, minHeight: 843, backgroundColor: clickedFromOrgTab ? '#212121':null, borderRadius: clickedFromOrgTab ? '16px':null, }}> <div style={{width: "100%", height: "100%", boxSizing: 'border-box', transition: 'width 0.3s ease', padding: clickedFromOrgTab ? "27px 10px 19px 27px":null, height: clickedFromOrgTab ? "auto":null, minHeight: 843, backgroundColor: clickedFromOrgTab ? '#212121':null, borderRadius: clickedFromOrgTab ? '16px':null, }}>
<div style={{ maxHeight: 1700, overflowY: "auto", width: '100%', scrollbarColor: '#494949 transparent', scrollbarWidth: 'thin'}}> <div style={{ maxHeight: 1700, overflowY: "auto", width: '100%', scrollbarColor: '#494949 transparent', scrollbarWidth: 'thin'}}>
<div style={{maxWidth: "calc(100% - 20px)"}}> <div style={{maxWidth: "calc(100% - 20px)"}}>
<Typography style={{ fontSize: 24, fontWeight: 'bold', display: clickedFromOrgTab?null:"inline", marginBottom: clickedFromOrgTab? 8:null, color: clickedFromOrgTab?"#ffffff":null }}>Notifications ({ <Typography variant="h5" style={{ color: "rgba(241, 241, 241, 1)", fontSize: 24, fontWeight: 600, textAlign: "left" }}>
Notification Workflow
</Typography>
<Typography style={{ color: "rgba(158, 158, 158, 1)", fontSize: 16, fontWeight: 400, marginTop: 5, }}>
The notification workflow triggers when an error occurs in one of your workflows. Each individual one will only start a workflow once every 2 minutes. <b>You can point child org notifications into the parent org notification by choosing it in the list.</b>
</Typography>
<div style={{ display: "flex", flexDirection: "row", alignItems: "center", }}>
{workflows !== undefined && workflows !== null && workflows.length > 0 ?
<Autocomplete
id="notification_workflow_search"
autoHighlight
open={openNotification}
onOpen={() => {
setOpenNotification(true);
}}
onClose={() => {
setOpenNotification(false);
}}
freeSolo
//autoSelect
value={workflows?.find(w => w.id === notificationWorkflow) || null}
classes={{ inputRoot: classes.inputRoot }}
ListboxProps={{
style: {
backgroundColor: "#212121",
color: "white",
},
}}
getOptionLabel={(option) => {
if (
option === undefined ||
option === null ||
option.name === undefined ||
option.name === null
) {
return "No Workflow Selected";
}
const newname = (
option.name.charAt(0).toUpperCase() + option.name.substring(1)
).replaceAll("_", " ");
return newname;
}}
options={workflows}
fullWidth
style={{
backgroundColor: "#212121",
borderRadius: theme.palette?.borderRadius,
height: 35,
marginBottom: 40,
}}
onChange={(event, newValue) => {
console.log("Found value: ", newValue)
var parsedinput = { target: { value: newValue } }
// For variables
if (typeof newValue === 'string' && newValue.startsWith("$")) {
parsedinput = {
target: {
value: {
"name": newValue,
"id": newValue,
"actions": [],
"triggers": [],
}
}
}
}
handleWorkflowSelectionUpdate(parsedinput)
}}
renderOption={(props, data, state) => {
if (data.id === workflow.id) {
data = workflow;
}
return (
<Tooltip arrow placement="right" title={
<span style={{}}>
{data.image !== undefined && data.image !== null && data.image.length > 0 ?
<img src={data.image} alt={data.name} style={{ backgroundColor: theme.palette.surfaceColor, maxHeight: 200, minHeigth: 200, borderRadius: theme.palette?.borderRadius, }} />
: null}
<Typography>
Choose {data.name}
</Typography>
</span>
} placement="bottom">
<MenuItem
{...props}
style={{
// backgroundColor: theme.palette.inputColor,
color: data.id === workflow.id ? "red" : "white",
borderBottom: data.id === "parent" ? "2px solid rgba(255,255,255,0.5)" : null
}}
value={data}
onClick={(e) => {
props.onMouseDown?.(null);
var parsedinput = { target: { value: data } }
handleWorkflowSelectionUpdate(parsedinput)
}}
>
{data.name}
</MenuItem>
</Tooltip>
)
}}
renderInput={(params) => {
return (
<TextField
{...params}
style={{
backgroundColor: "rgba(33, 33, 33, 1)",
borderRadius: 4,
height: 35,
fontSize: 16,
marginTop: "16px"
}}
InputProps={{
...params.InputProps,
style: {
height: 35,
display: "flex",
alignItems: "center",
padding: "0px 8px",
fontSize: 16,
borderRadius: 4,
},
inputProps: {
...params.inputProps,
style: {
height: "100%",
boxSizing: "border-box",
}
}
}}
// label="Find a notification workflow"
variant="outlined"
placeholder="Select a notification workflow"
/>
);
}}
/>
:
<TextField
required
InputProps={{
style: {
height: 35,
display: "flex",
alignItems: "center",
padding: "0px 8px",
fontSize: 16,
borderRadius: 4,
},
inputProps: {
style: {
height: "100%",
boxSizing: "border-box",
}
}
}}
style={{
backgroundColor: "rgba(33, 33, 33, 1)",
borderRadius: 4,
height: 35,
fontSize: 16,
marginBottom: 30
}}
fullWidth={true}
type="name"
id="outlined-with-placeholder"
margin="normal"
variant="outlined"
placeholder="ID of the workflow to receive notifications"
value={notificationWorkflow}
onChange={(e) => {
setNotificationWorkflow(e.target.value);
}}
/>
}
{/* <div style={{ minWidth: 150, maxWidth: 150, marginTop: 5, marginLeft: 10, }}>
{orgSaveButton}
</div> */}
</div>
<Typography style={{marginTop: 50, fontSize: 24, fontWeight: 'bold', display: clickedFromOrgTab?null:"inline", marginBottom: clickedFromOrgTab? 8:null, color: clickedFromOrgTab?"#ffffff":null }}>Notifications ({
notifications?.filter((notification) => showRead === true || notification.read === false).length notifications?.filter((notification) => showRead === true || notification.read === false).length
})</Typography> })</Typography>
@@ -261,10 +575,12 @@ const Priorities = memo((props) => {
</Button> </Button>
) : null} ) : null}
</div> </div>
<NotificationComponent notifications={notifications} showRead={showRead} selectedExecutionId={selectedExecutionId} selectedWorkflow={selectedWorkflow} highlightKMS={highlightKMS} userdata={userdata} imagesize={imagesize} boxColor={boxColor} clickedFromOrgTab={clickedFromOrgTab} notificationWidth={notificationWidth} dismissNotification={dismissNotification}/> <NotificationComponent notifications={notifications} showRead={showRead} selectedExecutionId={selectedExecutionId} selectedWorkflow={selectedWorkflow} highlightKMS={highlightKMS} userdata={userdata} imagesize={imagesize} boxColor={boxColor} clickedFromOrgTab={clickedFromOrgTab} notificationWidth={notificationWidth} dismissNotification={dismissNotification}/>
{clickedFromOrgTab? null : <Divider style={{marginTop: 50, marginBottom: 50, }} />} {clickedFromOrgTab? null : <Divider style={{marginTop: 50, marginBottom: 50, }} />}
<h2 style={{ display: clickedFromOrgTab ? null:"inline", marginBottom: clickedFromOrgTab ? 8:null, marginTop: clickedFromOrgTab ? 30 :null, color: clickedFromOrgTab ? "#ffffff" : null }}>Suggestions</h2>
<h2 style={{ display: clickedFromOrgTab ? null:"inline", marginBottom: clickedFromOrgTab ? 8:null, marginTop: clickedFromOrgTab ? 60 : null, color: clickedFromOrgTab ? "#ffffff" : null }}>Suggestions</h2>
<span style={{ fontSize: 16, color: clickedFromOrgTab ?"#9E9E9E":null,marginLeft: clickedFromOrgTab ?null:25, }}> <span style={{ fontSize: 16, color: clickedFromOrgTab ?"#9E9E9E":null,marginLeft: clickedFromOrgTab ?null:25, }}>
Suggestions are tasks identified by Shuffle to help you discover ways to protect your and customers' company. <br/>These range from simple configurations in Shuffle to Usecases you may have missed.&nbsp; Suggestions are tasks identified by Shuffle to help you discover ways to protect your and customers' company. <br/>These range from simple configurations in Shuffle to Usecases you may have missed.&nbsp;
<a <a
@@ -90,8 +90,8 @@ const pythonFilters = [
{ "name": "Use files", "value": `# Create a sample file\nfiles = [{\n \"name\": \"test.txt\",\n \"data\": \"Testdata\"\n}]\nret = self.set_files(files)\n\n# Get the content of the file from Shuffle storage\n# Originally a byte string in the \"data\" key\nfile_content = (self.get_file(ret[0])[\"data\"]).decode()\nprint(file_content)`, "example": `` }, { "name": "Use files", "value": `# Create a sample file\nfiles = [{\n \"name\": \"test.txt\",\n \"data\": \"Testdata\"\n}]\nret = self.set_files(files)\n\n# Get the content of the file from Shuffle storage\n# Originally a byte string in the \"data\" key\nfile_content = (self.get_file(ret[0])[\"data\"]).decode()\nprint(file_content)`, "example": `` },
{ "name": "Use datastore", "value": `key = \"testkey\"\nvalue = \"The value of the testkey\"\n\nself.set_cache(key, value)\n\n# Print the details of the key after it's been updated\n# To get the value, use self.get_cache(key)[\"value\"]\nprint(self.get_cache(key))`, "example": `` }, { "name": "Use datastore", "value": `key = \"testkey\"\nvalue = \"The value of the testkey\"\n\nself.set_cache(key, value)\n\n# Print the details of the key after it's been updated\n# To get the value, use self.get_cache(key)[\"value\"]\nprint(self.get_cache(key))`, "example": `` },
{ "name": "Run an App Action", "value": `response = self.run_app(app_id="app", action="action_name", auth="authentication_id", params={})\nprint(response)`, "example": ``, "disabled": true, }, { "name": "Run an App Action", "value": `response = shuffle.run_app(app_id="app", action="action_name", auth="authentication_id", params={})\nprint(response)`, "example": ``, "disabled": true, },
{ "name": "Run a Singul AI Action", "value": `response = self.create_ticket(app="jira/iris/ticketingsystem", fields={"title": "Test ticket!"})\nprint(response)`, "example": ``, "disabled": true, }, { "name": "Run a Singul AI Action", "value": `response = singul.create_ticket(app="jira/iris/ticketingsystem", fields={"title": "Test ticket!"})\nprint(response)`, "example": ``, "disabled": true, },
] ]
@@ -1416,7 +1416,7 @@ const CodeEditor = (props) => {
return ( return (
<MenuItem key={index} onClick={() => { <MenuItem key={index} onClick={() => {
if (item.disabled) { if (item.disabled) {
toast.error("This feature may not work in your environment yet, and is awaiting updates to the Shuffle python execution environment.", { autoClose: 10000 }) toast.error("This feature may not work in your environment until you update your Shuffle Tools app.", { autoClose: 10000 })
} }
if (selectedAction.name !== "execute_python") { if (selectedAction.name !== "execute_python") {
@@ -26,6 +26,7 @@ import {
Close as CloseIcon, Close as CloseIcon,
East as EastIcon, East as EastIcon,
Interests as InterestsIcon, Interests as InterestsIcon,
OpenInNew as OpenInNewIcon,
} from '@mui/icons-material'; } from '@mui/icons-material';
import { import {
@@ -35,7 +36,8 @@ import {
grey, grey,
} from "../views/AngularWorkflow.jsx" } from "../views/AngularWorkflow.jsx"
import WorkflowTemplatePopup2 from "./WorkflowTemplatePopup.jsx"; //import WorkflowTemplatePopup2 from "./WorkflowTemplatePopup2.jsx";
import WorkflowTemplatePopup2 from "./WorkflowTemplatePopup2.jsx";
import ConfigureWorkflow from "../components/ConfigureWorkflow.jsx"; import ConfigureWorkflow from "../components/ConfigureWorkflow.jsx";
import WorkflowValidationTimeline from "../components/WorkflowValidationTimeline.jsx"; import WorkflowValidationTimeline from "../components/WorkflowValidationTimeline.jsx";
import FixWorkflowValidationErrors from "../components/FixWorkflowValidationErrors.jsx"; import FixWorkflowValidationErrors from "../components/FixWorkflowValidationErrors.jsx";
@@ -47,9 +49,11 @@ const WorkflowTemplatePopup = (props) => {
isModalOpenDefault, isModalOpenDefault,
setIsClicked, setIsClicked,
inputWorkflowId, inputWorkflowId,
inputWorkflow,
} = props; } = props;
const [isActive, setIsActive] = useState(workflowBuilt === true); const [isActive, setIsActive] = useState(workflowBuilt === true || (workflowBuilt !== undefined && workflowBuilt !== null && workflowBuilt?.length > 0) || (inputWorkflow !== undefined && inputWorkflow !== null && inputWorkflow.id !== undefined && inputWorkflow.id !== null && inputWorkflow.id !== "") ? true : false)
const [isHovered, setIsHovered] = useState(false); const [isHovered, setIsHovered] = useState(false);
const [modalOpen, setModalOpen] = useState(isModalOpenDefault === true ? true : false) const [modalOpen, setModalOpen] = useState(isModalOpenDefault === true ? true : false)
const [errorMessage, setErrorMessage] = useState(""); const [errorMessage, setErrorMessage] = useState("");
@@ -65,7 +69,7 @@ const WorkflowTemplatePopup = (props) => {
const [showTryitOut, setShowTryitout] = React.useState(showTryit === true ? true : false) const [showTryitOut, setShowTryitout] = React.useState(showTryit === true ? true : false)
const [loadingWorkflow, setLoadingWorkflow] = React.useState(false) const [loadingWorkflow, setLoadingWorkflow] = React.useState(false)
const [workflow, setWorkflow] = useState({}); const [workflow, setWorkflow] = useState(inputWorkflow !== undefined && inputWorkflow !== null && inputWorkflow.id !== undefined && inputWorkflow.id !== null && inputWorkflow.id !== "" ? inputWorkflow : {})
const [_, setUpdate] = useState(0) const [_, setUpdate] = useState(0)
const fetchWorkflow = (id) => { const fetchWorkflow = (id) => {
@@ -455,7 +459,7 @@ const WorkflowTemplatePopup = (props) => {
//console.log("Error in workflow template: ", responseJson.error); //console.log("Error in workflow template: ", responseJson.error);
setRequestSent(false) setRequestSent(false)
const defaultMessage = "Error: Failed to generate workflow the workflow - the Shuffle team has been notified. Contact support@shuffler.io if you want manual help building this usecase until the AI system is handled." const defaultMessage = "Error: Failed to generate workflow the workflow - the Shuffle team has been notified. Contact support@shuffler.io if you want manual help building this usecase."
if (responseJson.reason !== undefined && responseJson.reason !== null && responseJson.reason !== "") { if (responseJson.reason !== undefined && responseJson.reason !== null && responseJson.reason !== "") {
setErrorMessage(defaultMessage + "\n\n" + responseJson.reason) setErrorMessage(defaultMessage + "\n\n" + responseJson.reason)
} else { } else {
@@ -535,8 +539,8 @@ const WorkflowTemplatePopup = (props) => {
style: { style: {
backgroundColor: "black", backgroundColor: "black",
color: "white", color: "white",
minWidth: isHomePage ? null : isMobile ? 300 : 850, minWidth: isHomePage ? null : isMobile ? 300 : 750,
maxWidth: isHomePage ? null : isMobile ? 300 : 850, maxWidth: isHomePage ? null : isMobile ? 300 : 750,
paddingTop: isMobile ? null : 75, paddingTop: isMobile ? null : 75,
itemAlign: "center", itemAlign: "center",
}, },
@@ -564,7 +568,7 @@ const WorkflowTemplatePopup = (props) => {
{title === undefined || title === null || title === "" ? null : {title === undefined || title === null || title === "" ? null :
<span> <span>
<Typography variant="body2" color="textSecondary" style={{marginTop: 25, }}> <Typography variant="body2" color="textSecondary" style={{marginTop: 25, }}>
Selected Workflow: Selected Usecase:
</Typography> </Typography>
<div style={{marginBottom: 0, }} id="workflow-template"> <div style={{marginBottom: 0, }} id="workflow-template">
<WorkflowTemplatePopup2 <WorkflowTemplatePopup2
@@ -575,9 +579,10 @@ const WorkflowTemplatePopup = (props) => {
dstapp={dstapp} dstapp={dstapp}
title={title} title={title}
description={description} description={description}
visualOnly={true}
visualOnly={true}
workflowBuilt={workflowBuilt} workflowBuilt={workflowBuilt}
inputWorkflow={workflow}
shownColor={shownColor} shownColor={shownColor}
/> />
@@ -585,7 +590,7 @@ const WorkflowTemplatePopup = (props) => {
</span> </span>
} }
<div style={{marginTop: 15, }}> <div style={{marginTop: 0, }}>
{/* Fix the timeline when errors are fixed.. how? */} {/* Fix the timeline when errors are fixed.. how? */}
<WorkflowValidationTimeline <WorkflowValidationTimeline
workflow={workflow} workflow={workflow}
@@ -601,21 +606,23 @@ const WorkflowTemplatePopup = (props) => {
</div> </div>
{workflowLoading === true ? {workflowLoading === true ?
<div style={{marginTop: 75, textAlign: "center", }}> <div style={{marginTop: 60, textAlign: "center", }}>
<Typography variant="h4"> Generating the Workflow... <Typography variant="h4"> Generating Workflows...
</Typography> </Typography>
<CircularProgress style={{marginLeft: 0, marginTop: 25, }}/> <CircularProgress style={{marginLeft: 0, marginTop: 25, }}/>
</div> </div>
: :
<div> <div>
{usecaseDetails === undefined ? null : {usecaseDetails === undefined || usecaseDetails === null || workflow.id !== undefined ? null :
<Typography variant="h6" style={{marginTop: 75, }}> <Typography variant="body1" style={{marginTop: 60, }} color="textSecondary">
{usecaseDetails?.description} {usecaseDetails?.description}
</Typography> </Typography>
} }
{errorMessage !== "" ?
<Typography variant="h6" style={{marginTop: 75, }}> <Typography variant="h6" style={{marginTop: 75, }}>
{errorMessage !== "" ? errorMessage : ""} {errorMessage !== "" ? errorMessage : ""}
</Typography> </Typography>
: null}
{showLoginButton ? {showLoginButton ?
<Link to="/register?message=Please login to create workflows&view=usecases" <Link to="/register?message=Please login to create workflows&view=usecases"
style={{ style={{
@@ -643,6 +650,7 @@ const WorkflowTemplatePopup = (props) => {
variant="outlined" variant="outlined"
style={{ style={{
textTransform: "none", textTransform: "none",
marginTop: 15,
}} }}
onClick={() => { onClick={() => {
//setWorkflowLoading(true) //setWorkflowLoading(true)
@@ -730,11 +738,11 @@ const WorkflowTemplatePopup = (props) => {
const parsedDescription = description !== undefined && description !== null ? description.replaceAll("_", " ") : "" const parsedDescription = description !== undefined && description !== null ? description.replaceAll("_", " ") : ""
const boxHeight = 104 const boxHeight = visualOnly ? 75 : 104
const highlightColor = shownColor !== undefined && shownColor !== null && shownColor !== "" ? shownColor : "#f85a3e" const highlightColor = shownColor !== undefined && shownColor !== null && shownColor !== "" ? shownColor : "#f85a3e"
var hasInterest = false var hasInterest = false
if (userdata.interests !== undefined && userdata.interests !== null && userdata.interests.length > 0) { if (userdata?.interests !== undefined && userdata?.interests !== null && userdata?.interests?.length > 0) {
const comparisonTitle = title === undefined || title === null ? "" : title.trim().toLowerCase().replaceAll(" ", "_") const comparisonTitle = title === undefined || title === null ? "" : title.trim().toLowerCase().replaceAll(" ", "_")
for (var interestkey in userdata.interests) { for (var interestkey in userdata.interests) {
if (userdata.interests[interestkey].name === undefined || userdata.interests[interestkey].name === null || userdata.interests[interestkey].name === "") { if (userdata.interests[interestkey].name === undefined || userdata.interests[interestkey].name === null || userdata.interests[interestkey].name === "") {
@@ -742,12 +750,12 @@ const WorkflowTemplatePopup = (props) => {
} }
if (modalOpen) { if (modalOpen) {
console.log("COMPARE: ", userdata.interests[interestkey].name.trim().toLowerCase().replaceAll(" ", "_"), comparisonTitle) //console.log("COMPARE: ", userdata.interests[interestkey].name.trim().toLowerCase().replaceAll(" ", "_"), comparisonTitle)
} }
if (userdata.interests[interestkey].name.trim().toLowerCase().replaceAll(" ", "_") === comparisonTitle) { if (userdata.interests[interestkey].name.trim().toLowerCase().replaceAll(" ", "_") === comparisonTitle) {
if (modalOpen) { if (modalOpen) {
console.log("FOUND: ", comparisonTitle) //console.log("FOUND: ", comparisonTitle)
} }
hasInterest = true hasInterest = true
@@ -791,7 +799,16 @@ const WorkflowTemplatePopup = (props) => {
}} }}
onClick={() => { onClick={() => {
if (visualOnly === true) { if (visualOnly === true) {
console.log("Not showing more than visuals.") console.log("Not showing more than visuals. Workflow built: ", workflowBuilt, workflow)
if (workflowBuilt !== undefined && workflowBuilt !== null && workflowBuilt?.length > 0) {
window.open("/workflows/" + workflowBuilt, "_blank")
} else if (workflow.id !== undefined && workflow.id !== null && workflow.id !== "") {
window.open("/workflows/" + workflow.id, "_blank")
} else {
toast("Click 'Try this usecase' to generate workflows for this usecase.")
}
return return
} }
@@ -822,7 +839,7 @@ const WorkflowTemplatePopup = (props) => {
: null} : null}
<div style={{ display: "flex", itemAlign: "left", textAlign: "left", }}> <div style={{ display: "flex", itemAlign: "left", textAlign: "left", }}>
<div style={{display: "flex", flex: 1, marginLeft: 25, marginTop: showTryitOut && !isActive ? 14 : 30, }}> <div style={{display: "flex", flex: 1, marginLeft: 25, marginTop: visualOnly ? 18 : showTryitOut && !isActive ? 14 : 30, }}>
<div style={{zIndex: 51}}> <div style={{zIndex: 51}}>
{img1 !== undefined && img1 !== "" && srcapp !== undefined && srcapp !== "" ? {img1 !== undefined && img1 !== "" && srcapp !== undefined && srcapp !== "" ?
<Tooltip title={srcapp.replaceAll(":default", "").replaceAll("_", " ").replaceAll(" API", "")} placement="top"> <Tooltip title={srcapp.replaceAll(":default", "").replaceAll("_", " ").replaceAll(" API", "")} placement="top">
@@ -849,7 +866,7 @@ const WorkflowTemplatePopup = (props) => {
} }
</div> </div>
<div style={{ marginLeft: 20, overflow: "hidden", maxHeight: 30, marginTop: showTryitOut && !isActive ? 8 : 23, }}> <div style={{ marginLeft: 20, overflow: "hidden", maxHeight: 30, marginTop: visualOnly ? 12 : showTryitOut && !isActive ? 8 : 23, }}>
<Typography variant="body1" style={{ marginTop: parsedDescription.length === 0 ? 10 : 0, fontSize: isMobile ? 13 : 16, fontWeight: isHomePage ? 600 : null, textTransform: 'capitalize', color: isHomePage ? "var(--White-text, #F1F1F1)" : "rgba(241, 241, 241, 1)"}} > <Typography variant="body1" style={{ marginTop: parsedDescription.length === 0 ? 10 : 0, fontSize: isMobile ? 13 : 16, fontWeight: isHomePage ? 600 : null, textTransform: 'capitalize', color: isHomePage ? "var(--White-text, #F1F1F1)" : "rgba(241, 241, 241, 1)"}} >
<b>{parsedTitle}</b> <b>{parsedTitle}</b>
</Typography> </Typography>
@@ -858,13 +875,19 @@ const WorkflowTemplatePopup = (props) => {
</div> </div>
<div> <div>
{isActive === true && errorMessage === "" ? {isActive === true && errorMessage === "" ?
visualOnly === true ?
<Tooltip title="Open the workflow in a new tab" placement="top">
<OpenInNewIcon color="primary" sx={{ borderRadius: 4 }} style={{ position: "absolute", top: 22, right: 20, }} />
</Tooltip>
:
<Tooltip title="You already have workflows that are based on this usecase" placement="top"> <Tooltip title="You already have workflows that are based on this usecase" placement="top">
<CheckIcon color="primary" sx={{ borderRadius: 4 }} style={{ position: "absolute", color: theme.palette.green, top: 10, right: 10, }} /> <CheckIcon color="primary" sx={{ borderRadius: 4 }} style={{ position: "absolute", color: theme.palette.green, top: 10, right: 10, }} />
</Tooltip> </Tooltip>
: ""} : ""}
{!isActive && hasInterest === true ? {!isActive && hasInterest === true && !visualOnly ?
<Tooltip title="Your team has shown interest in this usecase previously." placement="top"> <Tooltip title="Your team has shown interest in this usecase previously." placement="top">
<InterestsIcon color="primary" sx={{ borderRadius: 4 }} style={{ position: "absolute", color: "rgba(254, 204, 0, 0.5)", top: 10, right: 10, }} /> <InterestsIcon color="primary" sx={{ borderRadius: 4 }} style={{ position: "absolute", color: "rgba(254, 204, 0, 0.5)", top: 10, right: 10, }} />
</Tooltip> </Tooltip>
@@ -872,7 +895,7 @@ const WorkflowTemplatePopup = (props) => {
</div> </div>
{showTryitOut && !isActive ? {showTryitOut && !isActive && !visualOnly ?
<Fade in={showTryitOut} timeout={300}> <Fade in={showTryitOut} timeout={300}>
<Button <Button
variant="text" variant="text"
@@ -287,7 +287,7 @@ const WorkflowValidationTimeline = (props) => {
var scheduleNotStarted = false var scheduleNotStarted = false
if (workflow.validation !== undefined && workflow.validation !== null && workflow.validation.validation_ran === false) { if (workflow.validation !== undefined && workflow.validation !== null && workflow.validation.validation_ran === false) {
console.log("Validation didn't run. Why?") console.log("Validation didn't run or get set for workflow. Why?")
return null return null
} }
@@ -367,10 +367,13 @@ const WorkflowValidationTimeline = (props) => {
action.result = foundResult action.result = foundResult
action.status = foundResult.status action.status = foundResult.status
} else {
action.status = "SUCCESS"
} }
} }
const lastitem = index === relevantactions.length - 1 const lastitem = index === relevantactions.length - 1
if (!lastitem) { if (!lastitem) {
if (action.app_name === "Shuffle Tools") { if (action.app_name === "Shuffle Tools") {
if (action.status === "SUCCESS") { if (action.status === "SUCCESS") {
@@ -386,9 +389,10 @@ const WorkflowValidationTimeline = (props) => {
nodecolor = grey nodecolor = grey
branchcolor = grey branchcolor = grey
} }
} else {
nodecolor = green
branchcolor = green
} }
} else if (action.status === "SKIPPED") { } else if (action.status === "SKIPPED") {
branchcolor = grey branchcolor = grey
} else { } else {
@@ -489,7 +493,7 @@ const WorkflowValidationTimeline = (props) => {
if (!showMiddle && relevantactions.length > 2 && index > 0 && index === relevantactions.length - 2) { if (!showMiddle && relevantactions.length > 2 && index > 0 && index === relevantactions.length - 2) {
if (founderror.length > 0) { if (founderror.length > 0) {
middleError += founderror+"\n" middleError += action.label+": "+founderror+"\n\n"
middleBranchColor = branchcolor middleBranchColor = branchcolor
} }
@@ -497,11 +501,18 @@ const WorkflowValidationTimeline = (props) => {
if (index === relevantactions.length-2 && relevantactions.length > 2) { if (index === relevantactions.length-2 && relevantactions.length > 2) {
const selectedIcon = middleError.length > 0 ? const selectedIcon = middleError.length > 0 ?
<Tooltip title={ <Tooltip
title={
<Typography variant="body1" style={{margin: 5, whiteSpace: "pre-line", }}> <Typography variant="body1" style={{margin: 5, whiteSpace: "pre-line", }}>
{middleError} {middleError}
</Typography> </Typography>
}> }
inputProps={{
paperProps: {
backgroundColor: "red",
}
}}
>
<IconButton style={{width: 30, height: 30, backgroundColor: "rgba(255,255,255,0.0)", borderRadius: 30, marginTop: 2, }}> <IconButton style={{width: 30, height: 30, backgroundColor: "rgba(255,255,255,0.0)", borderRadius: 30, marginTop: 2, }}>
<ErrorOutlineIcon style={{color: "red", }} /> <ErrorOutlineIcon style={{color: "red", }} />
</IconButton> </IconButton>
@@ -517,7 +528,7 @@ const WorkflowValidationTimeline = (props) => {
// Returns for anything non-middle // Returns for anything non-middle
if (relevantactions.length > 2 && index >= 1 && index < relevantactions.length - 2) { if (relevantactions.length > 2 && index >= 1 && index < relevantactions.length - 2) {
if (founderror.length > 0) { if (founderror.length > 0) {
middleError += founderror+"\n" middleError += action.label+": "+founderror+"\n\n"
} }
return null return null
+216 -169
View File
@@ -417,8 +417,8 @@ const AngularWorkflow = (defaultprops) => {
const [editWorkflowDetails, setEditWorkflowDetails] = React.useState(false); const [editWorkflowDetails, setEditWorkflowDetails] = React.useState(false);
const [workflow, setWorkflow] = React.useState({}); const [workflow, setWorkflow] = React.useState({});
const [currentWorkflow, setCurrentWorkflow] = React.useState({}); // only for suborg distribution
const [originalWorkflow, setOriginalWorkflow] = React.useState({}); const [originalWorkflow, setOriginalWorkflow] = React.useState({});
const [userSettings, setUserSettings] = React.useState({});
const [subworkflow, setSubworkflow] = React.useState({}); const [subworkflow, setSubworkflow] = React.useState({});
const [subworkflowStartnode, setSubworkflowStartnode] = React.useState(""); const [subworkflowStartnode, setSubworkflowStartnode] = React.useState("");
const [leftViewOpen, setLeftViewOpen] = React.useState(isMobile ? false : true); const [leftViewOpen, setLeftViewOpen] = React.useState(isMobile ? false : true);
@@ -1254,13 +1254,18 @@ const releaseToConnectLabel = "Release to Connect"
}; };
const getWorkflowExecutionCount = (workflowId) => { const getWorkflowExecutionCount = (workflowId) => {
var headers = {
"Content-Type": "application/json",
"Accept": "application/json",
}
if (workflow.org_id !== undefined && workflow.org_id !== null && workflow.org_id.length > 0) {
headers["Org-Id"] = workflow.org_id
}
fetch(`${globalUrl}/api/v1/workflows/${workflowId}/executions/count`, { fetch(`${globalUrl}/api/v1/workflows/${workflowId}/executions/count`, {
method: "GET", method: "GET",
headers: { headers: headers,
"Content-Type": "application/json",
Accept: "application/json",
},
credentials: "include", credentials: "include",
}) })
.then((response) => { .then((response) => {
@@ -1282,12 +1287,18 @@ const releaseToConnectLabel = "Release to Connect"
}; };
const getAvailableWorkflows = (trigger_index) => { const getAvailableWorkflows = (trigger_index) => {
var headers = {
"Content-Type": "application/json",
"Accept": "application/json",
}
if (workflow.org_id !== undefined && workflow.org_id !== null && workflow.org_id.length > 0) {
headers["Org-Id"] = workflow.org_id
}
fetch(globalUrl + "/api/v1/workflows?subflow=true", { fetch(globalUrl + "/api/v1/workflows?subflow=true", {
method: "GET", method: "GET",
headers: { headers: headers,
"Content-Type": "application/json",
Accept: "application/json",
},
credentials: "include", credentials: "include",
}) })
.then((response) => { .then((response) => {
@@ -1312,13 +1323,13 @@ const releaseToConnectLabel = "Release to Connect"
// User Input & Subflow nodes // User Input & Subflow nodes
if (param.name === "workflow" || param.name === "subflow") { if (param.name === "workflow" || param.name === "subflow") {
const paramIndex = param.name === "workflow" ? 0 : 5 const paramIndex = param.name === "workflow" ? 0 : 5
if (workflow.triggers[trigger_index].parameters[paramIndex].value !== subworkflow.id) { if (workflow.triggers[trigger_index].parameters[paramIndex].value !== subworkflow?.id) {
if (param.value === workflow.id) { if (param.value === workflow?.id) {
setSubworkflow(workflow); setSubworkflow(workflow);
baseSubflow = workflow baseSubflow = workflow
} else { } else {
const sub = responseJson.find((data) => data.id === param.value); const sub = responseJson.find((data) => data?.id === param.value);
if (sub !== undefined && subworkflow.id !== sub.id) { if (sub !== undefined && subworkflow?.id !== sub?.id) {
baseSubflow = sub baseSubflow = sub
setSubworkflow(sub); setSubworkflow(sub);
} }
@@ -1329,7 +1340,7 @@ const releaseToConnectLabel = "Release to Connect"
if (param.name === "startnode" && param.value !== undefined && param.value !== null) { if (param.name === "startnode" && param.value !== undefined && param.value !== null) {
if (Object.getOwnPropertyNames(baseSubflow).length > 0) { if (Object.getOwnPropertyNames(baseSubflow).length > 0) {
const foundAction = baseSubflow.actions.find(action => action.id === param.value) const foundAction = baseSubflow.actions.find(action => action?.id === param.value)
if (foundAction !== null && foundAction !== undefined) { if (foundAction !== null && foundAction !== undefined) {
setSubworkflowStartnode(foundAction); setSubworkflowStartnode(foundAction);
} }
@@ -1354,11 +1365,11 @@ const releaseToConnectLabel = "Release to Connect"
for (let paramkey in trigger.parameters) { for (let paramkey in trigger.parameters) {
const param = trigger.parameters[paramkey] const param = trigger.parameters[paramkey]
if ((param.name === "workflow" || param.name === "subflow") && param.value === props.match.params.key && !parent_ids.includes(innerworkflow.id)) { if ((param.name === "workflow" || param.name === "subflow") && param.value === props.match.params.key && !parent_ids.includes(innerworkflow?.id)) {
parent_ids.push(innerworkflow.id) parent_ids.push(innerworkflow?.id)
parentworkflows.push({ parentworkflows.push({
id: innerworkflow.id, id: innerworkflow?.id,
name: innerworkflow.name, name: innerworkflow.name,
image: innerworkflow.image, image: innerworkflow.image,
}) })
@@ -1369,7 +1380,7 @@ const releaseToConnectLabel = "Release to Connect"
} }
if (parentworkflows.length > 0) { if (parentworkflows.length > 0) {
setParentWorkflows(parentworkflows.filter(wf => wf.id !== props.match.params.key)) setParentWorkflows(parentworkflows.filter(wf => wf?.id !== props.match.params.key))
} }
} }
@@ -1404,65 +1415,6 @@ const releaseToConnectLabel = "Release to Connect"
); );
} }
const generateApikey = () => {
fetch(globalUrl + "/api/v1/generateapikey", {
method: "GET",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
credentials: "include",
})
.then((response) => {
if (response.status !== 200) {
console.log("Status not 200 for APIKEY gen :O!");
}
return response.json();
})
.then((responseJson) => {
setUserSettings(responseJson);
})
.catch((error) => {
console.log("Apikey error: ", error);
});
};
const getSettings = () => {
fetch(globalUrl + "/api/v1/getsettings", {
method: "GET",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
credentials: "include",
})
.then((response) => {
if (response.status !== 200) {
console.log("Status not 200 for get settings :O!");
}
return response.json();
})
.then((responseJson) => {
if (
responseJson.success === true &&
(responseJson.apikey === undefined ||
responseJson.apikey.length === 0 ||
responseJson.apikey === null)
) {
generateApikey();
}
if (responseJson.success === true) {
setUserSettings(responseJson)
}
})
.catch((error) => {
console.log("Settings error: ", error);
});
};
const setNewAppAuth = (appAuthData, refresh) => { const setNewAppAuth = (appAuthData, refresh) => {
var headers = { var headers = {
"Content-Type": "application/json", "Content-Type": "application/json",
@@ -1537,12 +1489,22 @@ const releaseToConnectLabel = "Release to Connect"
} }
} }
var formattedBody = { let headers = {
method: method,
headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
Accept: "application/json", Accept: "application/json",
}, }
if (currentWorkflow?.id?.length > 0 && currentWorkflow?.id !== undefined && currentWorkflow?.id !== null) {
id = currentWorkflow.id;
}
if (currentWorkflow?.org_id?.length > 0 && currentWorkflow?.org_id !== undefined && currentWorkflow?.org_id !== null) {
headers["Org-Id"] = currentWorkflow.org_id;
}
var formattedBody = {
method: method,
headers: headers,
credentials: "include", credentials: "include",
} }
@@ -1653,12 +1615,18 @@ const releaseToConnectLabel = "Release to Connect"
}; };
const fetchUpdates = () => { const fetchUpdates = () => {
var headers = {
"Content-Type": "application/json",
"Accept": "application/json",
}
if (workflow.org_id !== undefined && workflow.org_id !== null && workflow.org_id.length > 0) {
headers["Org-Id"] = workflow.org_id
}
fetch(globalUrl + "/api/v1/streams/results", { fetch(globalUrl + "/api/v1/streams/results", {
method: "POST", method: "POST",
headers: { headers: headers,
"Content-Type": "application/json",
Accept: "application/json",
},
body: JSON.stringify(executionRequest), body: JSON.stringify(executionRequest),
credentials: "include", credentials: "include",
cors: "no-cors", cors: "no-cors",
@@ -1689,13 +1657,19 @@ const releaseToConnectLabel = "Release to Connect"
const abortExecution = () => { const abortExecution = () => {
setExecutionRunning(false); setExecutionRunning(false);
var headers = {
"Content-Type": "application/json",
"Accept": "application/json",
}
if (workflow.org_id !== undefined && workflow.org_id !== null && workflow.org_id.length > 0) {
headers["Org-Id"] = workflow.org_id
}
fetch(globalUrl + "/api/v1/workflows/" + props.match.params.key + "/executions/" + executionRequest.execution_id + "/abort", fetch(globalUrl + "/api/v1/workflows/" + props.match.params.key + "/executions/" + executionRequest.execution_id + "/abort",
{ {
method: "GET", method: "GET",
headers: { headers: headers,
"Content-Type": "application/json",
Accept: "application/json",
},
credentials: "include", credentials: "include",
} }
) )
@@ -2014,12 +1988,18 @@ const releaseToConnectLabel = "Release to Connect"
console.log("Error parsing body for stream: ", e) console.log("Error parsing body for stream: ", e)
} }
var headers = {
"Content-Type": "application/json",
"Accept": "application/json",
}
if (workflow.org_id !== undefined && workflow.org_id !== null && workflow.org_id.length > 0) {
headers["Org-Id"] = workflow.org_id
}
fetch(url, { fetch(url, {
method: "POST", method: "POST",
headers: { headers: headers,
"Content-Type": "application/json",
Accept: "application/json",
},
body: parsedbody, body: parsedbody,
credentials: "include", credentials: "include",
}) })
@@ -2372,9 +2352,11 @@ const releaseToConnectLabel = "Release to Connect"
return; return;
} }
if (!responseJson.success) { if (!responseJson.success) {
setSavingState(0); setSavingState(0);
console.log(responseJson); console.log("Workflow failed loading: ", responseJson);
if (responseJson.reason !== undefined && responseJson.reason !== null) { if (responseJson.reason !== undefined && responseJson.reason !== null) {
toast("Failed to save: " + responseJson.reason); toast("Failed to save: " + responseJson.reason);
} else { } else {
@@ -2482,7 +2464,7 @@ const releaseToConnectLabel = "Release to Connect"
setTimeout(() => { setTimeout(() => {
setSavingState(0); setSavingState(0);
}, 1500); }, 1500);
getRevisionHistory(useworkflow.id) getRevisionHistory(useworkflow.id, 50, 0, useworkflow.org_id)
} }
}) })
.catch((error) => { .catch((error) => {
@@ -2641,7 +2623,8 @@ const releaseToConnectLabel = "Release to Connect"
} }
const data = { execution_argument: executionArgument, start: startNode }; const data = { execution_argument: executionArgument, start: startNode };
fetch(`${globalUrl}/api/v1/workflows/${props.match.params.key}/execute`, // fetch(`${globalUrl}/api/v1/workflows/${props.match.params.key}/execute`,
fetch(`${globalUrl}/api/v1/workflows/${workflow.id}/execute`,
{ {
method: "POST", method: "POST",
headers: headers, headers: headers,
@@ -3820,7 +3803,8 @@ const releaseToConnectLabel = "Release to Connect"
if (responseJson.public) { if (responseJson.public) {
setAppAuthentication([]) setAppAuthentication([])
console.log("RESP: ", responseJson) setLeftBarSize(300)
if (Object.getOwnPropertyNames(creatorProfile).length === 0) { if (Object.getOwnPropertyNames(creatorProfile).length === 0) {
//getUserProfile("frikky") //getUserProfile("frikky")
getUserProfile(responseJson.id, false) getUserProfile(responseJson.id, false)
@@ -3851,11 +3835,11 @@ const releaseToConnectLabel = "Release to Connect"
setTriggerGroup(appsFound) setTriggerGroup(appsFound)
setWorkflows([responseJson]) setWorkflows([responseJson])
setCurrentWorkflow(responseJson)
} else { } else {
getAppAuthentication(); getAppAuthentication();
getEnvironments(responseJson.org_id) getEnvironments(responseJson.org_id)
getSettings();
getFiles() getFiles()
getWorkflowExecution(props.match.params.key, ""); getWorkflowExecution(props.match.params.key, "");
getAvailableWorkflows(-1); getAvailableWorkflows(-1);
@@ -4007,7 +3991,9 @@ const releaseToConnectLabel = "Release to Connect"
decorator: true, decorator: true,
source_workflow: responseJson.id, source_workflow: responseJson.id,
}, },
}); })
} else {
console.log("Node not found: ", target_id)
} }
cy.fit(null, 400); cy.fit(null, 400);
@@ -5819,7 +5805,6 @@ const releaseToConnectLabel = "Release to Connect"
setWorkflows([workflow]) setWorkflows([workflow])
} else { } else {
getAvailableWorkflows(trigger_index); getAvailableWorkflows(trigger_index);
getSettings();
} }
} else if (data.app_name === "Webhook") { } else if (data.app_name === "Webhook") {
if (workflow.triggers[trigger_index].parameters !== undefined && workflow.triggers[trigger_index].parameters !== null && workflow.triggers[trigger_index].parameters.length > 0) { if (workflow.triggers[trigger_index].parameters !== undefined && workflow.triggers[trigger_index].parameters !== null && workflow.triggers[trigger_index].parameters.length > 0) {
@@ -8388,9 +8373,8 @@ const releaseToConnectLabel = "Release to Connect"
const node = {}; const node = {};
node.position = trigger.position; node.position = trigger.position;
if (trigger.large_image === undefined || trigger.large_image === null || trigger.large_image.length === 0) {
// Search triggers array for it where the name is matching and set image // Search triggers array for it where the name is matching and set image
if (trigger.large_image === undefined || trigger.large_image === null || trigger.large_image.length === 0) {
var foundTrigger = triggers.find((t) => t.name === trigger.name) var foundTrigger = triggers.find((t) => t.name === trigger.name)
if (foundTrigger !== undefined && foundTrigger !== null) { if (foundTrigger !== undefined && foundTrigger !== null) {
console.log("Autofilled missing trigger image") console.log("Autofilled missing trigger image")
@@ -8403,6 +8387,60 @@ const releaseToConnectLabel = "Release to Connect"
node.data.id = trigger["id"]; node.data.id = trigger["id"];
node.data.type = "TRIGGER"; node.data.type = "TRIGGER";
// Adds the correct branching for same-workflow trigger
if (trigger?.trigger_type === "SUBFLOW" && trigger?.parameters !== undefined && trigger?.parameters !== null && trigger?.parameters.length > 0) {
var foundTargetNode = ""
var sameWorkflow = false
for (var key in trigger.parameters) {
if (trigger.parameters[key].name === "workflow" && trigger.parameters[key].value === inputworkflow.id) {
sameWorkflow = true
}
if (trigger.parameters[key].name === "startnode" && trigger.parameters[key].value !== "") {
foundTargetNode = trigger.parameters[key].value
}
}
if (sameWorkflow && foundTargetNode !== "") {
const newid = uuidv4()
const newbranch = {
id: newid,
_id: newid,
source: trigger.id,
source_id: trigger.id,
target: foundTargetNode,
destination_id: foundTargetNode,
conditions: [],
has_errors: false,
decorator: true,
label: "Subflow",
}
if (inputworkflow.visual_branches !== undefined) {
if (inputworkflow.visual_branches === null) {
inputworkflow.visual_branches = [newbranch]
} else if (inputworkflow.visual_branches.length === 0) {
inputworkflow.visual_branches.push(newbranch)
} else {
const foundIndex = inputworkflow.visual_branches.findIndex(
(branch) => branch.source_id === newbranch.source_id
)
if (foundIndex !== -1) {
//console.log("Already found subflow branch")
} else {
inputworkflow.visual_branches.push(newbranch);
}
}
} else {
inputworkflow.visual_branches = [newbranch]
}
}
}
if (inputworkflow?.id !== undefined && originalWorkflow?.id !== undefined && inputworkflow?.id !== originalWorkflow?.id && originalWorkflow.triggers !== undefined && originalWorkflow.triggers !== null && originalWorkflow.triggers.length > 0) { if (inputworkflow?.id !== undefined && originalWorkflow?.id !== undefined && inputworkflow?.id !== originalWorkflow?.id && originalWorkflow.triggers !== undefined && originalWorkflow.triggers !== null && originalWorkflow.triggers.length > 0) {
// Find the node in the original workflow // Find the node in the original workflow
var inParent = false var inParent = false
@@ -8795,13 +8833,19 @@ const releaseToConnectLabel = "Release to Connect"
}) })
} }
const getRevisionHistory = (workflow_id) => { const getRevisionHistory = (workflow_id, revisionCount = 50, turn = 0, orgId = "") => {
fetch(`${globalUrl}/api/v1/workflows/${workflow_id}/revisions`, { let headers = {
method: "GET",
headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
Accept: "application/json", Accept: "application/json",
}, }
if (orgId !== "") {
headers["Org-Id"] = orgId
}
fetch(`${globalUrl}/api/v1/workflows/${workflow_id}/revisions?count=${revisionCount}`, {
method: "GET",
headers: headers,
credentials: "include", credentials: "include",
}) })
.then((response) => { .then((response) => {
@@ -8828,7 +8872,11 @@ const releaseToConnectLabel = "Release to Connect"
setSelectedVersion(responseJson[0]) setSelectedVersion(responseJson[0])
}) })
.catch((error) => { .catch((error) => {
console.log("Error getting workflow revisions: ", error) console.log("Error getting workflow revisions: ", error);
++turn;
if (turn < 2) {
getRevisionHistory(workflow_id, 5, turn, orgId);
}
}); });
} }
@@ -9132,7 +9180,8 @@ const releaseToConnectLabel = "Release to Connect"
trigger.parameters.push({ trigger.parameters.push({
name: data.name, name: data.name,
value: data.command, value: data.command,
});} });
}
if (data.type === "stop") trigger.status = "stopped"; if (data.type === "stop") trigger.status = "stopped";
else trigger.status = "running"; else trigger.status = "running";
@@ -13324,7 +13373,8 @@ const releaseToConnectLabel = "Release to Connect"
)} )}
</select> </select>
</div> </div>
)})} )
})}
</div> </div>
); );
}; };
@@ -13371,23 +13421,6 @@ const releaseToConnectLabel = "Release to Connect"
value: "", value: "",
}; };
/*
// API-key has been replaced by auth key for the execution.
// Parents can now automatically execute children without auth from a user, as long as the subflow in question is owned by the same org and the subflow is actually referencing it during checkin.
console.log("SETTINGS: ", userSettings);
if (
userSettings !== undefined &&
userSettings !== null &&
userSettings.apikey !== null &&
userSettings.apikey !== undefined &&
userSettings.apikey.length > 0
) {
workflow.triggers[selectedTriggerIndex].parameters[2] = {
name: "user_apikey",
value: userSettings.apikey,
};
}
*/
} }
var handleSubflowStartnodeSelection = (e) => { var handleSubflowStartnodeSelection = (e) => {
@@ -13419,6 +13452,7 @@ const releaseToConnectLabel = "Release to Connect"
const foundIndex = workflow.visual_branches.findIndex( const foundIndex = workflow.visual_branches.findIndex(
(branch) => branch.source_id === newbranch.source_id (branch) => branch.source_id === newbranch.source_id
); );
if (foundIndex !== -1) { if (foundIndex !== -1) {
const currentEdge = cy.getElementById( const currentEdge = cy.getElementById(
workflow.visual_branches[foundIndex].id workflow.visual_branches[foundIndex].id
@@ -13515,6 +13549,9 @@ const releaseToConnectLabel = "Release to Connect"
}, },
{ {
name: "Enrich", name: "Enrich",
},
{
name: "Ticket Creation",
} }
] ]
@@ -13543,7 +13580,7 @@ const releaseToConnectLabel = "Release to Connect"
<h3 style={{ marginBottom: "5px", flex: 3, }}> <h3 style={{ marginBottom: "5px", flex: 3, }}>
{selectedTrigger.app_name} {selectedTrigger.app_name}
</h3> </h3>
<Tooltip title="Choose the type of subflow to run. This is NOT required, but is used to help Shuffle's workflow generators better understand the workflow." placement="top"> <Tooltip placement="left">
<Select <Select
MenuProps={{ MenuProps={{
disableScrollLock: true, disableScrollLock: true,
@@ -13642,7 +13679,7 @@ const releaseToConnectLabel = "Release to Connect"
<div style={{ flex: 1, marginLeft: 5, }}> <div style={{ flex: 1, marginLeft: 5, }}>
<Tooltip <Tooltip
color="primary" color="primary"
title={"Delay before action runs (in seconds)"} title={"Delay before subflow runs (in seconds)"}
placement="top" placement="top"
> >
<span> <span>
@@ -13787,7 +13824,6 @@ const releaseToConnectLabel = "Release to Connect"
fullWidth fullWidth
style={{ style={{
backgroundColor: theme.palette.inputColor, backgroundColor: theme.palette.inputColor,
height: 50,
borderRadius: theme.palette?.borderRadius, borderRadius: theme.palette?.borderRadius,
}} }}
onChange={(event, newValue) => { onChange={(event, newValue) => {
@@ -13920,7 +13956,6 @@ const releaseToConnectLabel = "Release to Connect"
fullWidth fullWidth
style={{ style={{
backgroundColor: theme.palette.inputColor, backgroundColor: theme.palette.inputColor,
height: 50,
borderRadius: theme.palette?.borderRadius, borderRadius: theme.palette?.borderRadius,
}} }}
onChange={(event, newValue) => { onChange={(event, newValue) => {
@@ -14791,12 +14826,10 @@ const releaseToConnectLabel = "Release to Connect"
fullWidth fullWidth
style={{ style={{
backgroundColor: theme.palette.inputColor, backgroundColor: theme.palette.inputColor,
height: 50,
borderRadius: theme.palette?.borderRadius, borderRadius: theme.palette?.borderRadius,
}} }}
onChange={(event, newValue) => { onChange={(event, newValue) => {
// Workaround with event lol // Workaround with event lol
console.log("CHANGE: ", event, newValue)
if (newValue !== undefined && newValue !== null) { if (newValue !== undefined && newValue !== null) {
var parsedvalue = JSON.parse(JSON.stringify(newValue)) var parsedvalue = JSON.parse(JSON.stringify(newValue))
parsedvalue.actions = [] parsedvalue.actions = []
@@ -14864,7 +14897,7 @@ const releaseToConnectLabel = "Release to Connect"
variant="body1" variant="body1"
style={theme.palette.textFieldStyle} style={theme.palette.textFieldStyle}
{...params} {...params}
label="Find Associated App (optional)" label="Associated App (optional)"
variant="outlined" variant="outlined"
/> />
); );
@@ -14873,6 +14906,7 @@ const releaseToConnectLabel = "Release to Connect"
</div> </div>
: null} : null}
{selectedTrigger.status === "running" ? null : {selectedTrigger.status === "running" ? null :
<div style={{ marginTop: 20 }}> <div style={{ marginTop: 20 }}>
<Typography>Environment</Typography> <Typography>Environment</Typography>
@@ -15686,7 +15720,8 @@ const releaseToConnectLabel = "Release to Connect"
handleWorkflowSelectionUpdate({ handleWorkflowSelectionUpdate({
target: { target: {
value: data, value: data,
}}, }
},
true) true)
document.activeElement.blur(); document.activeElement.blur();
}} }}
@@ -15876,7 +15911,8 @@ const releaseToConnectLabel = "Release to Connect"
if (selectedTrigger.trigger_type === "PIPELINE" && selectedTrigger.environment === "onprem" && defaultEnvironment !== undefined) { if (selectedTrigger.trigger_type === "PIPELINE" && selectedTrigger.environment === "onprem" && defaultEnvironment !== undefined) {
selectedTrigger.environment = defaultEnvironment.Name selectedTrigger.environment = defaultEnvironment.Name
setSelectedTrigger(selectedTrigger) } setSelectedTrigger(selectedTrigger)
}
const PipelineSidebar = !selectedTrigger || Object.getOwnPropertyNames(selectedTrigger)?.length === 0 || !workflow?.triggers || !workflow?.triggers[selectedTriggerIndex] || !selectedTrigger.trigger_type || selectedTrigger.trigger_type !== "SCHEDULE" ? null : const PipelineSidebar = !selectedTrigger || Object.getOwnPropertyNames(selectedTrigger)?.length === 0 || !workflow?.triggers || !workflow?.triggers[selectedTriggerIndex] || !selectedTrigger.trigger_type || selectedTrigger.trigger_type !== "SCHEDULE" ? null :
<div style={appApiViewStyle}> <div style={appApiViewStyle}>
@@ -17388,6 +17424,10 @@ const releaseToConnectLabel = "Release to Connect"
return null return null
} }
if (workflow.public === true) {
return null
}
return ( return (
<div <div
style={{ style={{
@@ -17427,14 +17467,17 @@ const releaseToConnectLabel = "Release to Connect"
// Used for handling suborg workflow distribution management // Used for handling suborg workflow distribution management
const updateCurrentWorkflow = (inputworkflow) => { const updateCurrentWorkflow = (inputworkflow) => {
setCurrentWorkflow(inputworkflow)
//setLastSaved(false) //setLastSaved(false)
setSelectedAction({}); setSelectedAction({});
setSelectedApp({}) setSelectedApp({})
setWorkflow(inputworkflow) setWorkflow(inputworkflow)
if (inputworkflow !== undefined && inputworkflow !== null && inputworkflow.id !== undefined && inputworkflow.id !== null) { if (inputworkflow !== undefined && inputworkflow !== null && inputworkflow.id !== undefined && inputworkflow.id !== null) {
getRevisionHistory(inputworkflow.id) getRevisionHistory(inputworkflow.id, 50, 0, inputworkflow.org_id)
getWorkflowExecution(inputworkflow.id) getWorkflowExecution(inputworkflow.id, "", "")
} }
// Update props match key // Update props match key
@@ -18228,11 +18271,10 @@ const releaseToConnectLabel = "Release to Connect"
setUserediting(true) setUserediting(true)
setWorkflow(workflow) setWorkflow(workflow)
getAppAuthentication(); getAppAuthentication()
getEnvironments(workflow.org_id) getEnvironments(workflow.org_id)
getWorkflowExecution(props.match.params.key, ""); getWorkflowExecution(props.match.params.key, "")
getAvailableWorkflows(-1); getAvailableWorkflows(-1)
getSettings();
getFiles() getFiles()
// For loading datastore // For loading datastore
@@ -18990,7 +19032,8 @@ const releaseToConnectLabel = "Release to Connect"
variant="outlined" variant="outlined"
fullWidth fullWidth
onClick={() => { onClick={() => {
getWorkflowExecution(props.match.params.key, "", executionFilter)
getWorkflowExecution(props.match.params.key, "", executionFilter);
}} }}
color="secondary" color="secondary"
> >
@@ -19232,6 +19275,7 @@ const releaseToConnectLabel = "Release to Connect"
marginLeft: 10, marginLeft: 10,
marginTop: "auto", marginTop: "auto",
marginBottom: "auto", marginBottom: "auto",
color: "rgba(255,255,255,0.5)",
}} }}
> >
{successActions} <span style={{ color: "rgba(255,255,255,0.4)" }}>+</span> {skippedActions > 0 ? skippedActions : <span style={{ color: "rgba(255,255,255,0.4)" }}>{skippedActions}</span>} <span style={{ color: "rgba(255,255,255,0.4)" }}>=</span> {calculatedResult} {successActions} <span style={{ color: "rgba(255,255,255,0.4)" }}>+</span> {skippedActions > 0 ? skippedActions : <span style={{ color: "rgba(255,255,255,0.4)" }}>{skippedActions}</span>} <span style={{ color: "rgba(255,255,255,0.4)" }}>=</span> {calculatedResult}
@@ -19315,6 +19359,7 @@ const releaseToConnectLabel = "Release to Connect"
onClick={() => { onClick={() => {
setExecutionRunning(false); setExecutionRunning(false);
stop(); stop();
// getWorkflowExecution(currentWorkflow.id, "");
getWorkflowExecution(props.match.params.key, ""); getWorkflowExecution(props.match.params.key, "");
setExecutionModalView(0); setExecutionModalView(0);
setLastExecution(executionData.execution_id); setLastExecution(executionData.execution_id);
@@ -19536,31 +19581,6 @@ const releaseToConnectLabel = "Release to Connect"
</div> </div>
{executionData.workflow !== undefined && executionData.workflow !== null && executionData.workflow.actions !== undefined && executionData.workflow.actions !== null && executionData.workflow.actions.length > 0 ?
<div style={{ display: "flex", marginLeft: 10, }}>
<Typography variant="body1">
{/*envStatus === "success" ?
<Tooltip title="Environment is healthy" placement="top">
<CheckCircleIcon style={{ color: "green" }} />
</Tooltip>
: envStatus === "failure" ?
<Tooltip title="Environment is unhealthy" placement="top">
<ErrorIcon style={{ color: "red" }} />
</Tooltip>
: null*/}
<b style={{ }}>Env &nbsp;&nbsp;&nbsp;&nbsp;</b>
</Typography>
<Typography variant="body1" color="textSecondary" style={{color: "#FF8544", cursor: "pointer", }} onClick={() => {
window.open("/admin?tab=locations", "_blank")
}}>
{executionData.workflow.actions[0].environment}
</Typography>
</div>
: null}
{executionData.status !== undefined && {executionData.status !== undefined &&
executionData.status.length > 0 ? ( executionData.status.length > 0 ? (
<div style={{ display: "flex", marginLeft: 10, }}> <div style={{ display: "flex", marginLeft: 10, }}>
@@ -19661,6 +19681,32 @@ const releaseToConnectLabel = "Release to Connect"
) : null} ) : null}
{executionData.workflow !== undefined && executionData.workflow !== null && executionData.workflow.actions !== undefined && executionData.workflow.actions !== null && executionData.workflow.actions.length > 0 ?
<div style={{ display: "flex", marginLeft: 10, }}>
<Typography variant="body1">
{/*envStatus === "success" ?
<Tooltip title="Environment is healthy" placement="top">
<CheckCircleIcon style={{ color: "green" }} />
</Tooltip>
: envStatus === "failure" ?
<Tooltip title="Environment is unhealthy" placement="top">
<ErrorIcon style={{ color: "red" }} />
</Tooltip>
: null*/}
<b style={{}}>Location &nbsp;</b>
</Typography>
<Typography variant="body1" color="textSecondary" style={{ color: "#FF8544", cursor: "pointer", }} onClick={() => {
window.open("/admin?tab=locations", "_blank")
}}>
{executionData.workflow.actions[0].environment}
</Typography>
</div>
: null}
{userdata.support === true && executionData.workflow !== undefined && executionData.workflow !== null && executionData.status !== "EXECUTING" ? {userdata.support === true && executionData.workflow !== undefined && executionData.workflow !== null && executionData.status !== "EXECUTING" ?
<div style={{ marginTop: 5, marginBottom: 5, }}> <div style={{ marginTop: 5, marginBottom: 5, }}>
<WorkflowValidationTimeline <WorkflowValidationTimeline
@@ -22291,7 +22337,8 @@ const releaseToConnectLabel = "Release to Connect"
return ( return (
<Paper <Paper
style={{padding: "15px 15px 15px 25px", minHeight: 105, maxHeight: 105, cursor: "pointer", backgroundColor: newrevision.edited === selectedVersion.edited ? "rgba(255,255,255,0.3)" : theme.palette.surfaceColor, border: showBorder === true ? `1px solid ${green}` : "1px solid rgba(255,255,255,0.3)", marginBottom: 10, style={{
padding: "15px 15px 15px 25px", minHeight: 105, maxHeight: 105, cursor: "pointer", backgroundColor: newrevision.edited === selectedVersion.edited ? "rgba(255,255,255,0.3)" : theme.palette.surfaceColor, border: showBorder === true ? `1px solid ${green}` : "1px solid rgba(255,255,255,0.3)", marginBottom: 10,
}} onClick={(e) => { }} onClick={(e) => {
setRightSideBarOpen(false) setRightSideBarOpen(false)
if (newrevision.edited === selectedVersion.edited) { if (newrevision.edited === selectedVersion.edited) {
+4 -2
View File
@@ -5435,6 +5435,7 @@ const AppCreator = (defaultprops) => {
minWidth: 174, minWidth: 174,
minHeight: 174, minHeight: 174,
objectFit: "contain", objectFit: "contain",
borderRadius: theme.palette?.borderRadius,
}} }}
/> />
); );
@@ -5450,6 +5451,7 @@ const AppCreator = (defaultprops) => {
margin: "auto", margin: "auto",
marginTop: 30, marginTop: 30,
marginLeft: 40, marginLeft: 40,
borderRadius: theme.palette?.borderRadius,
}} }}
onClick={() => { onClick={() => {
upload.click(); upload.click();
@@ -5832,6 +5834,7 @@ const AppCreator = (defaultprops) => {
//setOpenApiModal(true) //setOpenApiModal(true)
toast.info("Action merging & fork management coming soon") toast.info("Action merging & fork management coming soon")
}} }}
disabled={true}
style={{marginLeft: 10, }} style={{marginLeft: 10, }}
> >
<CallMergeIcon <CallMergeIcon
@@ -5846,7 +5849,7 @@ const AppCreator = (defaultprops) => {
href="https://shuffler.io/docs/app_creation#app-creator-instructions" href="https://shuffler.io/docs/app_creation#app-creator-instructions"
style={{ textDecoration: "none", color: "#f85a3e" }} style={{ textDecoration: "none", color: "#f85a3e" }}
> >
Click here to learn more about app creation Click to learn more about app creation
</a> </a>
<div <div
style={{ style={{
@@ -5940,7 +5943,6 @@ const AppCreator = (defaultprops) => {
<TextField <TextField
required required
style={{ style={{
paddingTop: 5,
marginTop: 5, marginTop: 5,
marginRight: 15, marginRight: 15,
backgroundColor: inputColor, backgroundColor: inputColor,
-1
View File
@@ -1258,7 +1258,6 @@ const Apps = (props) => {
style={{ style={{
width: 150, width: 150,
backgroundColor: theme.palette.surfaceColor, backgroundColor: theme.palette.surfaceColor,
backgroundColor: inputColor,
color: "white", color: "white",
height: 35, height: 35,
marginleft: 10, marginleft: 10,
+1
View File
@@ -1853,6 +1853,7 @@ const Apps2 = (props) => {
app={selectedApp} app={selectedApp}
userdata={userdata} userdata={userdata}
globalUrl={globalUrl} globalUrl={globalUrl}
getApps={getApps}
/> />
<AppCreationModal <AppCreationModal
open={createAppModalOpen} open={createAppModalOpen}
+1 -1
View File
@@ -1642,7 +1642,7 @@ const RunWorkflow = (defaultprops) => {
maxHeight: 500, maxHeight: 500,
position: "absolute", position: "absolute",
left: 150, left: 150,
top: 0, top: 75,
border: "1px solid rgba(255,255,255,0.3)", border: "1px solid rgba(255,255,255,0.3)",
borderRadius: theme.palette?.borderRadius, borderRadius: theme.palette?.borderRadius,
+3 -2
View File
@@ -621,7 +621,7 @@ const UsecaseListComponent = (props) => {
parsedUsecase.dstapp = newsubcase.dstapp parsedUsecase.dstapp = newsubcase.dstapp
var workflowBuilt = false var workflowBuilt = ""
const newname = subcase.name.toLowerCase().replaceAll(" ", "_") const newname = subcase.name.toLowerCase().replaceAll(" ", "_")
for (var workflowkey in workflows) { for (var workflowkey in workflows) {
const workflow = workflows[workflowkey] const workflow = workflows[workflowkey]
@@ -635,7 +635,7 @@ const UsecaseListComponent = (props) => {
//console.log("WORKFLOW: ", newname, newusecases) //console.log("WORKFLOW: ", newname, newusecases)
if (newusecases.includes(newname)) { if (newusecases.includes(newname)) {
workflowBuilt = true workflowBuilt = workflow.id
break break
} }
} }
@@ -676,6 +676,7 @@ const UsecaseListComponent = (props) => {
showTryit={false} showTryit={false}
shownColor={""} shownColor={""}
workflowBuilt={workflowBuilt} workflowBuilt={workflowBuilt}
inputWorkflowId={workflowBuilt}
usecaseDetails={usecaseDetails} usecaseDetails={usecaseDetails}
/> />
+32 -8
View File
@@ -92,6 +92,7 @@ import {
ArrowRight as ArrowRightIcon, ArrowRight as ArrowRightIcon,
Visibility as VisibilityIcon, Visibility as VisibilityIcon,
EditNote as EditNoteIcon, EditNote as EditNoteIcon,
ErrorOutline as ErrorOutlineIcon,
} from "@mui/icons-material"; } from "@mui/icons-material";
// Additional Components // Additional Components
@@ -108,6 +109,8 @@ import { InstantSearch, Configure, connectHits, connectSearchBox, connectRefinem
import { debounce } from "lodash"; import { debounce } from "lodash";
import { removeQuery } from "../components/ScrollToTop.jsx"; import { removeQuery } from "../components/ScrollToTop.jsx";
import {green, yellow, red, grey } from "../views/AngularWorkflow.jsx"
const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240"); const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240");
@@ -1114,12 +1117,9 @@ const Workflows2 = (props) => {
<Button <Button
style={{}} style={{}}
onClick={() => { onClick={() => {
console.log("Editing: ", editingWorkflow);
if (selectedWorkflowId) { if (selectedWorkflowId) {
deleteWorkflow(selectedWorkflowId) deleteWorkflow(selectedWorkflowId)
setTimeout(() => {
getAvailableWorkflows();
}, 1000);
} else if (selectedWorkflowIndexes.length > 0) { } else if (selectedWorkflowIndexes.length > 0) {
// Do backwards so it doesn't change // Do backwards so it doesn't change
toast("Starting deletion of workflows. This might take a while.") toast("Starting deletion of workflows. This might take a while.")
@@ -1132,11 +1132,11 @@ const Workflows2 = (props) => {
setTimeout(() => { setTimeout(() => {
getAvailableWorkflows() getAvailableWorkflows()
}, 1000); }, 5000)
setSelectedWorkflowIndexes([]); setSelectedWorkflowIndexes([]);
} }
setDeleteModalOpen(false);
setDeleteModalOpen(false)
}} }}
color="primary" color="primary"
> >
@@ -2807,6 +2807,7 @@ const Workflows2 = (props) => {
{workflowMenuButtons} {workflowMenuButtons}
</div> </div>
) : null} ) : null}
{(data.sharing !== undefined && data.sharing !== null && data.sharing === "form") || (data?.form_control?.input_markdown !== undefined && data?.form_control?.input_markdown !== null && data?.form_control?.input_markdown !== "") && type !== "public" ? {(data.sharing !== undefined && data.sharing !== null && data.sharing === "form") || (data?.form_control?.input_markdown !== undefined && data?.form_control?.input_markdown !== null && data?.form_control?.input_markdown !== "") && type !== "public" ?
<Tooltip title="Edit Form" placement="top"> <Tooltip title="Edit Form" placement="top">
<div style={{ position: "absolute", top: 45, right: 8, }}> <div style={{ position: "absolute", top: 45, right: 8, }}>
@@ -2821,10 +2822,33 @@ const Workflows2 = (props) => {
> >
<EditNoteIcon /> <EditNoteIcon />
</IconButton> </IconButton>
{workflowMenuButtons}
</div> </div>
</Tooltip> </Tooltip>
: null} : null}
{(data?.validation?.validation_ran === true && data?.validation?.valid === false && data?.validation?.errors?.length > 0 ) ?
<Tooltip title={`Explore more than ${data?.validation?.errors?.length} errors. When the last execution finishes without errors AND notifications stop occuring, this icon disappears.`} placement="top">
<div style={{ position: "absolute", top: 45, right: 8, }}>
<IconButton
aria-label="more"
aria-controls="long-menu"
aria-haspopup="true"
onClick={() => {
window.open(`/admin?admin_tab=notifications&workflow_id=${data.id}`, "_blank")
}}
style={{
padding: "0px",
color: "#979797",
}}
>
<ErrorOutlineIcon style={{
marginRight: 2,
}} />
</IconButton>
</div>
</Tooltip>
: null}
</Grid> </Grid>
</Paper> </Paper>
</div> </div>