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"; import BubbleChartIcon from "@material-ui/icons/BubbleChart"; import RestoreIcon from "@material-ui/icons/Restore"; import mobileImage from "../assets/img/mobile.svg"; import bagImage from "../assets/img/bag.svg"; import bookImage from "../assets/img/book.svg"; import { DataGrid, GridToolbarContainer, GridDensitySelector, GridToolbar, } from "@material-ui/data-grid"; import { makeStyles } from "@material-ui/core/styles"; import ListIcon from "@material-ui/icons/List"; import GridOnIcon from "@material-ui/icons/GridOn"; 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 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", flexDirection: "row", }; const iconStyle = { width: "75px", height: "75px", padding: "20px", }; const fontSize_16 = { fontSize: "16px" }; const counterStyle = { fontSize: "36px", fontWeight: "bold" }; const blockRightStyle = { textAlign: "right", padding: "20px 20px 0px 0px", width: "100%", }; 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 MyView = (props) => { const { globalUrl, isLoggedIn, isLoaded, 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 [view, setView] = React.useState("grid"); const { start, stop } = useInterval({ duration: 5000, startImmediate: false, callback: () => { //getWorkflowExecution(selectedWorkflow.id) }, }); // DEBUG HERE const handleClickLogout = () => {}; const deleteModal = deleteModalOpen ? ( { setDeleteModalOpen(false); setSelectedWorkflowId(""); }} PaperProps={{ style: { backgroundColor: surfaceColor, color: "white", minWidth: 500, }, }} >
Are you sure?
Other workflows relying on this one may stop working
) : 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 paperAppContainer = { display: "flex", flexWrap: "wrap", alignContent: "space-between", }; const paperAppStyle = { minHeight: 130, width: "100%", color: "white", backgroundColor: surfaceColor, padding: "12px 12px 0px 15px", borderRadius: 5, display: "flex", boxSizing: "border-box", position: "relative", }; const gridContainer = { height: "auto", color: "white", margin: "10px", backgroundColor: surfaceColor, }; const workflowActionStyle = { flex: "1", display: "flex", width: 150, height: 44, justifyContent: "space-between", overflow: "hidden", }; 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()); }); }; 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]; }; // 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 = "#FECC00"; if (data.is_valid) { boxColor = "#86c142"; } if (!data.previously_saved) { boxColor = "#f85a3e"; } const menuClick = (event) => { setOpen(!open); setAnchorEl(event.currentTarget); }; var parsedName = data.name; if ( parsedName !== undefined && parsedName !== null && parsedName.length > 25 ) { parsedName = parsedName.slice(0, 25) + ".."; } const actions = data.actions !== null ? data.actions.length : 0; const [triggers, schedules, webhooks, subflows] = getWorkflowMeta(data); return (
{parsedName} {actions} {triggers} {subflows} {/* : null} {schedules > 0 ? : null} */} {data.tags !== undefined ? data.tags.map((tag, index) => { if (index >= 3) { return null; } return ( ); }) : null} {data.actions !== undefined && data.actions !== null ? ( { setOpen(false); setAnchorEl(null); }} > { setModalOpen(true); setEditingWorkflow(data); setNewWorkflowName(data.name); setNewWorkflowDescription(data.description); if (data.tags !== undefined && data.tags !== null) { setNewWorkflowTags( JSON.parse(JSON.stringify(data.tags)) ); } }} key={"change"} > {"Change details"} { copyWorkflow(data); setOpen(false); }} key={"copy"} > {"Copy"} { exportWorkflow(data); setOpen(false); }} key={"export"} > {"Export"} { setDeleteModalOpen(true); setSelectedWorkflowId(data.id); setOpen(false); }} key={"delete"} > {"Delete"} ) : null} ); }; 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 ( { setSelectedExecution(data); }} >

Status: {data.status}

