import React, { useEffect} from 'react';
import { useInterval } from 'react-powerhooks';
import Grid from '@material-ui/core/Grid';
import Paper from '@material-ui/core/Paper';
import Tooltip from '@material-ui/core/Tooltip';
import Divider from '@material-ui/core/Divider';
import Button from '@material-ui/core/Button';
import TextField from '@material-ui/core/TextField';
import FormControl from '@material-ui/core/FormControl';
import IconButton from '@material-ui/core/IconButton';
import Menu from '@material-ui/core/Menu';
import MenuItem from '@material-ui/core/MenuItem';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import Chip from '@material-ui/core/Chip';
import Switch from '@material-ui/core/Switch';
import Typography from '@material-ui/core/Typography';
import Zoom from '@material-ui/core/Zoom';
import CircularProgress from '@material-ui/core/CircularProgress';
import CachedIcon from '@material-ui/icons/Cached';
import GetAppIcon from '@material-ui/icons/GetApp';
import AppsIcon from '@material-ui/icons/Apps';
import EditIcon from '@material-ui/icons/Edit';
import MoreVertIcon from '@material-ui/icons/MoreVert';
import PlayArrowIcon from '@material-ui/icons/PlayArrow';
import AddIcon from '@material-ui/icons/Add';
import PublishIcon from '@material-ui/icons/Publish';
//import JSONPretty from 'react-json-pretty';
//import JSONPrettyMon from 'react-json-pretty/dist/monikai'
import ReactJson from 'react-json-view'
import {Link} from 'react-router-dom';
import { useAlert } from "react-alert";
import ChipInput from 'material-ui-chip-input'
import Dialog from '@material-ui/core/Dialog';
import DialogTitle from '@material-ui/core/DialogTitle';
import DialogActions from '@material-ui/core/DialogActions';
import DialogContent from '@material-ui/core/DialogContent';
import CloudDownloadIcon from '@material-ui/icons/CloudDownload';
const inputColor = "#383B40"
const surfaceColor = "#27292D"
export const validateJson = (showResult) => {
//showResult = showResult.split(" None").join(" \"None\"")
showResult = showResult.split(" False").join(" false")
showResult = showResult.split(" True").join(" true")
var jsonvalid = true
try {
const tmp = String(JSON.parse(showResult))
if (!showResult.includes("{") && !showResult.includes("[")) {
jsonvalid = false
}
} catch (e) {
showResult = showResult.split("\'").join("\"")
try {
const tmp = String(JSON.parse(showResult))
if (!showResult.includes("{") && !showResult.includes("[")) {
jsonvalid = false
}
} catch (e) {
jsonvalid = false
}
}
const result = jsonvalid ? JSON.parse(showResult) : showResult
//console.log("VALID: ", jsonvalid, result)
return {
"valid": jsonvalid,
"result": result,
}
}
const Workflows = (props) => {
const { globalUrl, isLoggedIn, isLoaded, removeCookie, cookies, userdata} = props;
document.title = "Shuffle - Workflows"
const alert = useAlert()
var upload = ""
const [file, setFile] = React.useState("");
const [workflows, setWorkflows] = React.useState([]);
const [selectedWorkflow, setSelectedWorkflow] = React.useState({});
const [selectedExecution, setSelectedExecution] = React.useState({});
const [workflowExecutions, setWorkflowExecutions] = React.useState([]);
const [firstrequest, setFirstrequest] = React.useState(true)
const [workflowDone, setWorkflowDone] = React.useState(false)
const [, setTrackingId] = React.useState("")
const [selectedWorkflowId, setSelectedWorkflowId] = React.useState("")
const [collapseJson, setCollapseJson] = React.useState(false)
const [field1, setField1] = React.useState("")
const [field2, setField2] = React.useState("")
const [downloadUrl, setDownloadUrl] = React.useState("https://github.com/frikky/shuffle-workflows")
const [downloadBranch, setDownloadBranch] = React.useState("master")
const [loadWorkflowsModalOpen, setLoadWorkflowsModalOpen] = React.useState(false)
const [modalOpen, setModalOpen] = React.useState(false);
const [newWorkflowName, setNewWorkflowName] = React.useState("");
const [newWorkflowDescription, setNewWorkflowDescription] = React.useState("");
const [newWorkflowTags, setNewWorkflowTags] = React.useState([]);
const [update, setUpdate] = React.useState("test");
const [deleteModalOpen, setDeleteModalOpen] = React.useState(false);
const [editingWorkflow, setEditingWorkflow] = React.useState({})
const [executionLoading, setExecutionLoading] = React.useState(false)
const { start, stop } = useInterval({
duration: 5000,
startImmediate: false,
callback: () => {
//getWorkflowExecution(selectedWorkflow.id)
}
})
// DEBUG HERE
const handleClickLogout = () => {
//console.log("Cookies: ", cookies)
//console.log("SHOULD LOG OUT")
//console.log(isLoggedIn)
// Don't really care about the logout
//fetch(globalUrl+"/api/v1/logout", {
// credentials: "include",
// method: 'POST',
// headers: {
// 'Content-Type': 'application/json',
// },
//})
//.then(() => {
// // Log out anyway
// removeCookie("session_token", {path: "/"})
// //window.location = "/login"
//})
//.catch(error => {
// console.log(error)
// removeCookie("session_token", {path: "/"})
//});
}
const deleteModal = deleteModalOpen ?
: null
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) => {
setSelectedExecution({})
setWorkflowExecutions([])
if (responseJson !== undefined) {
setWorkflows(responseJson)
setWorkflowDone(true)
} else {
if (isLoggedIn) {
alert.error("An error occurred while loading workflows")
} else {
handleClickLogout()
}
return
}
if (responseJson.length > 0){
setSelectedWorkflow(responseJson[0])
//getWorkflowExecution(responseJson[0].id)
}
})
.catch(error => {
alert.error(error.toString())
});
}
useEffect(() => {
if (workflows.length <= 0 && firstrequest) {
setFirstrequest(false)
getAvailableWorkflows()
}
})
const viewStyle = {
color: "#ffffff",
width: "100%",
display: "flex",
minWidth: 1024,
maxWidth: 1024,
margin: "auto",
maxHeight: "90vh",
}
const emptyWorkflowStyle = {
paddingTop: "200px",
width: 1024,
margin: "auto",
}
const boxStyle = {
padding: "20px 20px 20px 20px",
width: "100%",
height: "250px",
color: "white",
backgroundColor: surfaceColor,
display: "flex",
flexDirection: "column",
}
const scrollStyle = {
marginTop: "10px",
overflow: "scroll",
height: "90%",
overflowX: "hidden",
overflowY: "auto",
}
const paperAppStyle = {
minHeight: "100px",
minWidth: "100%",
maxWidth: "100%",
marginTop: "5px",
color: "white",
backgroundColor: surfaceColor,
borderRadius: 5,
padding: 10,
cursor: "pointer",
display: "flex",
}
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 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 executeWorkflow = (id) => {
alert.show("Executing workflow "+id)
setTrackingId(id)
fetch(globalUrl+"/api/v1/workflows/"+id+"/execute", {
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!")
}
return response.json()
})
.then((responseJson) => {
if (!responseJson.success) {
alert.error(responseJson.reason)
}
})
.catch(error => {
alert.error(error.toString())
});
if (id === selectedWorkflow.id) {
sleep(2000).then(() => {
stop()
start()
})
}
}
function sleep (time) {
return new Promise((resolve) => setTimeout(resolve, time));
}
const exportAllWorkflows = () => {
for (var key in workflows) {
exportWorkflow(workflows[key])
}
}
const exportWorkflow = (data) => {
console.log("export")
let dataStr = JSON.stringify(data)
let dataUri = 'data:application/json;charset=utf-8,'+ encodeURIComponent(dataStr);
let exportFileDefaultName = data.name+'.json';
data["owner"] = ""
for (var key in data.triggers) {
const trigger = data.triggers[key]
if (trigger.app_name === "Shuffle Workflow") {
if (trigger.parameters.length > 2) {
trigger.parameters[2].value = ""
}
}
if (trigger.status == "running") {
trigger.status = "stopped"
}
}
for (var key in data.actions) {
data.actions[key].authentication_id = ""
}
//return
data["org"] = []
data["org_id"] = ""
data.execution_org = {"id": ""}
console.log(data)
let linkElement = document.createElement('a');
linkElement.setAttribute('href', dataUri);
linkElement.setAttribute('download', exportFileDefaultName);
linkElement.click();
}
const copyWorkflow = (data) => {
data = JSON.parse(JSON.stringify(data))
alert.success("Copying workflow "+data.name)
console.log("data: ", data)
data.id = ""
data.name = data.name+"_copy"
//return
fetch(globalUrl+"/api/v1/workflows", {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: JSON.stringify(data),
credentials: "include",
})
.then((response) => {
if (response.status !== 200) {
console.log("Status not 200 for workflows :O!")
return
}
return response.json()
})
.then((responseJson) => {
getAvailableWorkflows()
})
.catch(error => {
alert.error(error.toString())
});
}
const deleteWorkflow = (id) => {
alert.success("Deleted workflow "+id)
fetch(globalUrl+"/api/v1/workflows/"+id, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
credentials: "include",
})
.then((response) => {
if (response.status !== 200) {
console.log("Status not 200 for setting workflows :O!")
}
return response.json()
})
.then((responseJson) => {
getAvailableWorkflows()
})
.catch(error => {
alert.error(error.toString())
});
}
// dropdown with copy etc I guess
const WorkflowPaper = (props) => {
const { data } = props;
const [open, setOpen] = React.useState(false);
const [anchorEl, setAnchorEl] = React.useState(null);
var boxWidth = "2px"
if (selectedWorkflow.id === data.id) {
boxWidth = "4px"
}
var boxColor = "orange"
if (data.is_valid) {
boxColor = "green"
}
const menuClick = (event) => {
setOpen(!open)
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
}
}
}
const imgSize = 25
return (
Status: {data.status}
Actions: {results}/{actions}
Name: {data.action.label}
Shuffle is a flexible, easy to use, automation platform allowing users to integrate their services and devices freely. It's made to significantly reduce the amount of manual labor, and is focused on security applications. Click here to learn more.