Downgraded baseimage to 0.8.64
This commit is contained in:
@@ -18,7 +18,7 @@ require (
|
||||
github.com/docker/docker v1.13.1
|
||||
github.com/docker/go-connections v0.4.0
|
||||
github.com/docker/go-units v0.4.0 // indirect
|
||||
github.com/frikky/shuffle-shared v0.0.20
|
||||
github.com/frikky/shuffle-shared v0.0.23
|
||||
github.com/ghodss/yaml v1.0.0
|
||||
github.com/go-git/go-billy/v5 v5.0.0
|
||||
github.com/go-git/go-git/v5 v5.0.0
|
||||
|
||||
@@ -90,6 +90,12 @@ github.com/frikky/shuffle-shared v0.0.12 h1:+0EIfThmK47Po+LogPYZR4XjbS4Ds19WNMFu
|
||||
github.com/frikky/shuffle-shared v0.0.12/go.mod h1:SEY432/xs4oBkOUnGwxiYKCZWZLRaheGInhAoW7N8ww=
|
||||
github.com/frikky/shuffle-shared v0.0.15 h1:508ceeEHfPBMCC8/K4Zve3kwRQqiXNJSw6+BDoq9X4E=
|
||||
github.com/frikky/shuffle-shared v0.0.15/go.mod h1:SEY432/xs4oBkOUnGwxiYKCZWZLRaheGInhAoW7N8ww=
|
||||
github.com/frikky/shuffle-shared v0.0.20 h1:y6JlPnQDq//elICWvVfUfJyU9gH3fSpQmPy+agqZ5sA=
|
||||
github.com/frikky/shuffle-shared v0.0.20/go.mod h1:H7SqOta/EAYnfYuWzwzYSh/oWfF0kgnuaJTQNKQBvoQ=
|
||||
github.com/frikky/shuffle-shared v0.0.21 h1:xj/XPsXTa2rx41mm4nUc7+2K9RGkq2/mpjSPtIfpjE4=
|
||||
github.com/frikky/shuffle-shared v0.0.21/go.mod h1:H7SqOta/EAYnfYuWzwzYSh/oWfF0kgnuaJTQNKQBvoQ=
|
||||
github.com/frikky/shuffle-shared v0.0.22 h1:TFMcJCNmOOSneMMWbg5dNzp2z6m0aZLROuL+bzVToRE=
|
||||
github.com/frikky/shuffle-shared v0.0.22/go.mod h1:H7SqOta/EAYnfYuWzwzYSh/oWfF0kgnuaJTQNKQBvoQ=
|
||||
github.com/getkin/kin-openapi v0.8.0 h1:a6TQjTqwkyscC4/hShJX7WhCVE+4bi9lzw61XHQW5hE=
|
||||
github.com/getkin/kin-openapi v0.8.0/go.mod h1:zZQMFkVgRHCdhgb6ihCTIo9dyDZFvX0k/xAKqw1FhPw=
|
||||
github.com/getkin/kin-openapi v0.52.0 h1:6WqsF5d6PfJ8AscdD+9Rtb2RP2iBWyC7V6GcjssWg7M=
|
||||
|
||||
+5
-5
@@ -1,8 +1,8 @@
|
||||
version: '3'
|
||||
services:
|
||||
frontend:
|
||||
build: ./frontend
|
||||
image: ghcr.io/frikky/shuffle-frontend:0.8.70
|
||||
#build: ./frontend
|
||||
image: ghcr.io/frikky/shuffle-frontend:0.8.64
|
||||
container_name: shuffle-frontend
|
||||
hostname: shuffle-frontend
|
||||
ports:
|
||||
@@ -16,8 +16,8 @@ services:
|
||||
depends_on:
|
||||
- backend
|
||||
backend:
|
||||
build: ./backend
|
||||
image: ghcr.io/frikky/shuffle-backend:0.8.70
|
||||
#build: ./backend
|
||||
image: ghcr.io/frikky/shuffle-backend:0.8.64
|
||||
container_name: shuffle-backend
|
||||
hostname: ${BACKEND_HOSTNAME}
|
||||
# Here for debugging:
|
||||
@@ -47,7 +47,7 @@ services:
|
||||
- database
|
||||
orborus:
|
||||
#build: ./functions/onprem/orborus
|
||||
image: ghcr.io/frikky/shuffle-orborus:0.8.70
|
||||
image: ghcr.io/frikky/shuffle-orborus:0.8.64
|
||||
container_name: shuffle-orborus
|
||||
hostname: shuffle-orborus
|
||||
networks:
|
||||
|
||||
@@ -55,7 +55,6 @@ const data = [{
|
||||
css: {
|
||||
'width': '30px',
|
||||
'height': '30px',
|
||||
'font-size': '0px',
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -10,8 +10,7 @@ import { useBeforeunload } from 'react-beforeunload';
|
||||
import NestedMenuItem from "material-ui-nested-menu-item";
|
||||
|
||||
import {TextField, Drawer, Button, Paper, Grid, Tabs, InputAdornment, Tab, ButtonBase, Tooltip, Select, MenuItem, Divider, Dialog, Modal, DialogActions, DialogTitle, InputLabel, DialogContent, FormControl, IconButton, Menu, Input, FormGroup, FormControlLabel, Typography, Checkbox, Breadcrumbs, CircularProgress, Switch, Fade} from '@material-ui/core';
|
||||
|
||||
import {ArrowUpward as ArrowUpwardIcon, Visibility as VisibilityIcon, Done as DoneIcon, Close as CloseIcon, Error as ErrorIcon, FindReplace as FindreplaceIcon, ArrowLeft as ArrowLeftIcon, Cached as CachedIcon, DirectionsRun as DirectionsRunIcon, Add as AddIcon, Polymer as PolymerIcon, FormatListNumbered as FormatListNumberedIcon, Create as CreateIcon, PlayArrow as PlayArrowIcon, AspectRatio as AspectRatioIcon, MoreVert as MoreVertIcon, Apps as AppsIcon, Schedule as ScheduleIcon, FavoriteBorder as FavoriteBorderIcon, Pause as PauseIcon, Delete as DeleteIcon, AddCircleOutline as AddCircleOutlineIcon, Save as SaveIcon, KeyboardArrowLeft as KeyboardArrowLeftIcon, KeyboardArrowRight as KeyboardArrowRightIcon, ArrowBack as ArrowBackIcon, Settings as SettingsIcon, LockOpen as LockOpenIcon, ExpandMore as ExpandMoreIcon, VpnKey as VpnKeyIcon} from '@material-ui/icons';
|
||||
import {GetApp as GetAppIcon, Search as SearchIcon, ArrowUpward as ArrowUpwardIcon, Visibility as VisibilityIcon, Done as DoneIcon, Close as CloseIcon, Error as ErrorIcon, FindReplace as FindreplaceIcon, ArrowLeft as ArrowLeftIcon, Cached as CachedIcon, DirectionsRun as DirectionsRunIcon, Add as AddIcon, Polymer as PolymerIcon, FormatListNumbered as FormatListNumberedIcon, Create as CreateIcon, PlayArrow as PlayArrowIcon, AspectRatio as AspectRatioIcon, MoreVert as MoreVertIcon, Apps as AppsIcon, Schedule as ScheduleIcon, FavoriteBorder as FavoriteBorderIcon, Pause as PauseIcon, Delete as DeleteIcon, AddCircleOutline as AddCircleOutlineIcon, Save as SaveIcon, KeyboardArrowLeft as KeyboardArrowLeftIcon, KeyboardArrowRight as KeyboardArrowRightIcon, ArrowBack as ArrowBackIcon, Settings as SettingsIcon, LockOpen as LockOpenIcon, ExpandMore as ExpandMoreIcon, VpnKey as VpnKeyIcon} from '@material-ui/icons';
|
||||
|
||||
import * as cytoscape from 'cytoscape';
|
||||
import * as edgehandles from 'cytoscape-edgehandles';
|
||||
@@ -90,6 +89,8 @@ const AngularWorkflow = (props) => {
|
||||
const alert = useAlert()
|
||||
const borderRadius = 5
|
||||
const theme = useTheme();
|
||||
const green = "#86c142"
|
||||
const yellow = "#FECC00"
|
||||
|
||||
const [bodyWidth, bodyHeight] = useWindowSize();
|
||||
|
||||
@@ -97,7 +98,6 @@ const AngularWorkflow = (props) => {
|
||||
const [cystyle, ] = useState(cytoscapestyle)
|
||||
const [cy, setCy] = React.useState()
|
||||
|
||||
const [appSearch, setAppSearch] = React.useState("")
|
||||
const [currentView, setCurrentView] = React.useState(0)
|
||||
const [triggerAuthentication, setTriggerAuthentication] = React.useState({})
|
||||
const [triggerFolders, setTriggerFolders] = React.useState([])
|
||||
@@ -284,14 +284,14 @@ const AngularWorkflow = (props) => {
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
console.log("Status not 200 for WORKFLOW EXECUTION :O!")
|
||||
console.log("Status not 200 for APIKEY gen :O!")
|
||||
}
|
||||
|
||||
return response.json()
|
||||
})
|
||||
.then((responseJson) => {
|
||||
setUserSettings(responseJson)
|
||||
})
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error)
|
||||
});
|
||||
@@ -308,7 +308,7 @@ const AngularWorkflow = (props) => {
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
console.log("Status not 200 for WORKFLOW EXECUTION :O!")
|
||||
console.log("Status not 200 for get settings :O!")
|
||||
}
|
||||
|
||||
return response.json()
|
||||
@@ -477,7 +477,7 @@ const AngularWorkflow = (props) => {
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
console.log("Status not 200 for WORKFLOW EXECUTION :O!")
|
||||
console.log("Status not 200 for ABORT EXECUTION :O!")
|
||||
} else {
|
||||
alert.success("Execution aborted")
|
||||
}
|
||||
@@ -798,6 +798,21 @@ const AngularWorkflow = (props) => {
|
||||
workflow.errors = responseJson.errors
|
||||
if (responseJson.errors.length === 0) {
|
||||
workflow.isValid = true
|
||||
workflow.is_valid = true
|
||||
|
||||
//console.log("ELEMENTS: ", cy.elements())
|
||||
//const setupGraph = () => {
|
||||
const cyelements = cy.elements()
|
||||
for (var i = 0; i < cyelements.length; i++) {
|
||||
cyelements[i].removeStyle()
|
||||
cyelements[i].data().is_valid = true
|
||||
cyelements[i].data().errors = []
|
||||
}
|
||||
|
||||
for (var key in workflow.actions) {
|
||||
workflow.actions[key].is_valid = true
|
||||
workflow.actions[key].errors = []
|
||||
}
|
||||
}
|
||||
|
||||
for (var key in workflow.errors) {
|
||||
@@ -1049,7 +1064,7 @@ const AngularWorkflow = (props) => {
|
||||
setAuthLoaded(true)
|
||||
} else {
|
||||
setAuthLoaded(true)
|
||||
alert.error("Failed getting authentications")
|
||||
//alert.error("Failed getting authentications")
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
@@ -1119,6 +1134,7 @@ const AngularWorkflow = (props) => {
|
||||
if (responseJson.isValid === undefined) {
|
||||
responseJson.isValid = true
|
||||
}
|
||||
|
||||
if (responseJson.errors === undefined) {
|
||||
responseJson.errors = []
|
||||
}
|
||||
@@ -1219,10 +1235,21 @@ const AngularWorkflow = (props) => {
|
||||
}
|
||||
|
||||
// Comparing locations between nodes and setting views
|
||||
const onNodeDrag = (event, newAppAuth) => {
|
||||
const onNodeDrag = (event) => {
|
||||
//console.log("DRAGGING: ", event.target)
|
||||
//console.log("LEN2: ", event.target.edges.length)
|
||||
|
||||
const nodedata = event.target.data()
|
||||
if (nodedata.app_name == "Shuffle Tools" || nodedata.app_name == "Testing") {
|
||||
//console.log("NODE: ",
|
||||
//selector: `node[app_name="Shuffle Tools"]`,
|
||||
console.log(event.target)
|
||||
|
||||
// 1. Find location of node
|
||||
// 2. Check if it's within view of another node (inside)
|
||||
// 3. If it is, then hide text
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
event.target.animate({
|
||||
@@ -1258,7 +1285,7 @@ const AngularWorkflow = (props) => {
|
||||
|
||||
//const branch = workflow.branches.filter(branch => branch.source_id === data.id || branch.destination_id === data.id)
|
||||
console.log("NODE: ", data)
|
||||
console.log("APPAUTH: ", newAppAuth)
|
||||
//console.log("APPAUTH: ", newAppAuth)
|
||||
//console.log("BRANCHES: ", branch)
|
||||
|
||||
if (data.type === "ACTION") {
|
||||
@@ -1682,15 +1709,21 @@ const AngularWorkflow = (props) => {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// App length necessary cus of cy initialization
|
||||
//console.log("PRE ELEMENTS: !", workflow.actions, graphSetup, apps, authLoaded, cy)
|
||||
if (elements.length === 0 && workflow.actions !== undefined && !graphSetup && Object.getOwnPropertyNames(workflow).length > 0) {
|
||||
setGraphSetup(true)
|
||||
setupGraph()
|
||||
|
||||
//console.log("IN ELEMENT CHECK!")
|
||||
} else if (!established && cy !== undefined && apps !== null && apps !== undefined && apps.length > 0 && Object.getOwnPropertyNames(workflow).length > 0 && authLoaded){
|
||||
//This part has to load LAST, as it's kind of not async.
|
||||
//This means we need everything else to happen first.
|
||||
//
|
||||
//console.log("IN THIS PART AGAIN")
|
||||
|
||||
//console.log("IN ESTABLISHED!")
|
||||
|
||||
setEstablished(true)
|
||||
cy.edgehandles({
|
||||
@@ -1719,7 +1752,7 @@ const AngularWorkflow = (props) => {
|
||||
cy.on('mouseout', 'node', (e) => onNodeHoverOut(e))
|
||||
|
||||
// Handles dragging
|
||||
//cy.on('drag', 'node', (e) => onNodeDrag(e))
|
||||
cy.on('drag', 'node', (e) => onNodeDrag(e))
|
||||
|
||||
//cy.on('mouseover', 'node', () => $(targetElement).addClass('mouseover'));
|
||||
|
||||
@@ -1801,6 +1834,7 @@ const AngularWorkflow = (props) => {
|
||||
node.data.type = "ACTION"
|
||||
node.isStartNode = action["id"] === workflow.start
|
||||
|
||||
|
||||
var example = ""
|
||||
if (action.example !== undefined && action.example !== null && action.example.length > 0) {
|
||||
example = action.example
|
||||
@@ -1808,6 +1842,8 @@ const AngularWorkflow = (props) => {
|
||||
|
||||
node.data.example = example
|
||||
|
||||
//node.data.is_valid = false
|
||||
|
||||
return node;
|
||||
})
|
||||
|
||||
@@ -2094,7 +2130,7 @@ const AngularWorkflow = (props) => {
|
||||
<div key={variable.name} >
|
||||
<Paper square style={paperVariableStyle} onClick={() => {
|
||||
}}>
|
||||
<div style={{marginLeft: "10px", marginTop: "5px", marginBottom: "5px", width: "2px", backgroundColor: "orange", marginRight: "5px"}} />
|
||||
<div style={{marginLeft: "10px", marginTop: "5px", marginBottom: "5px", width: "2px", backgroundColor: yellow, marginRight: "5px"}} />
|
||||
<div style={{display: "flex", width: "100%"}}>
|
||||
<div style={{flex: "10", marginTop: "15px", marginLeft: "10px", overflow: "hidden"}} onClick={() => {
|
||||
setNewVariableName(variable.name)
|
||||
@@ -2161,11 +2197,12 @@ const AngularWorkflow = (props) => {
|
||||
<div>
|
||||
<Paper square style={paperVariableStyle} onClick={() => {
|
||||
}}>
|
||||
<div style={{marginLeft: "10px", marginTop: "5px", marginBottom: "5px", width: "2px", backgroundColor: "orange", marginRight: "5px"}} />
|
||||
<div style={{marginLeft: "10px", marginTop: "5px", marginBottom: "5px", width: "2px", backgroundColor: yellow, marginRight: "5px"}} />
|
||||
<div style={{display: "flex", width: "100%"}}>
|
||||
<div style={{flex: "10", marginTop: "15px", marginLeft: "10px", overflow: "hidden"}} onClick={() => {
|
||||
setNewVariableName(variable.name)
|
||||
setExecutionVariablesModalOpen(true)}}>
|
||||
setNewVariableName(variable.name)
|
||||
setExecutionVariablesModalOpen(true)
|
||||
}}>
|
||||
Name: {variable.name}
|
||||
</div>
|
||||
<div style={{flex: "1", marginLeft: "0px"}}>
|
||||
@@ -2228,7 +2265,7 @@ const AngularWorkflow = (props) => {
|
||||
|
||||
const HandleLeftView = () => {
|
||||
// Defaults to apps.
|
||||
var thisview = <AppView />
|
||||
var thisview = <AppView allApps={apps} prioritizedApps={prioritizedApps} filteredApps={filteredApps} />
|
||||
if (currentView === 1) {
|
||||
thisview = <TriggersView />
|
||||
} else if (currentView === 2) {
|
||||
@@ -2379,7 +2416,7 @@ const AngularWorkflow = (props) => {
|
||||
:
|
||||
<img alt="" src={trigger.large_image} style={{width: 80, height: 80, pointerEvents: "none", }} />
|
||||
|
||||
const color = trigger.is_valid ? "green" : "orange"
|
||||
const color = trigger.is_valid ? green : yellow
|
||||
return(
|
||||
<Draggable
|
||||
key={index}
|
||||
@@ -2724,108 +2761,114 @@ const AngularWorkflow = (props) => {
|
||||
|
||||
const appScrollStyle = {
|
||||
overflow: "scroll",
|
||||
maxHeight: bodyHeight-appBarSize-55,
|
||||
minHeight: bodyHeight-appBarSize-55,
|
||||
maxHeight: bodyHeight-appBarSize-55-50,
|
||||
minHeight: bodyHeight-appBarSize-55-50,
|
||||
marginTop: 1,
|
||||
overflowY: "auto",
|
||||
overflowX: "hidden",
|
||||
}
|
||||
|
||||
const runAppSearch = (event) => {
|
||||
setAppSearch(event.target.value)
|
||||
setFilteredApps(apps.filter(app => app.name.toLowerCase().includes(event.target.value.trim().toLowerCase())))
|
||||
}
|
||||
const AppView = (props) => {
|
||||
const { allApps, prioritizedApps, filteredApps } = props;
|
||||
const [visibleApps, setVisibleApps] = React.useState(prioritizedApps.concat(filteredApps.filter(innerapp => !internalIds.includes(innerapp.id))))
|
||||
|
||||
const ParsedAppPaper = (props) => {
|
||||
const app = props.app
|
||||
const [hover, setHover] = React.useState(false)
|
||||
const ParsedAppPaper = (props) => {
|
||||
const app = props.app
|
||||
const [hover, setHover] = React.useState(false)
|
||||
|
||||
// FIXME - add label to apps, as this might be slow with A LOT of apps
|
||||
const maxlen = 24
|
||||
var newAppname = app.name.replace("_", " ", -1)
|
||||
newAppname = newAppname.charAt(0).toUpperCase()+newAppname.substring(1)
|
||||
if (newAppname.length > maxlen) {
|
||||
newAppname = newAppname.slice(0, maxlen)+".."
|
||||
}
|
||||
const maxlen = 24
|
||||
var newAppname = app.name.replace("_", " ", -1)
|
||||
newAppname = newAppname.charAt(0).toUpperCase()+newAppname.substring(1)
|
||||
if (newAppname.length > maxlen) {
|
||||
newAppname = newAppname.slice(0, maxlen)+".."
|
||||
}
|
||||
|
||||
//const image = "url("+app.large_image+")"
|
||||
const image = app.large_image
|
||||
const newAppStyle = JSON.parse(JSON.stringify(paperAppStyle))
|
||||
const pixelSize = !hover ? "2px" : "4px"
|
||||
newAppStyle.borderLeft = app.is_valid ? `${pixelSize} solid green` : `${pixelSize} solid orange`
|
||||
//const image = "url("+app.large_image+")"
|
||||
const image = app.large_image
|
||||
const newAppStyle = JSON.parse(JSON.stringify(paperAppStyle))
|
||||
const pixelSize = !hover ? "2px" : "4px"
|
||||
newAppStyle.borderLeft = app.is_valid ? `${pixelSize} solid ${green}` : `${pixelSize} solid ${yellow}`
|
||||
|
||||
return (
|
||||
<Draggable
|
||||
onDrag={(e) => {handleAppDrag(e, app)}}
|
||||
onStop={(e) => {handleDragStop(e, app)}}
|
||||
key={app.id}
|
||||
dragging={false}
|
||||
position={{
|
||||
x: 0,
|
||||
y: 0,
|
||||
}}
|
||||
>
|
||||
<Paper square style={newAppStyle} onMouseOver={() => {setHover(true)}} onMouseOut={() => {setHover(false)}}>
|
||||
<Grid container style={{margin: "10px 10px 10px 15px", flex: "10"}}>
|
||||
<Grid item>
|
||||
<img alt={newAppname} src={image} style={{pointerEvents: "none", userDrag: "none", userSelect: "none", borderRadius: borderRadius, height: 80, width: 80,}} />
|
||||
</Grid>
|
||||
<Grid style={{display: "flex", flexDirection: "column", marginLeft: "20px", minWidth: 185, maxWidth: 185, overflow: "hidden", maxHeight: 77, }}>
|
||||
<Grid item style={{flex: 1}}>
|
||||
<h4 style={{marginBottom: 0, marginTop: 5}}>{newAppname}</h4>
|
||||
return (
|
||||
<Draggable
|
||||
onDrag={(e) => {handleAppDrag(e, app)}}
|
||||
onStop={(e) => {handleDragStop(e, app)}}
|
||||
key={app.id}
|
||||
dragging={false}
|
||||
position={{
|
||||
x: 0,
|
||||
y: 0,
|
||||
}}
|
||||
>
|
||||
<Paper square style={newAppStyle} onMouseOver={() => {setHover(true)}} onMouseOut={() => {setHover(false)}}>
|
||||
<Grid container style={{margin: "10px 10px 10px 15px", flex: "10"}}>
|
||||
<Grid item>
|
||||
<img alt={newAppname} src={image} style={{pointerEvents: "none", userDrag: "none", userSelect: "none", borderRadius: borderRadius, height: 80, width: 80,}} />
|
||||
</Grid>
|
||||
<Grid item style={{flex: 1}}>
|
||||
Version: {app.app_version}
|
||||
</Grid>
|
||||
<Grid item style={{flex: 1, width: "100%", maxHeight: 27, overflow: "hidden",}}>
|
||||
{app.description}
|
||||
<Grid style={{display: "flex", flexDirection: "column", marginLeft: "20px", minWidth: 185, maxWidth: 185, overflow: "hidden", maxHeight: 77, }}>
|
||||
<Grid item style={{flex: 1}}>
|
||||
<h4 style={{marginBottom: 0, marginTop: 5}}>{newAppname}</h4>
|
||||
</Grid>
|
||||
<Grid item style={{flex: 1}}>
|
||||
Version: {app.app_version}
|
||||
</Grid>
|
||||
<Grid item style={{flex: 1, width: "100%", maxHeight: 27, overflow: "hidden",}}>
|
||||
{app.description}
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Paper>
|
||||
</Draggable>
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
const runSearch = (event) => {
|
||||
if (event.target.value.length > 0) {
|
||||
setVisibleApps(allApps.filter(app => app.name.toLowerCase().includes(event.target.value.trim().toLowerCase())))
|
||||
} else {
|
||||
setVisibleApps(prioritizedApps.concat(filteredApps.filter(innerapp => !internalIds.includes(innerapp.id))))
|
||||
}
|
||||
}
|
||||
|
||||
const AppView = () => {
|
||||
return(
|
||||
<div style={appViewStyle}>
|
||||
<div style={{flex: "1"}}>
|
||||
<TextField
|
||||
style={{backgroundColor: inputColor, borderRadius: borderRadius, marginTop: 5, marginRight: 10,}}
|
||||
InputProps={{
|
||||
style:{
|
||||
color: "white",
|
||||
minHeight: 50,
|
||||
marginLeft: "5px",
|
||||
maxWidth: "95%",
|
||||
fontSize: "1em",
|
||||
},
|
||||
endAdornment: (
|
||||
<InputAdornment position="end">
|
||||
<Tooltip title="Run search" placement="top">
|
||||
<SearchIcon style={{cursor: "pointer"}} />
|
||||
</Tooltip>
|
||||
</InputAdornment>
|
||||
)
|
||||
}}
|
||||
fullWidth
|
||||
color="primary"
|
||||
placeholder={"Search Apps"}
|
||||
id="appsearch"
|
||||
onBlur={(event) => {
|
||||
runSearch(event)
|
||||
}}
|
||||
/>
|
||||
<div style={appScrollStyle}>
|
||||
{/*
|
||||
<TextField
|
||||
style={{backgroundColor: inputColor, borderRadius: borderRadius,}}
|
||||
style={{backgroundColor: inputColor}}
|
||||
InputProps={{
|
||||
style:{
|
||||
color: "white",
|
||||
minHeight: 50,
|
||||
marginLeft: "5px",
|
||||
maxWidth: "95%",
|
||||
fontSize: "1em",
|
||||
},
|
||||
}}
|
||||
fullWidth
|
||||
color="primary"
|
||||
placeholder={"Search apps"}
|
||||
onChange={(event) => {
|
||||
runAppSearch(event)
|
||||
}}
|
||||
/>
|
||||
*/}
|
||||
{prioritizedApps.map((app, index) => {
|
||||
return(
|
||||
<ParsedAppPaper key={index} app={app} />
|
||||
)
|
||||
})}
|
||||
{filteredApps.filter(innerapp => !internalIds.includes(innerapp.id)).map((app, index) => {
|
||||
if (app.invalid) {
|
||||
return null
|
||||
}
|
||||
{visibleApps.map((app, index) => {
|
||||
if (app.invalid) {
|
||||
return null
|
||||
}
|
||||
|
||||
return(
|
||||
<ParsedAppPaper key={index} app={app} />
|
||||
)
|
||||
})}
|
||||
return(
|
||||
<ParsedAppPaper key={index} app={app} />
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -2979,10 +3022,10 @@ const AngularWorkflow = (props) => {
|
||||
const AppActionArguments = (props) => {
|
||||
const [selectedActionParameters, setSelectedActionParameters] = React.useState([])
|
||||
const [selectedVariableParameter, setSelectedVariableParameter] = React.useState("")
|
||||
const [showDropdown, setShowDropdown] = React.useState(false)
|
||||
const [showDropdownNumber, setShowDropdownNumber] = React.useState(0)
|
||||
const [actionlist, setActionlist] = React.useState([])
|
||||
const [jsonList, setJsonList] = React.useState([])
|
||||
const [showDropdown, setShowDropdown] = React.useState(false)
|
||||
const [showDropdownNumber, setShowDropdownNumber] = React.useState(0)
|
||||
const [showAutocomplete, setShowAutocomplete] = React.useState(false)
|
||||
const [menuPosition, setMenuPosition] = useState(null)
|
||||
|
||||
@@ -3865,6 +3908,7 @@ const AngularWorkflow = (props) => {
|
||||
}
|
||||
|
||||
tmpitem = tmpitem.charAt(0).toUpperCase()+tmpitem.substring(1)
|
||||
tmpitem = tmpitem.replaceAll("_", " ")
|
||||
const description = data.description === undefined ? "" : data.description
|
||||
|
||||
return (
|
||||
@@ -4121,7 +4165,7 @@ const AngularWorkflow = (props) => {
|
||||
<div style={appApiViewStyle}>
|
||||
<div style={{display: "flex", minHeight: 40, marginBottom: 30}}>
|
||||
<div style={{flex: 1}}>
|
||||
<h3 style={{marginBottom: 5}}>{selectedAction.app_name}</h3>
|
||||
<h3 style={{marginBottom: 5}}>{selectedAction.app_name.replaceAll("_", " ")}</h3>
|
||||
<div style={{display: "flex",}}>
|
||||
<IconButton style={{marginTop: "auto", marginBottom: "auto", height: 30, paddingLeft: 0, paddingRight: 0}} onClick={() => {
|
||||
console.log("FIND EXAMPLE RESULTS FOR ", selectedAction)
|
||||
@@ -4969,7 +5013,7 @@ const AngularWorkflow = (props) => {
|
||||
|
||||
return (
|
||||
<Paper key={condition.condition.id} square style={paperVariableStyle} onClick={() => {}}>
|
||||
<div style={{marginLeft: "10px", marginTop: "5px", marginBottom: "5px", width: "2px", backgroundColor: "orange", marginRight: "5px"}} />
|
||||
<div style={{marginLeft: "10px", marginTop: "5px", marginBottom: "5px", width: "2px", backgroundColor: yellow, marginRight: "5px"}} />
|
||||
<div style={{display: "flex", width: "100%"}}>
|
||||
<div style={{flex: "10", display: "flex"}} onClick={() => {
|
||||
setSourceValue(condition.source)
|
||||
@@ -5371,6 +5415,11 @@ const AngularWorkflow = (props) => {
|
||||
}
|
||||
|
||||
const SubflowSidebar = () => {
|
||||
const [menuPosition, setMenuPosition] = useState(null)
|
||||
const [showDropdown, setShowDropdown] = React.useState(false)
|
||||
const [showDropdownNumber, setShowDropdownNumber] = React.useState(0)
|
||||
const [showAutocomplete, setShowAutocomplete] = React.useState(false)
|
||||
|
||||
if (Object.getOwnPropertyNames(selectedTrigger).length > 0) {
|
||||
if (workflow.triggers[selectedTriggerIndex] === undefined) {
|
||||
return null
|
||||
@@ -5485,48 +5534,50 @@ const AngularWorkflow = (props) => {
|
||||
<span style={{marginTop: 5}}><a rel="norefferer" href={`/workflows/${workflow.triggers[selectedTriggerIndex].parameters[0].value}`} target="_blank" style={{textDecoration: "none", color: "#f85a3e", marginLeft: 5,}}>Explore selected workflow</a></span>
|
||||
}
|
||||
|
||||
<div style={{marginTop: "20px", marginBottom: "7px", display: "flex"}}>
|
||||
<div style={{width: "17px", height: "17px", borderRadius: 17 / 2, backgroundColor: "#f85a3e", marginRight: "10px"}}/>
|
||||
<div style={{flex: "10"}}>
|
||||
<b>Select the Startnode</b>
|
||||
</div>
|
||||
</div>
|
||||
{subworkflow === undefined || subworkflow === null || subworkflow.id === undefined || subworkflow.actions === null || subworkflow.actions === undefined || subworkflow.actions.length === 0 ? null :
|
||||
<Select
|
||||
value={subworkflowStartnode}
|
||||
SelectDisplayProps={{
|
||||
style: {
|
||||
marginLeft: 10,
|
||||
<span>
|
||||
<div style={{marginTop: "20px", marginBottom: "7px", display: "flex"}}>
|
||||
<div style={{width: "17px", height: "17px", borderRadius: 17 / 2, backgroundColor: "#f85a3e", marginRight: "10px"}}/>
|
||||
<div style={{flex: "10"}}>
|
||||
<b>Select the Startnode</b>
|
||||
</div>
|
||||
</div>
|
||||
<Select
|
||||
value={subworkflowStartnode}
|
||||
SelectDisplayProps={{
|
||||
style: {
|
||||
marginLeft: 10,
|
||||
|
||||
}
|
||||
}}
|
||||
fullWidth
|
||||
onChange={(e) => {
|
||||
setSubworkflowStartnode(e.target.value)
|
||||
|
||||
try {
|
||||
workflow.triggers[selectedTriggerIndex].parameters[3].value = e.target.value.id
|
||||
} catch {
|
||||
workflow.triggers[selectedTriggerIndex].parameters[3] = {
|
||||
"name": "startnode",
|
||||
"value": e.target.value.id,
|
||||
}
|
||||
}
|
||||
}}
|
||||
fullWidth
|
||||
onChange={(e) => {
|
||||
setSubworkflowStartnode(e.target.value)
|
||||
|
||||
setWorkflow(workflow)
|
||||
//setUpdate(Math.random())
|
||||
}}
|
||||
style={{backgroundColor: inputColor, color: "white", height: "50px"}}
|
||||
>
|
||||
{subworkflow.actions.map((action, index) => {
|
||||
//console.log(action)
|
||||
return (
|
||||
<MenuItem disabled={getParents(selectedTrigger).find(parent => parent.id === action.id)} key={index} style={{backgroundColor: inputColor, color: "white"}} value={action}>
|
||||
{action.label}
|
||||
</MenuItem>
|
||||
)
|
||||
})}
|
||||
</Select>
|
||||
try {
|
||||
workflow.triggers[selectedTriggerIndex].parameters[3].value = e.target.value.id
|
||||
} catch {
|
||||
workflow.triggers[selectedTriggerIndex].parameters[3] = {
|
||||
"name": "startnode",
|
||||
"value": e.target.value.id,
|
||||
}
|
||||
}
|
||||
|
||||
setWorkflow(workflow)
|
||||
//setUpdate(Math.random())
|
||||
}}
|
||||
style={{backgroundColor: inputColor, color: "white", height: "50px"}}
|
||||
>
|
||||
{subworkflow.actions.map((action, index) => {
|
||||
//console.log(action)
|
||||
return (
|
||||
<MenuItem disabled={getParents(selectedTrigger).find(parent => parent.id === action.id)} key={index} style={{backgroundColor: inputColor, color: "white"}} value={action}>
|
||||
{action.label}
|
||||
</MenuItem>
|
||||
)
|
||||
})}
|
||||
</Select>
|
||||
</span>
|
||||
}
|
||||
<div style={{marginTop: "20px", marginBottom: "7px", display: "flex"}}>
|
||||
<div style={{width: "17px", height: "17px", borderRadius: 17 / 2, backgroundColor: "#f85a3e", marginRight: "10px"}}/>
|
||||
@@ -5543,6 +5594,21 @@ const AngularWorkflow = (props) => {
|
||||
maxWidth: "95%",
|
||||
fontSize: "1em",
|
||||
},
|
||||
endAdornment: (
|
||||
<InputAdornment position="end">
|
||||
<Tooltip title="Autocomplete text" placement="top">
|
||||
<AddCircleOutlineIcon style={{cursor: "pointer"}} onClick={(event) => {
|
||||
setMenuPosition({
|
||||
top: event.pageY+10,
|
||||
left: event.pageX+10,
|
||||
})
|
||||
//setShowDropdownNumber(count)
|
||||
setShowDropdown(true)
|
||||
setShowAutocomplete(true)
|
||||
}}/>
|
||||
</Tooltip>
|
||||
</InputAdornment>
|
||||
)
|
||||
}}
|
||||
rows="6"
|
||||
multiline
|
||||
@@ -6321,7 +6387,7 @@ const AngularWorkflow = (props) => {
|
||||
return null
|
||||
}
|
||||
|
||||
const cytoscapeViewWidths = 750
|
||||
const cytoscapeViewWidths = 800
|
||||
const bottomBarStyle = {
|
||||
position: "fixed",
|
||||
right: 20,
|
||||
@@ -6551,7 +6617,7 @@ const AngularWorkflow = (props) => {
|
||||
<Tooltip color="primary" title="Save (ctrl+s)" placement="top">
|
||||
<span>
|
||||
<Button disabled={savingState !== 0} color="primary" style={{height: 50, width: 64, marginLeft: 10, }} variant={lastSaved ? "outlined" : "contained"} onClick={() => saveWorkflow()}>
|
||||
{savingState === 2 ? <CircularProgress style={{height: 35, width: 35}} /> : savingState === 1 ? <DoneIcon style={{color: "green"}} /> : <SaveIcon /> }
|
||||
{savingState === 2 ? <CircularProgress style={{height: 35, width: 35}} /> : savingState === 1 ? <DoneIcon style={{color: green}} /> : <SaveIcon /> }
|
||||
</Button>
|
||||
</span>
|
||||
</Tooltip>
|
||||
@@ -6562,7 +6628,7 @@ const AngularWorkflow = (props) => {
|
||||
</Button>
|
||||
</span>
|
||||
</Tooltip>
|
||||
<Tooltip color="secondary" title="Remove selected item (del)" placement="top-start">
|
||||
<Tooltip disabled={workflow.public} color="secondary" title="Remove selected item (del)" placement="top-start">
|
||||
<span>
|
||||
<Button color="primary" style={{height: 50, marginLeft: 10, }} variant="outlined" onClick={() => {
|
||||
removeNode()
|
||||
@@ -6581,6 +6647,28 @@ const AngularWorkflow = (props) => {
|
||||
</Button>
|
||||
</span>
|
||||
</Tooltip>
|
||||
{workflow.public ?
|
||||
<Tooltip color="secondary" title="Download workflow" placement="top-start">
|
||||
<span>
|
||||
<Button color="primary" style={{height: 50, marginLeft: 10, }} variant="contained" onClick={() => {
|
||||
//setExecutionModalOpen(true)
|
||||
//getWorkflowExecution(props.match.params.key, "")
|
||||
//data = sanitizeWorkflow(workflow)
|
||||
const data = workflow
|
||||
let exportFileDefaultName = data.name+'.json';
|
||||
|
||||
let dataStr = JSON.stringify(data)
|
||||
let dataUri = 'data:application/json;charset=utf-8,'+ encodeURIComponent(dataStr);
|
||||
let linkElement = document.createElement('a');
|
||||
linkElement.setAttribute('href', dataUri);
|
||||
linkElement.setAttribute('download', exportFileDefaultName);
|
||||
linkElement.click();
|
||||
}}>
|
||||
<GetAppIcon />
|
||||
</Button>
|
||||
</span>
|
||||
</Tooltip>
|
||||
: null}
|
||||
{/* <FileMenu /> */}
|
||||
{workflow.configuration !== null && workflow.configuration !== undefined && workflow.configuration.exit_on_error !== undefined ? <WorkflowMenu /> : null}
|
||||
</div>
|
||||
@@ -6830,7 +6918,7 @@ const AngularWorkflow = (props) => {
|
||||
{workflowExecutions.length > 0 ?
|
||||
<div>
|
||||
{workflowExecutions.map((data, index) => {
|
||||
const statusColor = data.status === "FINISHED" ? "green" : data.status === "ABORTED" || data.status === "FAILED" ? "red" : "orange"
|
||||
const statusColor = data.status === "FINISHED" ? green : data.status === "ABORTED" || data.status === "FAILED" ? "red" : yellow
|
||||
const timeElapsed = data.completed_at-data.started_at
|
||||
const resultsLength = data.results !== undefined && data.results !== null ? data.results.length : 0
|
||||
|
||||
@@ -7006,7 +7094,7 @@ const AngularWorkflow = (props) => {
|
||||
|
||||
const curapp = apps.find(a => a.name === data.action.app_name && a.app_version === data.action.app_version)
|
||||
const imgsize = 50
|
||||
const statusColor = data.status === "FINISHED" || data.status === "SUCCESS" ? "green" : data.status === "ABORTED" || data.status === "FAILURE" ? "red" : "orange"
|
||||
const statusColor = data.status === "FINISHED" || data.status === "SUCCESS" ? green : data.status === "ABORTED" || data.status === "FAILURE" ? "red" : yellow
|
||||
|
||||
var imgSrc = curapp === undefined ? "" : curapp.large_image
|
||||
if (imgSrc.length === 0 && workflow.actions !== undefined && workflow.actions !== null) {
|
||||
@@ -7116,7 +7204,7 @@ const AngularWorkflow = (props) => {
|
||||
// This sucks :)
|
||||
const curapp = !codeModalOpen ? {} : selectedResult.action.app_name === "shuffle-subflow" ? triggers[1] : selectedResult.action.app_name === "User Input" ? triggers[2] : apps.find(a => a.name === selectedResult.action.app_name && a.app_version === selectedResult.action.app_version)
|
||||
const imgsize = 50
|
||||
const statusColor = !codeModalOpen ? "red" : selectedResult.status === "FINISHED" || selectedResult.status === "SUCCESS" ? "green" : selectedResult.status === "ABORTED" || selectedResult.status === "FAILURE" ? "red" : "orange"
|
||||
const statusColor = !codeModalOpen ? "red" : selectedResult.status === "FINISHED" || selectedResult.status === "SUCCESS" ? green : selectedResult.status === "ABORTED" || selectedResult.status === "FAILURE" ? "red" : yellow
|
||||
const validate = !codeModalOpen ? "" : validateJson(selectedResult.result.trim())
|
||||
if (validate.valid && typeof(validate.result) === "string") {
|
||||
validate.result = JSON.parse(validate.result)
|
||||
@@ -7287,7 +7375,7 @@ const AngularWorkflow = (props) => {
|
||||
</Dialog>
|
||||
</Draggable>
|
||||
|
||||
const newView = isLoggedIn ?
|
||||
const newView = //isLoggedIn ?
|
||||
<div style={{color: "white"}}>
|
||||
<div style={{display: "flex", borderTop: "1px solid rgba(91, 96, 100, 1)"}}>
|
||||
{leftView}
|
||||
@@ -7313,10 +7401,13 @@ const AngularWorkflow = (props) => {
|
||||
<BottomCytoscapeBar />
|
||||
<TopCytoscapeBar />
|
||||
</div>
|
||||
|
||||
/*
|
||||
:
|
||||
<div style={{color: "white"}}>
|
||||
TMP FOR NOT LOGGED IN
|
||||
</div>
|
||||
*/
|
||||
|
||||
const executionVariableModal = executionVariablesModalOpen ?
|
||||
<Dialog modal
|
||||
@@ -7756,7 +7847,8 @@ const AngularWorkflow = (props) => {
|
||||
<AuthenticationData app={selectedApp} />
|
||||
</Dialog> : null
|
||||
|
||||
const loadedCheck = isLoaded && isLoggedIn && workflowDone ?
|
||||
//const loadedCheck = isLoaded && isLoggedIn && workflowDone ?
|
||||
const loadedCheck = isLoaded && workflowDone ?
|
||||
<div>
|
||||
{newView}
|
||||
{variablesModal}
|
||||
|
||||
@@ -1340,7 +1340,9 @@ const AppCreator = (props) => {
|
||||
placeholder={'Query name'}
|
||||
helperText={<span style={{color:"white", marginBottom: "2px",}}>Click required switch</span>}
|
||||
onBlur={(e) => {
|
||||
urlPathQueries[index].name = e.target.value
|
||||
console.log("IN BLUR: ", e.target.value)
|
||||
urlPathQueries[index].name = e.target.value.replaceAll("=", "")
|
||||
|
||||
setUrlPathQueries(urlPathQueries)
|
||||
}}
|
||||
InputProps={{
|
||||
|
||||
@@ -68,17 +68,21 @@ const flexContainerStyle = {
|
||||
}
|
||||
|
||||
const flexBoxStyle = {
|
||||
width: 333,
|
||||
height: 125,
|
||||
borderRadius: 4,
|
||||
boxSizing: "border-box",
|
||||
letterSpacing: "0.4px",
|
||||
color: "#D6791E",
|
||||
margin: 10,
|
||||
flex: 1,
|
||||
}
|
||||
|
||||
const activeWorkflowStyle = {backgroundColor: "#FFF5EE"}
|
||||
const availableWorkflowStyle = {backgroundColor: "#F0F3FF"}
|
||||
const notificationStyle = {backgroundColor: "#E5F9FF"}
|
||||
//const activeWorkflowStyle = {backgroundColor: "#FFF5EE"}
|
||||
//const notificationStyle = {backgroundColor: "#E5F9FF"}
|
||||
//const activeWorkflowStyle = {backgroundColor: "#3d3f43"}
|
||||
const availableWorkflowStyle = {backgroundColor: "#3d3f43"}
|
||||
const notificationStyle = {backgroundColor: "#3d3f43"}
|
||||
const activeWorkflowStyle = {backgroundColor: "#3d3f43"}
|
||||
|
||||
const flexContentStyle = {
|
||||
display: "flex",
|
||||
@@ -302,8 +306,6 @@ const MyView = (props) => {
|
||||
alignContent: "space-between",
|
||||
}
|
||||
|
||||
|
||||
|
||||
const paperAppStyle = {
|
||||
minHeight: 130,
|
||||
width: "100%",
|
||||
@@ -573,13 +575,16 @@ const MyView = (props) => {
|
||||
boxColor = "#86c142"
|
||||
}
|
||||
|
||||
if (!data.previously_saved) {
|
||||
boxColor = "#f85a3e"
|
||||
}
|
||||
|
||||
const menuClick = (event) => {
|
||||
setOpen(!open)
|
||||
setAnchorEl(event.currentTarget);
|
||||
}
|
||||
|
||||
var parsedName = data.name
|
||||
console.log("LEN: ", parsedName.length)
|
||||
if (parsedName !== undefined && parsedName !== null && parsedName.length > 25) {
|
||||
parsedName = parsedName.slice(0,25)+".."
|
||||
}
|
||||
@@ -589,7 +594,7 @@ const MyView = (props) => {
|
||||
const [triggers, schedules, webhooks, subflows] = getWorkflowMeta(data)
|
||||
|
||||
return (
|
||||
<Grid item xs={4} spacing={4} style={{padding: 10,}}>
|
||||
<Grid item xs={4} style={{padding: "12px 10px 12px 10px",}}>
|
||||
<Paper square style={paperAppStyle} >
|
||||
<div style={{position: "absolute", bottom: 1, left: 1, height: 12, width: 12, backgroundColor: boxColor, borderRadius: "0 100px 0 0",}} />
|
||||
<Grid item style={{display: "flex", flexDirection: "column", width: "100%"}}>
|
||||
@@ -608,7 +613,7 @@ const MyView = (props) => {
|
||||
</span>
|
||||
</Tooltip>
|
||||
<Tooltip color="primary" title="Trigger amount" placement="bottom">
|
||||
<span style={{marginLeft: 10, color: "#979797", display: "flex"}}>
|
||||
<span style={{marginLeft: 15, color: "#979797", display: "flex"}}>
|
||||
<RestoreIcon style={{color: "#979797", marginTop: "auto", marginBottom: "auto",}}/>
|
||||
<Typography style={{marginLeft: 5, marginTop: "auto", marginBottom: "auto",}}>
|
||||
{triggers}
|
||||
@@ -616,7 +621,7 @@ const MyView = (props) => {
|
||||
</span>
|
||||
</Tooltip>
|
||||
<Tooltip color="primary" title="Subflows used" placement="bottom">
|
||||
<span style={{marginLeft: 10, display: "flex", color: "#979797", }}>
|
||||
<span style={{marginLeft: 15, display: "flex", color: "#979797", }}>
|
||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg" style={{color: "#979797", marginTop: "auto", marginBottom: "auto",}}>
|
||||
<path d="M0 0H15V15H0V0ZM16 16H18V18H16V16ZM16 13H18V15H16V13ZM16 10H18V12H16V10ZM16 7H18V9H16V7ZM16 4H18V6H16V4ZM13 16H15V18H13V16ZM10 16H12V18H10V16ZM7 16H9V18H7V16ZM4 16H6V18H4V16Z" fill="#979797"/>
|
||||
</svg>
|
||||
@@ -1420,10 +1425,6 @@ const MyView = (props) => {
|
||||
<div style={{display: "flex"}}>
|
||||
<div style={{flex: "1"}}>
|
||||
<h2>Workflows</h2>
|
||||
<p>THIS IS SOME WORKFLOW INFORMATION WHY ISN’T IT GROWING THE CORRECT WAY.</p>
|
||||
</div>
|
||||
<div style={{marginTop: 20}}>
|
||||
{workflowButtons}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1457,6 +1458,15 @@ const MyView = (props) => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{display: "flex", margin: 20}}>
|
||||
<div style={{flex: 10}}>
|
||||
<Typography>This is your workflow view. <a rel="norefferer" target="_blank" href="https://shuffler.io/docs/workflows" target="_blank" style={{textDecoration: "none", color: "#f85a3e"}}>Learn more about Workflows</a></Typography>
|
||||
</div>
|
||||
<div style={{float: "right",}}>
|
||||
{workflowButtons}
|
||||
</div>
|
||||
</div>
|
||||
<div style={{marginTop: 15,}} />
|
||||
{view === "grid" && (
|
||||
<Grid container spacing={4} style={paperAppContainer}>
|
||||
{workflows.map((data, index) => {
|
||||
@@ -1471,6 +1481,7 @@ const MyView = (props) => {
|
||||
<WorkflowGridView />
|
||||
)}
|
||||
|
||||
<div style={{marginBottom: 100}}/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
+416
-342
@@ -1,8 +1,12 @@
|
||||
import React, { useEffect} from 'react';
|
||||
import { useInterval } from 'react-powerhooks';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
|
||||
import {Grid, Paper, Tooltip, Divider, Button, TextField, FormControl, IconButton, Menu, MenuItem, FormControlLabel, Chip, Switch, Typography, Zoom, CircularProgress, Dialog, DialogTitle, DialogActions, DialogContent} from '@material-ui/core';
|
||||
import {Cached as CachedIcon, GetApp as GetAppIcon, Apps as AppsIcon, Edit as EditIcon, MoreVert as MoreVertIcon, PlayArrow as PlayArrowIcon, Add as AddIcon, Publish as PublishIcon, CloudDownload as CloudDownloadIcon} from '@material-ui/icons';
|
||||
import {FileCopy as FileCopyIcon, Delete as DeleteIcon, BubbleChart as BubbleChartIcon, Restore as RestoreIcon, Cached as CachedIcon, GetApp as GetAppIcon, Apps as AppsIcon, Edit as EditIcon, MoreVert as MoreVertIcon, PlayArrow as PlayArrowIcon, Add as AddIcon, Publish as PublishIcon, CloudUpload as CloudUploadIcon, CloudDownload as CloudDownloadIcon} from '@material-ui/icons';
|
||||
//import {Search as SearchIcon, ArrowUpward as ArrowUpwardIcon, Visibility as VisibilityIcon, Done as DoneIcon, Close as CloseIcon, Error as ErrorIcon, FindReplace as FindreplaceIcon, ArrowLeft as ArrowLeftIcon, Cached as CachedIcon, DirectionsRun as DirectionsRunIcon, Add as AddIcon, Polymer as PolymerIcon, FormatListNumbered as FormatListNumberedIcon, Create as CreateIcon, PlayArrow as PlayArrowIcon, AspectRatio as AspectRatioIcon, MoreVert as MoreVertIcon, Apps as AppsIcon, Schedule as ScheduleIcon, FavoriteBorder as FavoriteBorderIcon, Pause as PauseIcon, Delete as DeleteIcon, AddCircleOutline as AddCircleOutlineIcon, Save as SaveIcon, KeyboardArrowLeft as KeyboardArrowLeftIcon, KeyboardArrowRight as KeyboardArrowRightIcon, ArrowBack as ArrowBackIcon, Settings as SettingsIcon, LockOpen as LockOpenIcon, ExpandMore as ExpandMoreIcon, VpnKey as VpnKeyIcon} from '@material-ui/icons';
|
||||
|
||||
import {DataGrid, GridToolbarContainer, GridDensitySelector, GridToolbar} from '@material-ui/data-grid';
|
||||
|
||||
//import JSONPretty from 'react-json-pretty';
|
||||
//import JSONPrettyMon from 'react-json-pretty/dist/monikai'
|
||||
@@ -13,10 +17,86 @@ import {Link} from 'react-router-dom';
|
||||
import { useAlert } from "react-alert";
|
||||
import ChipInput from 'material-ui-chip-input'
|
||||
import uuid from "uuid"
|
||||
import CytoscapeWrapper from '../components/RenderCytoscape'
|
||||
|
||||
//import mobileImage from '../assets/img/mobile.svg';
|
||||
//import bagImage from '../assets/img/bag.svg';
|
||||
//import bookImage from '../assets/img/book.svg';
|
||||
|
||||
const inputColor = "#383B40"
|
||||
const surfaceColor = "#27292D"
|
||||
|
||||
const flexContainerStyle = {
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "left",
|
||||
alignContent: "space-between",
|
||||
}
|
||||
|
||||
const flexBoxStyle = {
|
||||
height: 125,
|
||||
borderRadius: 4,
|
||||
boxSizing: "border-box",
|
||||
letterSpacing: "0.4px",
|
||||
color: "#D6791E",
|
||||
margin: 10,
|
||||
flex: 1,
|
||||
}
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
root: {
|
||||
border: 0,
|
||||
'& .MuiDataGrid-columnsContainer': {
|
||||
backgroundColor: theme.palette.type === 'light' ? '#fafafa' : '#1d1d1d',
|
||||
},
|
||||
'& .MuiDataGrid-iconSeparator': {
|
||||
display: 'none',
|
||||
},
|
||||
'& .MuiDataGrid-colCell, .MuiDataGrid-cell': {
|
||||
borderRight: `1px solid ${
|
||||
theme.palette.type === 'light' ? 'white' : '#303030'
|
||||
}`,
|
||||
},
|
||||
'& .MuiDataGrid-columnsContainer, .MuiDataGrid-cell': {
|
||||
borderBottom: `1px solid ${
|
||||
theme.palette.type === 'light' ? '#f0f0f0' : '#303030'
|
||||
}`,
|
||||
},
|
||||
'& .MuiDataGrid-cell': {
|
||||
color:
|
||||
theme.palette.type === 'light'
|
||||
? 'white'
|
||||
: 'rgba(255,255,255,0.65)',
|
||||
},
|
||||
'& .MuiPaginationItem-root, .MuiTablePagination-actions, .MuiTablePagination-caption': {
|
||||
borderRadius: 0,
|
||||
color: "white",
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
//const activeWorkflowStyle = {backgroundColor: "#FFF5EE"}
|
||||
//const notificationStyle = {backgroundColor: "#E5F9FF"}
|
||||
//const activeWorkflowStyle = {backgroundColor: "#3d3f43"}
|
||||
const availableWorkflowStyle = {backgroundColor: "#3d3f43"}
|
||||
const notificationStyle = {backgroundColor: "#3d3f43"}
|
||||
const activeWorkflowStyle = {backgroundColor: "#3d3f43"}
|
||||
|
||||
const fontSize_16 = {fontSize: "16px",}
|
||||
const counterStyle = {fontSize: "36px",fontWeight:"bold"}
|
||||
const blockRightStyle = {textAlign: "right",padding: "20px 20px 0px 0px",width:"100%"}
|
||||
|
||||
const flexContentStyle = {
|
||||
display: "flex",
|
||||
flexDirection: "row"
|
||||
}
|
||||
|
||||
const iconStyle = {
|
||||
width: "75px",
|
||||
height: "75px",
|
||||
padding: "20px"
|
||||
}
|
||||
|
||||
export const validateJson = (showResult) => {
|
||||
//showResult = showResult.split(" None").join(" \"None\"")
|
||||
showResult = showResult.split(" False").join(" false")
|
||||
@@ -54,6 +134,7 @@ const Workflows = (props) => {
|
||||
document.title = "Shuffle - Workflows"
|
||||
|
||||
const alert = useAlert()
|
||||
const classes = useStyles();
|
||||
|
||||
var upload = ""
|
||||
const [file, setFile] = React.useState("");
|
||||
@@ -84,16 +165,9 @@ const Workflows = (props) => {
|
||||
const [executionLoading, setExecutionLoading] = React.useState(false)
|
||||
const [importLoading, setImportLoading] = React.useState(false)
|
||||
const [isDropzone, setIsDropzone] = React.useState(false);
|
||||
const [view, setView] = React.useState("grid")
|
||||
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"
|
||||
|
||||
const { start, stop } = useInterval({
|
||||
duration: 5000,
|
||||
startImmediate: false,
|
||||
callback: () => {
|
||||
//getWorkflowExecution(selectedWorkflow.id)
|
||||
}
|
||||
})
|
||||
|
||||
const deleteModal = deleteModalOpen ?
|
||||
<Dialog
|
||||
open={deleteModalOpen}
|
||||
@@ -197,6 +271,11 @@ const Workflows = (props) => {
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
console.log("Status not 200 for workflows :O!: ", response.status)
|
||||
|
||||
if (isCloud) {
|
||||
window.location.pathname = "/login"
|
||||
}
|
||||
|
||||
alert.info("Failed getting workflows.")
|
||||
setWorkflowDone(true)
|
||||
|
||||
@@ -244,7 +323,7 @@ const Workflows = (props) => {
|
||||
minWidth: 1024,
|
||||
maxWidth: 1024,
|
||||
margin: "auto",
|
||||
maxHeight: "90vh",
|
||||
/*maxHeight: "90vh",*/
|
||||
}
|
||||
|
||||
const emptyWorkflowStyle = {
|
||||
@@ -272,78 +351,38 @@ const Workflows = (props) => {
|
||||
overflowY: "auto",
|
||||
}
|
||||
|
||||
const paperAppContainer = {
|
||||
display: "flex",
|
||||
flexWrap: 'wrap',
|
||||
alignContent: "space-between",
|
||||
}
|
||||
|
||||
const paperAppStyle = {
|
||||
minHeight: "100px",
|
||||
minWidth: "100%",
|
||||
maxWidth: "100%",
|
||||
marginTop: "5px",
|
||||
minHeight: 130,
|
||||
width: "100%",
|
||||
color: "white",
|
||||
backgroundColor: surfaceColor,
|
||||
padding: "12px 12px 0px 15px",
|
||||
borderRadius: 5,
|
||||
padding: 10,
|
||||
cursor: "pointer",
|
||||
display: "flex",
|
||||
boxSizing: "border-box",
|
||||
position: "relative",
|
||||
}
|
||||
|
||||
const getWorkflowExecution = (id) => {
|
||||
setExecutionLoading(true)
|
||||
fetch(globalUrl+"/api/v1/workflows/"+id+"/executions", {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
},
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => {
|
||||
setExecutionLoading(false)
|
||||
if (response.status !== 200) {
|
||||
console.log("Status not 200 for WORKFLOW EXECUTION :O!")
|
||||
}
|
||||
|
||||
return response.json()
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success === false) {
|
||||
alert.error("Failed getting executions")
|
||||
} else {
|
||||
if (responseJson.length > 0) {
|
||||
setSelectedExecution(responseJson[0])
|
||||
setWorkflowExecutions(responseJson)
|
||||
} else {
|
||||
//alert.info("Couldn't find executions for the workflow")
|
||||
setSelectedExecution({})
|
||||
setWorkflowExecutions([])
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
setExecutionLoading(false)
|
||||
alert.error(error.toString())
|
||||
});
|
||||
const gridContainer = {
|
||||
height: "auto",
|
||||
color: "white",
|
||||
margin: "10px",
|
||||
backgroundColor: surfaceColor,
|
||||
}
|
||||
|
||||
const abortExecution = (workflowid, executionid) => {
|
||||
alert.success("Aborting execution")
|
||||
fetch(globalUrl+"/api/v1/workflows/"+workflowid+"/executions/"+executionid+"/abort", {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
},
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
console.log("Status not 200 for WORKFLOW EXECUTION :O!")
|
||||
}
|
||||
//getWorkflowExecution(workflowid)
|
||||
|
||||
return response.json()
|
||||
})
|
||||
.catch(error => {
|
||||
alert.error(error.toString())
|
||||
});
|
||||
const workflowActionStyle = {
|
||||
flex: "1",
|
||||
display: "flex",
|
||||
width: 150,
|
||||
height: 44,
|
||||
justifyContent: "space-between",
|
||||
overflow: "hidden"
|
||||
}
|
||||
|
||||
const executeWorkflow = (id) => {
|
||||
@@ -372,13 +411,6 @@ const Workflows = (props) => {
|
||||
.catch(error => {
|
||||
alert.error(error.toString())
|
||||
});
|
||||
|
||||
if (id === selectedWorkflow.id) {
|
||||
sleep(2000).then(() => {
|
||||
stop()
|
||||
start()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function sleep (time) {
|
||||
@@ -393,7 +425,7 @@ const Workflows = (props) => {
|
||||
|
||||
const sanitizeWorkflow = (data) => {
|
||||
data["owner"] = ""
|
||||
console.log(data)
|
||||
console.log("Sanitize start: ", data)
|
||||
if (data.triggers !== null && data.triggers !== undefined) {
|
||||
for (var key in data.triggers) {
|
||||
const trigger = data.triggers[key]
|
||||
@@ -457,7 +489,8 @@ const Workflows = (props) => {
|
||||
}
|
||||
}
|
||||
|
||||
data.actions[key].environment = isCloud ? "cloud" : "Shuffle"
|
||||
//data.actions[key].environment = isCloud ? "cloud" : "Shuffle"
|
||||
data.actions[key].environment = ""
|
||||
data.actions[key].id = newId
|
||||
}
|
||||
}
|
||||
@@ -482,16 +515,18 @@ const Workflows = (props) => {
|
||||
// These are backwards.. True = saved before. Very confuse.
|
||||
data["previously_saved"] = false
|
||||
data["first_save"] = false
|
||||
console.log(data)
|
||||
console.log("Sanitize end: ", data)
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
const exportWorkflow = (data) => {
|
||||
console.log("export")
|
||||
let exportFileDefaultName = data.name+'.json';
|
||||
data = sanitizeWorkflow(data)
|
||||
|
||||
//console.log("EXPORT: ", data)
|
||||
//return
|
||||
|
||||
let dataStr = JSON.stringify(data)
|
||||
let dataUri = 'data:application/json;charset=utf-8,'+ encodeURIComponent(dataStr);
|
||||
let linkElement = document.createElement('a');
|
||||
@@ -600,7 +635,6 @@ const Workflows = (props) => {
|
||||
});
|
||||
}
|
||||
|
||||
// dropdown with copy etc I guess
|
||||
const WorkflowPaper = (props) => {
|
||||
const { data } = props;
|
||||
const [open, setOpen] = React.useState(false);
|
||||
@@ -611,12 +645,11 @@ const Workflows = (props) => {
|
||||
boxWidth = "4px"
|
||||
}
|
||||
|
||||
var boxColor = "#f85a3e"
|
||||
var boxColor = "#FECC00"
|
||||
if (data.is_valid) {
|
||||
boxColor = "green"
|
||||
boxColor = "#86c142"
|
||||
}
|
||||
|
||||
//console.log(data)
|
||||
if (!data.previously_saved) {
|
||||
boxColor = "#f85a3e"
|
||||
}
|
||||
@@ -626,63 +659,110 @@ const Workflows = (props) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
}
|
||||
|
||||
const actions = data.actions !== null ? data.actions.length : 0
|
||||
var schedules = 0
|
||||
var webhooks = 0
|
||||
var webhookImg = ""
|
||||
var scheduleImg = ""
|
||||
if (data.triggers !== undefined && data.triggers !== null && data.triggers.length > 0) {
|
||||
for (var key in data.triggers) {
|
||||
if (data.triggers[key].app_name === "Webhook") {
|
||||
webhooks += 1
|
||||
webhookImg = data.triggers[key].large_image
|
||||
} else if (data.triggers[key].app_name === "Schedule") {
|
||||
schedules += 1
|
||||
scheduleImg = data.triggers[key].large_image
|
||||
}
|
||||
}
|
||||
var parsedName = data.name
|
||||
if (parsedName !== undefined && parsedName !== null && parsedName.length > 25) {
|
||||
parsedName = parsedName.slice(0,25)+".."
|
||||
}
|
||||
|
||||
const imgSize = 25
|
||||
//console.log("TOP INFO: ", data)
|
||||
return (
|
||||
<Paper square style={paperAppStyle} onClick={(e) => {
|
||||
}}>
|
||||
<div style={{width: boxWidth, backgroundColor: boxColor}} />
|
||||
<Grid container style={{margin: "0px 10px 0px 10px", flex: 10}}>
|
||||
<Grid style={{display: "flex", flexDirection: "column", width: "100%"}}>
|
||||
<Grid item style={{flex: 1, display: "flex"}}>
|
||||
<div style={{flex: "10",}} onClick={() => {
|
||||
if (selectedWorkflow.id !== data.id) {
|
||||
//setSelectedWorkflow(data)
|
||||
//getWorkflowExecution(data.id)
|
||||
}
|
||||
}}>
|
||||
<Typography variant="h6" style={{marginTop: 10, marginBottom: 0, }}>
|
||||
{data.name}
|
||||
</Typography>
|
||||
</div>
|
||||
<div style={{flex: 1, }}>
|
||||
<IconButton
|
||||
aria-label="more"
|
||||
aria-controls="long-menu"
|
||||
aria-haspopup="true"
|
||||
style={{color: "white"}}
|
||||
onClick={menuClick}
|
||||
>
|
||||
<MoreVertIcon />
|
||||
</IconButton>
|
||||
<Menu
|
||||
id="long-menu"
|
||||
anchorEl={anchorEl}
|
||||
keepMounted
|
||||
open={open}
|
||||
onClose={() => {
|
||||
setOpen(false)
|
||||
setAnchorEl(null)
|
||||
}}
|
||||
>
|
||||
|
||||
const actions = data.actions !== null ? data.actions.length : 0
|
||||
const [triggers, schedules, webhooks, subflows] = getWorkflowMeta(data)
|
||||
|
||||
return (
|
||||
<Grid item xs={4} style={{padding: "12px 10px 12px 10px",}}>
|
||||
<Paper square style={paperAppStyle} >
|
||||
<div style={{position: "absolute", bottom: 1, left: 1, height: 12, width: 12, backgroundColor: boxColor, borderRadius: "0 100px 0 0",}} />
|
||||
<Grid item style={{display: "flex", flexDirection: "column", width: "100%"}}>
|
||||
<Grid item style={{flex: 1, display: "flex", maxHeight: 34,}}>
|
||||
<Typography variant="h6" style={{marginBottom: 0, paddingBottom: 0, maxHeight: 30,}}>
|
||||
{parsedName}
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item style={workflowActionStyle}>
|
||||
<Tooltip color="primary" title="Action amount" placement="bottom">
|
||||
<span style={{color: "#979797", display: "flex"}}>
|
||||
<BubbleChartIcon style={{marginTop: "auto", marginBottom: "auto",}} />
|
||||
<Typography style={{marginLeft: 5, marginTop: "auto", marginBottom: "auto",}}>
|
||||
{actions}
|
||||
</Typography>
|
||||
</span>
|
||||
</Tooltip>
|
||||
<Tooltip color="primary" title="Trigger amount" placement="bottom">
|
||||
<span style={{marginLeft: 15, color: "#979797", display: "flex"}}>
|
||||
<RestoreIcon style={{color: "#979797", marginTop: "auto", marginBottom: "auto",}}/>
|
||||
<Typography style={{marginLeft: 5, marginTop: "auto", marginBottom: "auto",}}>
|
||||
{triggers}
|
||||
</Typography>
|
||||
</span>
|
||||
</Tooltip>
|
||||
<Tooltip color="primary" title="Subflows used" placement="bottom">
|
||||
<span style={{marginLeft: 15, display: "flex", color: "#979797", }}>
|
||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg" style={{color: "#979797", marginTop: "auto", marginBottom: "auto",}}>
|
||||
<path d="M0 0H15V15H0V0ZM16 16H18V18H16V16ZM16 13H18V15H16V13ZM16 10H18V12H16V10ZM16 7H18V9H16V7ZM16 4H18V6H16V4ZM13 16H15V18H13V16ZM10 16H12V18H10V16ZM7 16H9V18H7V16ZM4 16H6V18H4V16Z" fill="#979797"/>
|
||||
</svg>
|
||||
<Typography style={{marginLeft: 5, marginTop: "auto", marginBottom: "auto",}}>
|
||||
{subflows}
|
||||
</Typography>
|
||||
</span>
|
||||
</Tooltip>
|
||||
{/*
|
||||
<Tooltip color="primary" title={`Actions: ${data.actions.length}`} placement="bottom">
|
||||
<AppsIcon />
|
||||
</Tooltip>
|
||||
<Tooltip color="primary" title={`Webhooks: ${webhooks}`} placement="bottom">
|
||||
<RestoreIcon />
|
||||
</Tooltip>
|
||||
: null}
|
||||
{schedules > 0 ?
|
||||
<Tooltip color="primary" title={`Schedules: ${schedules}`} placement="bottom">
|
||||
<RestoreIcon />
|
||||
</Tooltip>
|
||||
: null}
|
||||
*/}
|
||||
</Grid>
|
||||
<Grid item style={{flex: "1", justifyContent: "left", overflow: "hidden", marginTop: 5,}}>
|
||||
{data.tags !== undefined ?
|
||||
data.tags.map((tag, index) => {
|
||||
if (index >= 3) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<Chip
|
||||
key={index}
|
||||
style={{backgroundColor: "#3d3f43", height: 30, marginRight: 5, paddingLeft: 5, paddingRight: 5, height: 28, cursor: "pointer", borderColor: "#3d3f43", color: "white",}}
|
||||
label={tag}
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
/>
|
||||
)
|
||||
})
|
||||
: null}
|
||||
</Grid>
|
||||
</Grid>
|
||||
{data.actions !== undefined && data.actions !== null ?
|
||||
<Grid item style={{display:"flex",flexDirection:"column", justifyContent:"space-between"}}>
|
||||
<Grid>
|
||||
<IconButton
|
||||
aria-label="more"
|
||||
aria-controls="long-menu"
|
||||
aria-haspopup="true"
|
||||
style={{color: "white"}}
|
||||
onClick={menuClick}
|
||||
style={{padding:"0px",color:"white", color:"#979797"}}
|
||||
>
|
||||
<MoreVertIcon />
|
||||
</IconButton>
|
||||
<Menu
|
||||
id="long-menu"
|
||||
anchorEl={anchorEl}
|
||||
keepMounted
|
||||
open={open}
|
||||
onClose={() => {
|
||||
setOpen(false)
|
||||
setAnchorEl(null)
|
||||
}}
|
||||
>
|
||||
<MenuItem style={{backgroundColor: inputColor, color: "white"}} onClick={() => {
|
||||
setModalOpen(true)
|
||||
setEditingWorkflow(data)
|
||||
@@ -691,146 +771,57 @@ const Workflows = (props) => {
|
||||
if (data.tags !== undefined && data.tags !== null) {
|
||||
setNewWorkflowTags(JSON.parse(JSON.stringify(data.tags)))
|
||||
}
|
||||
}} key={"change"}>{"Change details"}</MenuItem>
|
||||
}} key={"change"}>
|
||||
<EditIcon style={{marginLeft: 0, marginRight: 8}}/>
|
||||
{"Change details"}
|
||||
</MenuItem>
|
||||
<MenuItem style={{backgroundColor: inputColor, color: "white"}} onClick={() => {
|
||||
console.log("Should publish", data)
|
||||
publishWorkflow(data)
|
||||
}} key={"publish"}>{"Publish Workflow"}</MenuItem>
|
||||
publishWorkflow(data)
|
||||
}} key={"publish"}>
|
||||
<CloudUploadIcon style={{marginLeft: 0, marginRight: 8}}/>
|
||||
{"Publish Workflow"}
|
||||
</MenuItem>
|
||||
<MenuItem style={{backgroundColor: inputColor, color: "white"}} onClick={() => {
|
||||
copyWorkflow(data)
|
||||
setOpen(false)
|
||||
}} key={"duplicate"}>{"Duplicate Workflow"}</MenuItem>
|
||||
}} key={"duplicate"}>
|
||||
<FileCopyIcon style={{marginLeft: 0, marginRight: 8}}/>
|
||||
{"Duplicate Workflow"}
|
||||
</MenuItem>
|
||||
<MenuItem style={{backgroundColor: inputColor, color: "white"}} onClick={() => {
|
||||
exportWorkflow(data)
|
||||
setOpen(false)
|
||||
}} key={"export"}>{"Export"}</MenuItem>
|
||||
<MenuItem style={{backgroundColor: inputColor, color: "white"}} onClick={() => {
|
||||
}} key={"export"}>
|
||||
<GetAppIcon style={{marginLeft: 0, marginRight: 8}}/>
|
||||
{"Export Workflow"}
|
||||
</MenuItem>
|
||||
<MenuItem style={{backgroundColor: inputColor, color: "white"}} onClick={() => {
|
||||
setDeleteModalOpen(true)
|
||||
setSelectedWorkflowId(data.id)
|
||||
setOpen(false)
|
||||
}} key={"delete"}>{"Delete"}</MenuItem>
|
||||
}} key={"delete"}>
|
||||
<DeleteIcon style={{marginLeft: 0, marginRight: 8}}/>
|
||||
{"Delete Workflow"}
|
||||
</MenuItem>
|
||||
|
||||
</Menu>
|
||||
</div>
|
||||
</Menu>
|
||||
</Grid>
|
||||
<div style={{display: "flex", flex: 1}} onClick={() => {
|
||||
if (selectedWorkflow.id !== data.id) {
|
||||
//setSelectedWorkflow(data)
|
||||
//getWorkflowExecution(data.id)
|
||||
}
|
||||
}}>
|
||||
<Grid item style={{flex: "1", justifyContent: "center", overflow: "hidden", float: "bottom",}}>
|
||||
<Link to={"/workflows/"+data.id}>
|
||||
<Tooltip color="primary" title="Edit workflow" placement="bottom">
|
||||
<Button style={{}} color="secondary" variant="text" style={{marginRight: 10}} onClick={() => {}}>
|
||||
<EditIcon style={{marginRight: 10}}/> Edit
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</Link>
|
||||
<Tooltip color="primary" title="Execute workflow" placement="bottom">
|
||||
<span>
|
||||
<Button style={{}} disabled={!data.is_valid} color="secondary" variant="text" onClick={() => executeWorkflow(data.id)}>
|
||||
<PlayArrowIcon />
|
||||
</Button>
|
||||
</span>
|
||||
<Grid>
|
||||
<Link to={"/workflows/"+data.id}>
|
||||
<Tooltip title="Edit workflow" placement="bottom">
|
||||
<EditIcon style={{background: "#F85A3E", borderRadius: "4px", color: "black", height: 20, width: 20, padding: 7, fontSize: "small"}} />
|
||||
</Tooltip>
|
||||
{data.tags !== undefined ?
|
||||
data.tags.map((tag, index) => {
|
||||
if (index >= 3) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<Chip
|
||||
key={index}
|
||||
style={{height: 25, marginRight: 5, marginTop: 2, cursor: "pointer",}}
|
||||
label={tag}
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
/>
|
||||
)
|
||||
})
|
||||
: null}
|
||||
</Grid>
|
||||
</div>
|
||||
</Grid>
|
||||
</Grid>
|
||||
{data.actions !== undefined && data.actions !== null ?
|
||||
<Grid container style={{maxWidth: 35, marginRight: 10,}}>
|
||||
<Tooltip title={`Actions: ${data.actions.length}`} placement="right">
|
||||
<AppsIcon style={{width: imgSize, height: imgSize}} />
|
||||
</Tooltip>
|
||||
|
||||
{webhooks > 0 ?
|
||||
<Tooltip title={`Webhooks: ${webhooks}`} placement="right">
|
||||
<img alt={data.title} style={{width: imgSize, height: imgSize, marginTop: 5}} src={webhookImg} />
|
||||
</Tooltip>
|
||||
: null}
|
||||
{schedules > 0 ?
|
||||
<Tooltip title={`Schedules: ${schedules}`} placement="right">
|
||||
<img alt={data.title} style={{width: imgSize, height: imgSize, marginTop: 5}} src={scheduleImg} />
|
||||
</Tooltip>
|
||||
: null}
|
||||
</Grid>
|
||||
: null}
|
||||
</Paper>
|
||||
)
|
||||
}
|
||||
|
||||
const executionPaper = (data) => {
|
||||
var boxWidth = "2px"
|
||||
if (selectedExecution.execution_id === data.execution_id) {
|
||||
boxWidth = "4px"
|
||||
}
|
||||
|
||||
var boxColor = "orange"
|
||||
if (data.status === "ABORTED" || data.status === "UNFINISHED" || data.status === "FAILURE"){
|
||||
boxColor = "red"
|
||||
} else if (data.status === "FINISHED") {
|
||||
boxColor = "green"
|
||||
}
|
||||
|
||||
var t = new Date(data.started_at*1000)
|
||||
if (data.workflow.actions === null || data.workflow.actions === undefined ) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (data.workflow.actions === null || data.workflow.actions === undefined) {
|
||||
return null
|
||||
}
|
||||
|
||||
var actions = data.workflow.actions.length
|
||||
if (data.results !== null) {
|
||||
var results = data.results.length
|
||||
}
|
||||
|
||||
return (
|
||||
<Paper key={data.execution_id} square style={paperAppStyle} onClick={() => {
|
||||
setSelectedExecution(data)
|
||||
}}>
|
||||
<div style={{marginLeft: "10px", marginTop: "5px", marginBottom: "5px", width: boxWidth, backgroundColor: boxColor}} />
|
||||
<Grid container style={{margin: "10px 10px 10px 10px", flex: 1}}>
|
||||
<Grid style={{display: "flex", flexDirection: "column", width: "100%"}}>
|
||||
<Grid item style={{flex: 1, display: "flex"}}>
|
||||
<div style={{flex: "5"}}>
|
||||
<h3 style={{marginBottom: "0px", marginTop: "10px"}}><b>Status</b>: {data.status}</h3>
|
||||
Actions: {results}/{actions}
|
||||
</div>
|
||||
<div style={{flex: "1", marginTop: "10px"}}>
|
||||
<Button style={{}} color="primary" disabled={data.status === "FAILURE" || data.status === "ABORTED" || data.status === "FINISHED"} variant="outlined" onClick={() => abortExecution(data.workflow_id, data.execution_id)}>Abort</Button>
|
||||
</div>
|
||||
</Link>
|
||||
</Grid>
|
||||
<div style={{display: "flex", flex: "1"}}>
|
||||
<Grid item style={{flex: "10", justifyContent: "center"}}>
|
||||
Started: {t.toISOString()}
|
||||
</Grid>
|
||||
</div>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Paper>
|
||||
: null}
|
||||
</Paper>
|
||||
</Grid>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
const dividerColor = "rgb(225, 228, 232)"
|
||||
|
||||
const resultPaperAppStyle = {
|
||||
@@ -1025,32 +1016,6 @@ const Workflows = (props) => {
|
||||
)
|
||||
}
|
||||
|
||||
const ExecutionsView = () => {
|
||||
if (workflowExecutions.length > 0) {
|
||||
const sortedWorkflows = workflowExecutions.sort((a, b) => a.started_at - b.started_at).reverse()
|
||||
|
||||
return (
|
||||
<div>
|
||||
{sortedWorkflows.map(data => {
|
||||
return (
|
||||
executionPaper(data)
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return (
|
||||
executionLoading ?
|
||||
<div style={{marginTop: 25, textAlign: "center"}}>
|
||||
<CircularProgress />
|
||||
</div>
|
||||
:
|
||||
<h4>
|
||||
Executions have been moved to the Workflow itself. <div/><Link to={`/workflows/${selectedWorkflow.id}?view=executions`} style={{textDecoration: "none", color: "#f85a3e"}}>Click here to see them</Link>
|
||||
</h4>
|
||||
)
|
||||
}
|
||||
|
||||
// Can create and set workflows
|
||||
const setNewWorkflow = (name, description, tags, editingWorkflow, redirect) => {
|
||||
|
||||
@@ -1173,6 +1138,111 @@ const Workflows = (props) => {
|
||||
setLoadWorkflowsModalOpen(false)
|
||||
}
|
||||
|
||||
const getWorkflowMeta = (data) => {
|
||||
let triggers = 0
|
||||
let schedules = 0
|
||||
let webhooks = 0
|
||||
let subflows = 0
|
||||
if (data.triggers !== undefined && data.triggers !== null && data.triggers.length > 0) {
|
||||
triggers = data.triggers.length
|
||||
for (let key in data.triggers) {
|
||||
|
||||
if (data.triggers[key].app_name === "Webhook") {
|
||||
webhooks += 1
|
||||
//webhookImg = data.triggers[key].large_image
|
||||
} else if (data.triggers[key].app_name === "Schedule") {
|
||||
schedules += 1
|
||||
//scheduleImg = data.triggers[key].large_image
|
||||
} else if (data.triggers[key].app_name === "Subflow") {
|
||||
subflows += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [triggers, schedules, webhooks, subflows]
|
||||
}
|
||||
|
||||
const WorkflowGridView = () => {
|
||||
let workflowData = "";
|
||||
if (workflows.length > 0) {
|
||||
const columns = [
|
||||
{ field: 'title', headerName: 'Title', width: 330, },
|
||||
{ field: 'actions', headerName: 'Actions', width: 200, sortable: false,
|
||||
disableClickEventBubbling: true,
|
||||
renderCell: (params) => {
|
||||
const data = params.row.record;
|
||||
let [triggers, schedules, webhooks, subflows] = getWorkflowMeta(data);
|
||||
|
||||
return <Grid item>
|
||||
<Link to={"/workflows/"+data.id}>
|
||||
<EditIcon style={{background: "#F85A3E",
|
||||
boxShadow: "0px 1px 2px rgba(0, 0, 0, 0.16), 0px 2px 4px rgba(0, 0, 0, 0.12), 0px 1px 8px rgba(0, 0, 0, 0.1)",
|
||||
borderRadius: "4px", color: "black", height: "20px", "width": "20px", fontSize: "small"}} />
|
||||
</Link>
|
||||
<Tooltip color="primary" title="Edit workflow" placement="bottom">
|
||||
<BubbleChartIcon/>
|
||||
</Tooltip>
|
||||
<Tooltip color="primary" title="Execute workflow" placement="bottom">
|
||||
<PlayArrowIcon color="secondary" disabled={!data.is_valid} onClick={() => executeWorkflow(data.id)} />
|
||||
</Tooltip>
|
||||
<Tooltip color="primary" title={`Actions: ${data.actions.length}`} placement="bottom">
|
||||
<AppsIcon />
|
||||
</Tooltip>
|
||||
{webhooks > 0 ?
|
||||
<Tooltip color="primary" title={`Webhooks: ${webhooks}`} placement="bottom">
|
||||
<RestoreIcon />
|
||||
</Tooltip>
|
||||
: null}
|
||||
{schedules > 0 ?
|
||||
<Tooltip color="primary" title={`Schedules: ${schedules}`} placement="bottom">
|
||||
<RestoreIcon />
|
||||
</Tooltip>
|
||||
: null}
|
||||
</Grid>
|
||||
}
|
||||
},
|
||||
{ field: 'tags', headerName: 'Tags', width: 390, sortable: false,
|
||||
disableClickEventBubbling: true,
|
||||
renderCell: (params) => {
|
||||
const data = params.row.record;
|
||||
return <Grid item>
|
||||
{data.tags !== undefined ?
|
||||
data.tags.map((tag, index) => {
|
||||
if (index >= 3) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<Chip
|
||||
key={index}
|
||||
style={{backgroundColor: "#3d3f43", height: 30, marginRight: 5, paddingLeft: 5, paddingRight: 5, height: 28, cursor: "pointer", borderColor: "#3d3f43", color: "white",}}
|
||||
label={tag}
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
/>
|
||||
)
|
||||
})
|
||||
: null}
|
||||
</Grid>
|
||||
}
|
||||
},
|
||||
];
|
||||
let rows = [];
|
||||
rows = workflows.map((data, index) => {
|
||||
let obj = {"id":index+1, "title":data.name, "record":data,};
|
||||
return obj;
|
||||
});
|
||||
workflowData = <DataGrid color="primary" className={classes.root} rows={rows} columns={columns} pageSize={5} checkboxSelection autoHeight density="standard" components={{
|
||||
Toolbar: GridToolbar,
|
||||
}} />
|
||||
}
|
||||
return (
|
||||
<div style={gridContainer}>
|
||||
{workflowData}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const modalView = modalOpen ?
|
||||
<Dialog
|
||||
open={modalOpen}
|
||||
@@ -1366,64 +1436,68 @@ const Workflows = (props) => {
|
||||
<div style={viewStyle}>
|
||||
<div style={workflowViewStyle}>
|
||||
<div style={{display: "flex"}}>
|
||||
<div style={{flex: "4"}}>
|
||||
<h2>Workflows ({workflows.length})</h2>
|
||||
<div style={{flex: "1"}}>
|
||||
<h2>Workflows</h2>
|
||||
</div>
|
||||
<div style={{marginTop: 20}}>
|
||||
</div>
|
||||
|
||||
{/*
|
||||
<div style={flexContainerStyle}>
|
||||
<div style={{...flexBoxStyle, ...activeWorkflowStyle}}>
|
||||
<div style={flexContentStyle}>
|
||||
<div ><img src={mobileImage} style={iconStyle} /></div>
|
||||
<div style={ blockRightStyle }>
|
||||
<div style={counterStyle}>{workflows.length}</div>
|
||||
<div style={fontSize_16}>ACTIVE WORKFLOWS</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{...flexBoxStyle, ...availableWorkflowStyle}}>
|
||||
<div style={flexContentStyle}>
|
||||
<div><img src={bookImage} style={iconStyle} /></div>
|
||||
<div style={ blockRightStyle }>
|
||||
<div style={counterStyle}>{workflows.length}</div>
|
||||
<div style={fontSize_16}>AVAILABE WORKFLOWS</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{...flexBoxStyle, ...notificationStyle}}>
|
||||
<div style={flexContentStyle}>
|
||||
<div><img src={bagImage} style={iconStyle} /></div>
|
||||
<div style={ blockRightStyle }>
|
||||
<div style={counterStyle}>{workflows.length}</div>
|
||||
<div style={fontSize_16}>NOTIFICATIONS</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
*/}
|
||||
|
||||
<div style={{display: "flex", margin: "20px 0px 20px 0px"}}>
|
||||
<div style={{flex: 10}}>
|
||||
<Typography>This is your workflow view. <a rel="norefferer" target="_blank" href="https://shuffler.io/docs/workflows" target="_blank" style={{textDecoration: "none", color: "#f85a3e"}}>Learn more about Workflows</a></Typography>
|
||||
</div>
|
||||
<div style={{float: "right",}}>
|
||||
{workflowButtons}
|
||||
</div>
|
||||
</div>
|
||||
<Divider style={{marginBottom: "10px", height: "1px", width: "100%", backgroundColor: dividerColor}}/>
|
||||
|
||||
<div style={scrollStyle}>
|
||||
{workflows.map((data, index) => {
|
||||
return (
|
||||
<div style={{marginTop: 15,}} />
|
||||
{view === "grid" && (
|
||||
<Grid container spacing={4} style={paperAppContainer}>
|
||||
{workflows.map((data, index) => {
|
||||
return (
|
||||
<WorkflowPaper key={index} data={data} />
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</Grid>
|
||||
)}
|
||||
|
||||
{view === "list" && (
|
||||
<WorkflowGridView />
|
||||
)}
|
||||
|
||||
<div style={{marginBottom: 100}}/>
|
||||
</div>
|
||||
<div style={{flex: viewSize.executionsView, marginLeft: "10px", marginRight: "10px"}}>
|
||||
<div style={{display: "flex"}}>
|
||||
<div style={{flex: 10}}>
|
||||
<h2>Executions: {selectedWorkflow.name}</h2>
|
||||
</div>
|
||||
{/*
|
||||
<div style={{flex: 1}}>
|
||||
<Button color="primary" style={{marginTop: "20px"}} variant="text" onClick={() => {
|
||||
alert.info("Refreshing executions");
|
||||
//getWorkflowExecution(selectedWorkflow.id)
|
||||
}}>
|
||||
<CachedIcon />
|
||||
</Button>
|
||||
</div>
|
||||
*/}
|
||||
</div>
|
||||
<Divider style={{marginBottom: "10px", height: "1px", width: "100%", backgroundColor: dividerColor}}/>
|
||||
<div style={scrollStyle}>
|
||||
<ExecutionsView />
|
||||
</div>
|
||||
</div>
|
||||
{/*
|
||||
<div style={{flex: viewSize.executionResults, marginLeft: "10px", marginRight: "10px", minWidth: "40%"}}>
|
||||
<div style={{display: "flex"}}>
|
||||
<div style={{flex: "1"}}>
|
||||
<h2>Execution Timeline</h2>
|
||||
</div>
|
||||
<div style={{flex: 1}}>
|
||||
<FormControlLabel
|
||||
style={{color: "white", marginBottom: "0px", marginTop: "10px"}}
|
||||
label={<div style={{color: "white"}}>Collapse results</div>}
|
||||
control={<Switch checked={collapseJson} onChange={() => {setCollapseJson(!collapseJson)}} />}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<Divider style={{marginBottom: "10px", height: "1px", width: "100%", backgroundColor: dividerColor}}/>
|
||||
<div style={scrollStyle}>
|
||||
<ExecutionDetails />
|
||||
</div>
|
||||
</div>
|
||||
*/}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user