Actions: {results}/{actions}
Started: {t.toISOString()}
); }; const dividerColor = "rgb(225, 228, 232)"; const resultPaperAppStyle = { minHeight: "100px", minWidth: "100%", overflow: "hidden", maxWidth: "100%", marginTop: "5px", color: "white", backgroundColor: surfaceColor, display: "flex", }; function replaceAll(string, search, replace) { return string.split(search).join(replace); } const resultsPaper = (data) => { var boxWidth = "2px"; var boxColor = "orange"; if ( data.status === "ABORTED" || data.status === "UNFINISHED" || data.status === "FAILURE" ) { boxColor = "red"; } else if (data.status === "FINISHED" || data.status === "SUCCESS") { boxColor = "green"; } else if (data.status === "SKIPPED" || data.status === "EXECUTING") { boxColor = "yellow"; } else { boxColor = "green"; } var t = new Date(data.started_at * 1000); var showResult = data.result.trim(); const validate = validateJson(showResult); if (validate.valid) { showResult = ( ); } else { // FIXME - have everything parsed as json, either just for frontend // or in the backend? /* const newdata = {"result": data.result} showResult = */ } return ( {}} >

Name: {data.action.label}

App: {data.action.app_name}, Version: {data.action.app_version} Action: {data.action.name}, Environment: {data.action.environment} , Status: {data.status}
Started: {t.toISOString()}
{showResult}
); }; const resultsHandler = Object.getOwnPropertyNames(selectedExecution).length > 0 && selectedExecution.results !== null ? (
{selectedExecution.results .sort((a, b) => a.started_at - b.started_at) .map((data, index) => { return
{resultsPaper(data)}
; })}
) : (
No results yet
); const resultsLength = Object.getOwnPropertyNames(selectedExecution).length > 0 && selectedExecution.results !== null ? selectedExecution.results.length : 0; const ExecutionDetails = () => { var starttime = new Date(selectedExecution.started_at * 1000); var endtime = new Date(selectedExecution.started_at * 1000); var parsedArgument = selectedExecution.execution_argument; if ( selectedExecution.execution_argument !== undefined && selectedExecution.execution_argument.length > 0 ) { parsedArgument = replaceAll(parsedArgument, " None", ' "None"'); } var arg = null; if ( selectedExecution.execution_argument !== undefined && selectedExecution.execution_argument.length > 0 ) { var showResult = selectedExecution.execution_argument.trim(); const validate = validateJson(showResult); arg = validate.valid ? ( ) : ( showResult ); } var lastresult = null; if ( selectedExecution.result !== undefined && selectedExecution.result.length > 0 ) { var showResult = selectedExecution.result.trim(); const validate = validateJson(showResult); lastresult = validate.valid ? ( ) : ( showResult ); } /*
ID: {selectedExecution.execution_id}
Last node: {selectedExecution.workflow.actions.find(data => data.id === selectedExecution.last_node).actions[0].label}
*/ if ( Object.getOwnPropertyNames(selectedExecution).length > 0 && selectedExecution.workflow.actions !== null ) { return (
Status: {selectedExecution.status}
Started: {starttime.toISOString()}
Finished: {endtime.toISOString()}
{/*
Last Result: {lastresult}
*/}
{arg}
{resultsHandler}
); } return executionLoading ? (
) : (

There are no executiondetails yet. Click "execute" to run your first one.

); }; const ExecutionsView = () => { if (workflowExecutions.length > 0) { const sortedWorkflows = workflowExecutions .sort((a, b) => a.started_at - b.started_at) .reverse(); return (
{sortedWorkflows.map((data) => { return executionPaper(data); })}
); } return executionLoading ? (
) : (

Executions have been moved to the Workflow itself.
Click here to see them

); }; // Can create and set workflows const setNewWorkflow = ( name, description, tags, editingWorkflow, redirect ) => { var method = "POST"; var extraData = ""; var workflowdata = {}; if (editingWorkflow.id !== undefined) { console.log("Building original workflow"); method = "PUT"; extraData = "/" + editingWorkflow.id; workflowdata = editingWorkflow; console.log("REMOVING OWNER"); workflowdata["owner"] = ""; // FIXME: Loop triggers and turn them off? } workflowdata["name"] = name; workflowdata["description"] = description; if (tags !== undefined) { workflowdata["tags"] = tags; } //console.log(workflowdata) //return return fetch(globalUrl + "/api/v1/workflows" + extraData, { method: method, headers: { "Content-Type": "application/json", Accept: "application/json", }, body: JSON.stringify(workflowdata), credentials: "include", }) .then((response) => { if (response.status !== 200) { console.log("Status not 200 for workflows :O!"); return; } return response.json(); }) .then((responseJson) => { if (method === "POST" && redirect) { window.location.pathname = "/workflows/" + responseJson["id"]; } else if (!redirect) { // Update :) getAvailableWorkflows(); } else { alert.info("Successfully changed basic info for workflow"); } return responseJson; }) .catch((error) => { alert.error(error.toString()); }); }; const importFiles = (event) => { console.log("Importing!"); const file = event.target.value; if (event.target.files.length > 0) { for (var key in event.target.files) { const file = event.target.files[key]; if (file.type !== "application/json") { if (file.type !== undefined) { alert.error("File has to contain valid json"); } continue; } const reader = new FileReader(); // Waits for the read reader.addEventListener("load", (event) => { var data = reader.result; try { data = JSON.parse(reader.result); } catch (e) { alert.error("Invalid JSON: " + e); return; } // Initialize the workflow itself const ret = setNewWorkflow( data.name, data.description, data.tags, {}, false ) .then((response) => { if (response !== undefined) { // SET THE FULL THING data.id = response.id; // Actually create it const ret = setNewWorkflow( data.name, data.description, data.tags, data, false ).then((response) => { if (response !== undefined) { alert.success("Successfully imported " + data.name); } }); } }) .catch((error) => { alert.error("Import error: " + error.toString()); }); }); // Actually reads reader.readAsText(file); } } setLoadWorkflowsModalOpen(false); }; const modalView = modalOpen ? ( { setModalOpen(false); }} PaperProps={{ style: { backgroundColor: surfaceColor, color: "white", minWidth: "800px", }, }} >
{editingWorkflow.id !== undefined ? "Editing" : "New"} workflow
setNewWorkflowName(event.target.value)} InputProps={{ style: { color: "white", }, }} color="primary" placeholder="Name" margin="dense" defaultValue={newWorkflowName} fullWidth /> setNewWorkflowDescription(event.target.value)} InputProps={{ style: { color: "white", }, }} color="primary" defaultValue={newWorkflowDescription} placeholder="Description" margin="dense" fullWidth /> { newWorkflowTags.push(chip); setNewWorkflowTags(newWorkflowTags); }} onDelete={(chip, index) => { newWorkflowTags.splice(index, 1); setNewWorkflowTags(newWorkflowTags); setUpdate("delete " + chip); }} />
) : null; const viewSize = { workflowView: 4, executionsView: 3, executionResults: 4, }; const workflowViewStyle = { flex: viewSize.workflowView, marginLeft: "10px", marginRight: "10px", }; if (viewSize.workflowView === 0) { workflowViewStyle.display = "none"; } const workflowButtons = ( {view === "grid" && ( )} {view === "list" && ( )} {workflows.length > 0 ? ( ) : null} (upload = ref)} onChange={importFiles} /> {workflows.length > 0 ? ( ) : null} ); 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 classes = useStyles(); 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 ( executeWorkflow(data.id)} /> {webhooks > 0 ? ( ) : null} {schedules > 0 ? ( ) : null} ); }, }, { field: "tags", headerName: "Tags", width: 390, sortable: false, disableClickEventBubbling: true, renderCell: (params) => { const data = params.row.record; return ( {data.tags !== undefined ? data.tags.map((tag, index) => { if (index >= 3) { return null; } return ( ); }) : null} ); }, }, ]; let rows = []; rows = workflows.map((data, index) => { let obj = { id: index + 1, title: data.name, record: data }; return obj; }); workflowData = ( ); } return
{workflowData}
; }; const WorkflowView = () => { if (workflows.length === 0) { return (

Welcome to Shuffle

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.

If you want to jump straight into it, click here to create your first workflow:
..OR {workflowButtons}
); } return (

Workflows

{workflows.length}
ACTIVE WORKFLOWS
{workflows.length}
AVAILABE WORKFLOWS
{workflows.length}
NOTIFICATIONS
This is your workflow view.{" "} Learn more about Workflows
{workflowButtons}
{view === "grid" && ( {workflows.map((data, index) => { return ; })} )} {view === "list" && }
); }; const importWorkflowsFromUrl = (url) => { console.log("IMPORT WORKFLOWS FROM ", downloadUrl); const parsedData = { url: url, field_3: downloadBranch || "master", }; if (field1.length > 0) { parsedData["field_1"] = field1; } if (field2.length > 0) { parsedData["field_2"] = field2; } alert.success("Getting specific workflows from your URL."); var cors = "cors"; fetch(globalUrl + "/api/v1/workflows/download_remote", { method: "POST", mode: "cors", headers: { Accept: "application/json", }, body: JSON.stringify(parsedData), credentials: "include", }) .then((response) => { if (response.status === 200) { alert.success("Successfully loaded workflows from " + downloadUrl); getAvailableWorkflows(); } return response.json(); }) .then((responseJson) => { console.log("DATA: ", responseJson); if (!responseJson.success) { if (responseJson.reason !== undefined) { alert.error("Failed loading: " + responseJson.reason); } else { alert.error("Failed loading"); } } }) .catch((error) => { alert.error(error.toString()); }); }; const handleGithubValidation = () => { importWorkflowsFromUrl(downloadUrl); setLoadWorkflowsModalOpen(false); }; const workflowDownloadModalOpen = loadWorkflowsModalOpen ? ( {}} PaperProps={{ style: { backgroundColor: surfaceColor, color: "white", minWidth: "800px", minHeight: "320px", }, }} >
Load workflows from github repo
Repository (supported: github, gitlab, bitbucket) 0 ? userdata.active_org.defaults.workflow_download_repo : downloadUrl } InputProps={{ style: { color: "white", height: "50px", fontSize: "1em", }, }} onChange={(e) => setDownloadUrl(e.target.value)} placeholder="https://github.com/frikky/shuffle-apps" fullWidth /> Branch (default value is "master"):
0 ? userdata.active_org.defaults.workflow_download_branch : downloadBranch } InputProps={{ style: { color: "white", height: "50px", fontSize: "1em", }, }} onChange={(e) => setDownloadBranch(e.target.value)} placeholder="master" fullWidth />
Authentication (optional - private repos etc):
setField1(e.target.value)} type="username" placeholder="Username / APIkey (optional)" fullWidth /> setField2(e.target.value)} type="password" placeholder="Password (optional)" fullWidth />
) : null; const loadedCheck = isLoaded && isLoggedIn && workflowDone ? (
{modalView} {deleteModal} {workflowDownloadModalOpen}
) : (
Loading Workflows
); // Maybe use gridview or something, idk return
{loadedCheck}
; }; export default MyView;