A bunch of commits moved from cloud -> onprem to sync up current work on 2.0
This commit is contained in:
@@ -6274,7 +6274,7 @@ If you're interested, please let me know a time that works for you, or set up a
|
||||
style={{ minWidth: 50, maxWidth: 50 }}
|
||||
/>
|
||||
<ListItemText
|
||||
primary="License"
|
||||
primary="Scale"
|
||||
style={{ minWidth: 85, maxWidth: 85 }}
|
||||
/>
|
||||
<ListItemText
|
||||
@@ -6406,7 +6406,7 @@ If you're interested, please let me know a time that works for you, or set up a
|
||||
</Tooltip>
|
||||
) : (
|
||||
<Tooltip
|
||||
title="Not licensed, and can't scale.. This may cause service disruption."
|
||||
title="In Verbose mode. Set SHUFFLE_SWARM_CONFIG=run to Scale. This will not be as verbose. Details: https://shuffler.io/docs/configuration#scaling-shuffle"
|
||||
placement="top"
|
||||
>
|
||||
<a
|
||||
@@ -6441,7 +6441,7 @@ If you're interested, please let me know a time that works for you, or set up a
|
||||
environment.running_ip.length === 0 ? (
|
||||
<div>Not running</div>
|
||||
) : (
|
||||
environment.running_ip.split(":")[0]
|
||||
environment.running_ip
|
||||
)
|
||||
) : (
|
||||
"N/A"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -544,7 +544,7 @@ const AppCreator = (defaultprops) => {
|
||||
};
|
||||
|
||||
|
||||
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
|
||||
const isCloud = (window.location.host === "localhost:3002" || window.location.host === "shuffler.io") ? true : (process.env.IS_SSR === "true");
|
||||
|
||||
useEffect(() => {
|
||||
if (window.location.pathname.includes("apps/edit")) {
|
||||
@@ -897,8 +897,31 @@ const AppCreator = (defaultprops) => {
|
||||
};
|
||||
|
||||
if (methodvalue["x-label"] !== undefined && methodvalue["x-label"] !== null) {
|
||||
console.log("LABEL: ", methodvalue["x-label"])
|
||||
|
||||
var correctlabel = ""
|
||||
const labels = methodvalue["x-label"].split(",")
|
||||
for (let labelkey in labels) {
|
||||
var label = labels[labelkey].trim()
|
||||
if (label.toLowerCase() === "no label") {
|
||||
continue
|
||||
}
|
||||
|
||||
// Remove quotes and escapes
|
||||
label = label.replace(/['"]+/g, '')
|
||||
label = label.replace(/\\/g, '')
|
||||
|
||||
//label = label.replace("_", " ", -1)
|
||||
//label = label.charAt(0).toUpperCase() + label.slice(1)
|
||||
|
||||
correctlabel = label
|
||||
break
|
||||
}
|
||||
|
||||
console.log("LABEL: ", correctlabel)
|
||||
// FIX: Map labels only if they're actually in the category list
|
||||
newaction.action_label = methodvalue["x-label"]
|
||||
//newaction.action_label = methodvalue["x-label"]
|
||||
newaction.action_label = correctlabel
|
||||
}
|
||||
|
||||
if (methodvalue["x-required-fields"] !== undefined && methodvalue["x-required-fields"] !== null) {
|
||||
@@ -3124,15 +3147,14 @@ const AppCreator = (defaultprops) => {
|
||||
Scopes for Oauth2
|
||||
</Typography>
|
||||
<MuiChipsInput
|
||||
style={{border: "2px solid #f86a3e", borderRadius: theme.palette.borderRadius,}}
|
||||
required
|
||||
required
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
maxHeight: 50,
|
||||
maxHeight: 160,
|
||||
},
|
||||
}}
|
||||
style={{ maxHeight: 80, overflowX: "hidden", overflowY: "auto" }}
|
||||
style={{ minHeight: 160, maxHeight: 160, overflowX: "hidden", overflowY: "auto" }}
|
||||
placeholder="Available Oauth2 Scopes"
|
||||
color="primary"
|
||||
fullWidth
|
||||
@@ -3159,7 +3181,7 @@ const AppCreator = (defaultprops) => {
|
||||
required
|
||||
style={{ marginTop: 0, backgroundColor: inputColor }}
|
||||
fullWidth={true}
|
||||
placeholder="Field Name (key, NOT your actual API-key)"
|
||||
placeholder="The Key to use as the header/query - NOT your actual API-key"
|
||||
type="name"
|
||||
id="standard-required"
|
||||
margin="normal"
|
||||
@@ -4441,7 +4463,7 @@ const AppCreator = (defaultprops) => {
|
||||
setUpdate(Math.random())
|
||||
}
|
||||
}}
|
||||
value={data.action_label}
|
||||
value={data?.action_label?.replace(" ", "_").toLowerCase()}
|
||||
style={{
|
||||
border: data.action_label === undefined || data.action_label === "No Label" ? "" : `2px solid ${bgColor}`,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
@@ -4463,7 +4485,7 @@ const AppCreator = (defaultprops) => {
|
||||
return (
|
||||
<MenuItem
|
||||
key={labelindex}
|
||||
value={label}
|
||||
value={label.replace(" ", "_").toLowerCase()}
|
||||
style={{
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -3035,7 +3035,7 @@ const Apps = (props) => {
|
||||
height: "50px",
|
||||
}}
|
||||
variant="contained"
|
||||
disabled={openApi.length === 0 || appValidation.length > 0}
|
||||
disabled={openApi.length === 0 || appValidation.length > 0 || validation}
|
||||
color="primary"
|
||||
onClick={() => {
|
||||
setOpenApiError("");
|
||||
|
||||
@@ -11,6 +11,7 @@ import { useNavigate, Link, useParams } from "react-router-dom";
|
||||
//import { useAlert
|
||||
import { ToastContainer, toast } from "react-toastify"
|
||||
import Draggable from "react-draggable";
|
||||
import DashboardBarchart, { LoadStats } from '../components/DashboardBarchart.jsx';
|
||||
|
||||
import {
|
||||
Autocomplete,
|
||||
@@ -18,6 +19,8 @@ import {
|
||||
TextField,
|
||||
IconButton,
|
||||
Button,
|
||||
Select,
|
||||
MenuItem,
|
||||
Typography,
|
||||
Grid,
|
||||
Paper,
|
||||
@@ -146,28 +149,8 @@ const inputdata = [
|
||||
const LineChartWrapper = ({keys, height, width}) => {
|
||||
const [hovered, setHovered] = useState("");
|
||||
|
||||
//console.log("Date: ", new Date("2019-11-14T08:00:00.000Z"))
|
||||
console.log("Keys: ", keys)
|
||||
var inputdata = keys.data
|
||||
|
||||
/*
|
||||
const inputdata = [{
|
||||
"key": "Intel",
|
||||
"data": [
|
||||
{ key: new Date('11/22/2019'), data: 3, metadata: {color: "orange", "name": "Intel"}},
|
||||
{ key: new Date('11/24/2019'), data: 8, metadata: {color: "orange", "name": "Intel"}},
|
||||
{ key: new Date('11/29/2019'), data: 2, metadata: {color: "orange", "name": "Intel"}},
|
||||
]},
|
||||
{
|
||||
"key": "Popper",
|
||||
"data": [
|
||||
{ key: new Date('11/24/2019'), data: 9, },
|
||||
{ key: new Date('11/29/2019'), data: 3, },
|
||||
]
|
||||
}
|
||||
]
|
||||
*/
|
||||
|
||||
return (
|
||||
<div style={{}}>
|
||||
<Typography variant="h6" style={{marginBotton: 15}}>
|
||||
@@ -211,7 +194,6 @@ const LineChartWrapper = ({keys, height, width}) => {
|
||||
offset: '5px, 5px'
|
||||
}}
|
||||
content={(data, color) => {
|
||||
console.log("DATA: ", data)
|
||||
const name = data.metadata !== undefined && data.metadata.name !== undefined ? data.metadata.name : "No"
|
||||
|
||||
return (
|
||||
@@ -350,12 +332,35 @@ const Dashboard = (props) => {
|
||||
const [frameworkData, setFrameworkData] = useState(undefined);
|
||||
|
||||
const [widgetData, setWidgetData] = useState([]);
|
||||
const [newWidgetData, setNewWidgetData] = useState([]);
|
||||
|
||||
const [, setUpdate] = useState(0);
|
||||
|
||||
let navigate = useNavigate();
|
||||
const isCloud =
|
||||
window.location.host === "localhost:3002" ||
|
||||
window.location.host === "shuffler.io";
|
||||
|
||||
useEffect(() => {
|
||||
const widgetnames = ["app_executions_cloud"]
|
||||
for (let widgetkey in widgetnames) {
|
||||
const widgetName = widgetnames[widgetkey]
|
||||
|
||||
console.log("NAME: ", widgetName)
|
||||
|
||||
const resp = LoadStats(globalUrl, widgetName)
|
||||
if (resp !== undefined) {
|
||||
resp.then((data) => {
|
||||
console.log("Got data in parent: ", data)
|
||||
if (data === undefined) {
|
||||
} else {
|
||||
newWidgetData.push(data)
|
||||
setNewWidgetData(newWidgetData)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedUsecaseCategory.length === 0) {
|
||||
@@ -368,6 +373,7 @@ const Dashboard = (props) => {
|
||||
}
|
||||
}, [selectedUsecaseCategory])
|
||||
|
||||
|
||||
const checkSelectedParams = () => {
|
||||
const urlSearchParams = new URLSearchParams(window.location.search)
|
||||
const params = Object.fromEntries(urlSearchParams.entries())
|
||||
@@ -409,23 +415,22 @@ const Dashboard = (props) => {
|
||||
}, [usecases])
|
||||
|
||||
const getWidget = (dashboard, widget) => {
|
||||
fetch(`${globalUrl}/api/v1/dashboards/${dashboard}/widgets/${widget}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
console.log("Status not 200 for framework!");
|
||||
}
|
||||
fetch(`${globalUrl}/api/v1/dashboards/${dashboard}/widgets/${widget}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
console.log("Status not 200 for framework!");
|
||||
}
|
||||
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
console.log("Resp: ", responseJson)
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success === false) {
|
||||
if (responseJson.reason !== undefined) {
|
||||
//toast("Failed loading: " + responseJson.reason)
|
||||
@@ -441,14 +446,12 @@ const Dashboard = (props) => {
|
||||
}
|
||||
|
||||
const foundWidget = widgetData.findIndex(data => data.title === widget)
|
||||
console.log("Found: ", foundWidget)
|
||||
if (foundWidget !== undefined && foundWidget !== null && foundWidget >= 0) {
|
||||
widgetData[foundWidget] = tmpdata
|
||||
} else {
|
||||
widgetData.push(tmpdata)
|
||||
}
|
||||
|
||||
console.log("Data: ", widgetData)
|
||||
setWidgetData(widgetData)
|
||||
}
|
||||
})
|
||||
@@ -480,7 +483,7 @@ const Dashboard = (props) => {
|
||||
useEffect(() => {
|
||||
getWidget("main", "Overall")
|
||||
getWidget("main", "Overall2")
|
||||
}, []);
|
||||
}, [])
|
||||
|
||||
const fetchdata = (stats_id) => {
|
||||
fetch(globalUrl + "/api/v1/stats/" + stats_id, {
|
||||
@@ -646,7 +649,6 @@ const Dashboard = (props) => {
|
||||
stats["workflow_executions"].data !== undefined
|
||||
) {
|
||||
setStatsRan(true);
|
||||
//console.log("NEW DATA?: ", stats)
|
||||
console.log("SET WORKFLOW: ", stats["workflow_executions"]);
|
||||
//var curday = startDate.getDate()
|
||||
|
||||
@@ -729,6 +731,100 @@ const Dashboard = (props) => {
|
||||
</div>
|
||||
) : null;
|
||||
|
||||
const WidgetController = (props) => {
|
||||
const { data, index, availableStats, } = props
|
||||
const [hovering, setHovering] = useState(false)
|
||||
|
||||
const newname = data.key !== undefined ? data.key.replaceAll("_", " ") : ""
|
||||
|
||||
console.log("KEYDATA: ", data)
|
||||
|
||||
const loadNewStats = (newkey) => {
|
||||
const resp = LoadStats(globalUrl, newkey)
|
||||
if (resp !== undefined) {
|
||||
resp.then((respdata) => {
|
||||
if (respdata === undefined || respdata === null) {
|
||||
toast("Failed to laod data. Please try again, or contact support@shuffler.io if this persists.")
|
||||
} else {
|
||||
newWidgetData[index] = respdata
|
||||
setNewWidgetData(newWidgetData)
|
||||
|
||||
setUpdate(Math.random())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Draggable>
|
||||
<Paper
|
||||
style={{
|
||||
height: "100%", width: "100%", maxWidth: 500, margin: 15, padding: "15px 15px 15px 15px", textAlign: "left",
|
||||
backgroundColor: hovering ? theme.palette.inputColor : theme.palette.backgroundColor,
|
||||
cursor: hovering ? "pointer" : "default",
|
||||
}}
|
||||
onMouseEnter={() => {
|
||||
setHovering(true)
|
||||
}}
|
||||
onMouseLeave={() => {
|
||||
setHovering(false)
|
||||
}}
|
||||
>
|
||||
<div style={{display: "flex", justifyContent: "space-between", alignItems: "center",}}>
|
||||
<Typography variant="h6">
|
||||
{newname}
|
||||
</Typography>
|
||||
|
||||
{data.available_keys === undefined || data.available_keys === null || data.available_keys.length === 0 ? null :
|
||||
<Select
|
||||
MenuProps={{
|
||||
disableScrollLock: true,
|
||||
}}
|
||||
labelId="Response Action"
|
||||
value={data.key}
|
||||
SelectDisplayProps={{
|
||||
style: {
|
||||
},
|
||||
}}
|
||||
fullWidth
|
||||
onChange={(e) => {
|
||||
loadNewStats(e.target.value)
|
||||
}}
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
color: "white",
|
||||
height: 40,
|
||||
maxWidth: 150,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
}}
|
||||
>
|
||||
{data.available_keys.map((foundKey, index) => {
|
||||
const parsedKeyName = foundKey.replaceAll("_", " ")
|
||||
|
||||
return (
|
||||
<MenuItem
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
color: "white",
|
||||
}}
|
||||
value={foundKey}
|
||||
>
|
||||
<em>{parsedKeyName}</em>
|
||||
</MenuItem>
|
||||
)
|
||||
})}
|
||||
</Select>
|
||||
}
|
||||
</div>
|
||||
<DashboardBarchart
|
||||
timelineData={data}
|
||||
height={50}
|
||||
/>
|
||||
</Paper>
|
||||
</Draggable>
|
||||
)
|
||||
}
|
||||
|
||||
const data = (
|
||||
<div className="content" style={{width: 1000, margin: "auto", paddingBottom: 200, textAlign: "center",}}>
|
||||
<div style={{width: 500, margin: "auto"}}>
|
||||
@@ -739,12 +835,25 @@ const Dashboard = (props) => {
|
||||
: null}
|
||||
</div>
|
||||
|
||||
{widgetData === undefined || widgetData === null || widgetData === [] || widgetData.length === 0 ? null :
|
||||
{/*widgetData === undefined || widgetData === null || widgetData === [] || widgetData.length === 0 ? null :
|
||||
<Draggable>
|
||||
<Paper style={{height: 350, width: 500, padding: "15px 15px 15px 15px", }}>
|
||||
<LineChartWrapper keys={widgetData[0]} height={280} width={470} />
|
||||
</Paper>
|
||||
</Draggable>
|
||||
*/}
|
||||
|
||||
{newWidgetData === undefined || newWidgetData === null || newWidgetData === [] ? null :
|
||||
newWidgetData.map((data, index) => {
|
||||
|
||||
return (
|
||||
<WidgetController
|
||||
key={index}
|
||||
index={index}
|
||||
data={data}
|
||||
/>
|
||||
)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { useEffect, useLayoutEffect, useRef, useState } from "react"
|
||||
import { toast } from 'react-toastify';
|
||||
import Markdown from 'react-markdown'
|
||||
import theme from '../theme.jsx';
|
||||
import ReactJson from "react-json-view";
|
||||
import ReactJson from "react-json-view-ssr";
|
||||
import { isMobile } from "react-device-detect";
|
||||
import { BrowserView, MobileView } from "react-device-detect";
|
||||
import { useParams, useNavigate, Link } from "react-router-dom";
|
||||
@@ -152,9 +152,19 @@ export const OuterLink = (props) => {
|
||||
|
||||
|
||||
export const Img = (props) => {
|
||||
var height = "auto"
|
||||
var width = 750
|
||||
if (props.height !== undefined && props.height !== null) {
|
||||
height = props.height
|
||||
}
|
||||
|
||||
if (props.width !== undefined && props.width !== null) {
|
||||
width = props.width
|
||||
}
|
||||
|
||||
return(
|
||||
<img
|
||||
style={{border: "1px solid rgba(255,255,255,0.3)", borderRadius: theme.palette.borderRadius, width: 750, maxWidth: "100%", marginTop: 15, marginBottom: 15, }}
|
||||
style={{border: "1px solid rgba(255,255,255,0.3)", borderRadius: theme.palette.borderRadius, width: width, maxWidth: width, margin: "auto", marginTop: 10, marginBottom: 10, }}
|
||||
alt={props.alt}
|
||||
src={props.src}
|
||||
/>
|
||||
|
||||
+367
-382
@@ -7,15 +7,17 @@ import { CodeHandler, Img, OuterLink, } from "../views/Docs.jsx";
|
||||
import { useNavigate, Link, useParams } from "react-router-dom";
|
||||
import { validateJson, GetIconInfo } from "./Workflows.jsx";
|
||||
import EditWorkflow from "../components/EditWorkflow.jsx"
|
||||
import { ToastContainer, toast } from "react-toastify"
|
||||
import { toast } from "react-toastify"
|
||||
import { makeStyles } from '@mui/material/styles';
|
||||
import { useInterval } from "react-powerhooks";
|
||||
import { isMobile } from "react-device-detect";
|
||||
import Markdown from "react-markdown";
|
||||
import theme from '../theme.jsx';
|
||||
import rehypeRaw from "rehype-raw";
|
||||
|
||||
import {
|
||||
Tooltip,
|
||||
Tooltip,
|
||||
Select,
|
||||
IconButton,
|
||||
CircularProgress,
|
||||
TextField,
|
||||
@@ -28,6 +30,7 @@ import {
|
||||
Dialog,
|
||||
DialogTitle,
|
||||
DialogContent,
|
||||
MenuItem,
|
||||
} from '@mui/material';
|
||||
|
||||
import {
|
||||
@@ -45,7 +48,7 @@ const bodyDivStyle = {
|
||||
width: isMobile? "100%":"500px",
|
||||
position: "relative",
|
||||
|
||||
marginTop: 25,
|
||||
paddingBottom: 250,
|
||||
}
|
||||
|
||||
|
||||
@@ -71,7 +74,7 @@ const RunWorkflow = (defaultprops) => {
|
||||
|
||||
const boxStyle = {
|
||||
color: "white",
|
||||
padding: 50,
|
||||
padding: "25px 50px 50px 50px",
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
marginBottom: 150,
|
||||
borderRadius: 25,
|
||||
@@ -102,6 +105,17 @@ const RunWorkflow = (defaultprops) => {
|
||||
const [executionInfo, setExecutionInfo] = useState("");
|
||||
|
||||
const handleValidateForm = (executionArgument) => {
|
||||
// Check if every field exists
|
||||
if (executionArgument === undefined || executionArgument === null) {
|
||||
return true
|
||||
}
|
||||
|
||||
for (var key in executionArgument) {
|
||||
if (executionArgument[key] === undefined || executionArgument[key] === null || executionArgument[key] === "") {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -124,7 +138,10 @@ const RunWorkflow = (defaultprops) => {
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
toast.success("Saved workflow")
|
||||
if (responseJson.success === false) {
|
||||
toast.error("Failed saving workflow. Please try again.")
|
||||
}
|
||||
//toast.success("Saved workflow")
|
||||
})
|
||||
.catch((error) => {
|
||||
toast.error("Save workflow error: " + error)
|
||||
@@ -197,178 +214,25 @@ const RunWorkflow = (defaultprops) => {
|
||||
*/}
|
||||
|
||||
{executionData.result !== undefined && executionData.result !== null && executionData.result.length > 0 ?
|
||||
<Typography variant="h6">
|
||||
{executionData.result}
|
||||
</Typography>
|
||||
<div style={{marginTop: 20, }}>
|
||||
<Divider />
|
||||
<Markdown
|
||||
components={{
|
||||
img: Img,
|
||||
code: CodeHandler,
|
||||
a: OuterLink,
|
||||
}}
|
||||
id="markdown_wrapper"
|
||||
escapeHtml={false}
|
||||
style={{
|
||||
maxWidth: "100%", minWidth: "100%",
|
||||
}}
|
||||
>
|
||||
{executionData.result}
|
||||
</Markdown>
|
||||
</div>
|
||||
: null}
|
||||
|
||||
{/*executionData.results.map((data, index) => {
|
||||
if (executionData.results.length !== 1 && (data.status === "SKIPPED")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// FIXME: The latter replace doens't really work if ' is used in a string
|
||||
var showResult = data.result.trim();
|
||||
const validate = validateJson(showResult);
|
||||
|
||||
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" : yellow;
|
||||
|
||||
var imgSrc = curapp === undefined ? "" : curapp.large_image;
|
||||
if (
|
||||
imgSrc.length === 0 &&
|
||||
workflow.actions !== undefined &&
|
||||
workflow.actions !== null
|
||||
) {
|
||||
// Look for the node in the workflow
|
||||
const action = workflow.actions.find(
|
||||
(action) => action.id === data.action.id
|
||||
);
|
||||
if (action !== undefined && action !== null) {
|
||||
imgSrc = action.large_image;
|
||||
}
|
||||
}
|
||||
|
||||
var actionimg =
|
||||
curapp === null ? null : (
|
||||
<img
|
||||
alt={data.action.app_name}
|
||||
src={imgSrc}
|
||||
style={{
|
||||
marginRight: 20,
|
||||
width: imgsize,
|
||||
height: imgsize,
|
||||
border: `2px solid ${statusColor}`,
|
||||
borderRadius:
|
||||
executionData.start === data.action.id ? 25 : 5,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
if (data.action.app_name === "shuffle-subflow") {
|
||||
//const parsedImage = triggers[2].large_image;
|
||||
//actionimg = (
|
||||
// <img
|
||||
// alt={"Shuffle Subflow"}
|
||||
// src={parsedImage}
|
||||
// style={{
|
||||
// marginRight: 20,
|
||||
// width: imgsize,
|
||||
// height: imgsize,
|
||||
// border: `2px solid ${statusColor}`,
|
||||
// borderRadius:
|
||||
// executionData.start === data.action.id ? 25 : 5,
|
||||
// }}
|
||||
// />
|
||||
//);
|
||||
} else if (data.action.app_name === "User Input") {
|
||||
//actionimg = (
|
||||
// <img
|
||||
// alt={"Shuffle Subflow"}
|
||||
// src={triggers[3].large_image}
|
||||
// style={{
|
||||
// marginRight: 20,
|
||||
// width: imgsize,
|
||||
// height: imgsize,
|
||||
// border: `2px solid ${statusColor}`,
|
||||
// borderRadius:
|
||||
// executionData.start === data.action.id ? 25 : 5,
|
||||
// }}
|
||||
// />
|
||||
//);
|
||||
}
|
||||
|
||||
if (validate.valid && typeof validate.result === "string") {
|
||||
validate.result = JSON.parse(validate.result);
|
||||
}
|
||||
|
||||
if (validate.valid && typeof validate.result === "object") {
|
||||
if (
|
||||
validate.result.result !== undefined &&
|
||||
validate.result.result !== null
|
||||
) {
|
||||
try {
|
||||
validate.result.result = JSON.parse(validate.result.result);
|
||||
} catch (e) {
|
||||
//console.log("ERROR PARSING: ", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var similarActionsView = null
|
||||
if (data.similar_actions !== undefined && data.similar_actions !== null) {
|
||||
var minimumMatch = 85
|
||||
var matching_executions = []
|
||||
if (data.similar_actions !== undefined && data.similar_actions !== null) {
|
||||
for (let [k,kval] in Object.entries(data.similar_actions)){
|
||||
if (data.similar_actions.hasOwnProperty(k)) {
|
||||
if (data.similar_actions[k].similarity > minimumMatch) {
|
||||
matching_executions.push(data.similar_actions[k].execution_id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (matching_executions.length !== 0) {
|
||||
var parsed_url = matching_executions.join(",")
|
||||
|
||||
similarActionsView =
|
||||
<Tooltip
|
||||
color="primary"
|
||||
title="See executions with similar results (not identical)"
|
||||
placement="top"
|
||||
style={{ zIndex: 50000, marginLeft: 50, }}
|
||||
>
|
||||
<IconButton
|
||||
style={{
|
||||
marginTop: "auto",
|
||||
marginBottom: "auto",
|
||||
height: 30,
|
||||
paddingLeft: 0,
|
||||
width: 30,
|
||||
}}
|
||||
onClick={() => {
|
||||
//navigate(`?execution_highlight=${parsed_url}`)
|
||||
}}
|
||||
>
|
||||
<PreviewIcon style={{ color: "rgba(255,255,255,0.5)" }} />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
style={{
|
||||
marginBottom: 20,
|
||||
border:
|
||||
data.action.sub_action === true
|
||||
? "1px solid rgba(255,255,255,0.3)"
|
||||
: "1px solid rgba(255,255,255, 0.3)",
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
padding: "15px 10px 10px 10px",
|
||||
overflow: "hidden",
|
||||
}}
|
||||
onMouseOver={() => {
|
||||
}}
|
||||
onMouseOut={() => {
|
||||
}}
|
||||
>
|
||||
<div style={{ marginBottom: 5, display: "flex" }}>
|
||||
<Typography variant="body1">
|
||||
<b>Status </b>
|
||||
</Typography>
|
||||
<Typography variant="body1" color="textSecondary" style={{ marginRight: 15, }}>
|
||||
{data.status}
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})*/}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -388,7 +252,7 @@ const RunWorkflow = (defaultprops) => {
|
||||
|
||||
var data = {
|
||||
"execution_argument": executionArgument,
|
||||
"execution_source": "questions",
|
||||
"execution_source": "form",
|
||||
}
|
||||
|
||||
if (workflow.input_questions !== undefined && workflow.input_questions !== null && workflow.input_questions.length > 0) {
|
||||
@@ -467,6 +331,10 @@ const RunWorkflow = (defaultprops) => {
|
||||
}
|
||||
}
|
||||
|
||||
if (response.status === 401 || response.status === 403) {
|
||||
toast("This Form is not available for you to run. If you this is an error, contact support@shuffler.io with a link to this form")
|
||||
}
|
||||
|
||||
return response.json()
|
||||
})
|
||||
.then(responseJson => {
|
||||
@@ -509,7 +377,9 @@ const RunWorkflow = (defaultprops) => {
|
||||
}
|
||||
|
||||
const getWorkflow = (workflow_id, selectedNode) => {
|
||||
fetch(globalUrl + "/api/v1/workflows/" + workflow_id, {
|
||||
const url = `${globalUrl}/api/v1/workflows/${workflow_id}`
|
||||
|
||||
fetch(url, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
@@ -522,6 +392,10 @@ const RunWorkflow = (defaultprops) => {
|
||||
console.log("Status not 200 for workflows :O!");
|
||||
}
|
||||
|
||||
if (response.status === 401 || response.status === 403) {
|
||||
toast("This Form is not available to you. If you think this is an error, please contact support@shuffler.io with the URL.")
|
||||
}
|
||||
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
@@ -543,16 +417,24 @@ const RunWorkflow = (defaultprops) => {
|
||||
}
|
||||
|
||||
if (responseJson.input_questions !== undefined && responseJson.input_questions !== null && responseJson.input_questions.length > 0) {
|
||||
|
||||
var newexec = {}
|
||||
for (let questionkey in responseJson.input_questions) {
|
||||
const question = responseJson.input_questions[questionkey]
|
||||
newexec[question.value] = ""
|
||||
|
||||
var multiChoiceOptions = question.value !== undefined && question.value !== null && question.value.length > 0 && question.value.includes(";") ? question.value.split(";") : []
|
||||
if (multiChoiceOptions.length > 1) {
|
||||
newexec[multiChoiceOptions[0]] = ""
|
||||
} else {
|
||||
newexec[question.value] = ""
|
||||
}
|
||||
}
|
||||
|
||||
setExecutionArgument(newexec)
|
||||
}
|
||||
|
||||
if (selectedNode !== undefined && selectedNode !== null && selectedNode.length > 0) {
|
||||
|
||||
var found = false
|
||||
for (var actionkey in responseJson.actions) {
|
||||
if (responseJson.actions[actionkey].id === selectedNode) {
|
||||
@@ -567,6 +449,7 @@ const RunWorkflow = (defaultprops) => {
|
||||
if (responseJson.triggers[triggerkey].id !== selectedNode) {
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
setFoundSourcenode(responseJson.triggers[triggerkey])
|
||||
|
||||
@@ -864,215 +747,277 @@ const RunWorkflow = (defaultprops) => {
|
||||
const basedata =
|
||||
<div style={bodyDivStyle}>
|
||||
<Paper style={boxStyle}>
|
||||
{workflow.input_markdown !== undefined && workflow.input_markdown !== null && workflow.input_markdown.length > 0 ?
|
||||
<div style={{marginBottom: 20, }}>
|
||||
<Markdown
|
||||
components={{
|
||||
img: Img,
|
||||
code: CodeHandler,
|
||||
a: OuterLink,
|
||||
}}
|
||||
id="markdown_wrapper"
|
||||
escapeHtml={false}
|
||||
style={{
|
||||
maxWidth: "100%", minWidth: "100%",
|
||||
}}
|
||||
>
|
||||
{workflow.input_markdown}
|
||||
</Markdown>
|
||||
</div>
|
||||
: null}
|
||||
|
||||
<form onSubmit={(e) => {onSubmit(e)}} style={{margin: "50px 0px 15px 0px",}}>
|
||||
{workflow.input_markdown !== undefined && workflow.input_markdown !== null && workflow.input_markdown.length > 0 ? null :
|
||||
<div>
|
||||
<img
|
||||
alt={workflow.name}
|
||||
src={image}
|
||||
style={{
|
||||
marginRight: 20,
|
||||
width: 100,
|
||||
height: 100,
|
||||
border: `2px solid ${green}`,
|
||||
borderRadius: 50,
|
||||
position: "absolute",
|
||||
top: -50,
|
||||
left: 200,
|
||||
}}
|
||||
/>
|
||||
|
||||
<Typography variant="h6" style={{marginBottom: 10, marginTop: 50, textAlign: "center", }}>
|
||||
{organization}
|
||||
{workflow.id === undefined || workflow.id === null ?
|
||||
<div style={{paddingTop: 150, marginTop: 150, width: 250, itemAlign: "center", textAlign: "center", margin: "auto", }}>
|
||||
<CircularProgress />
|
||||
<Typography variant="body1" style={{marginTop: 20, }}>
|
||||
Loding Form Details...
|
||||
</Typography>
|
||||
<Divider style={{marginTop: 20, marginBottom: 20, }}/>
|
||||
</div>
|
||||
:
|
||||
<div>
|
||||
|
||||
{disabledButtons && message.length > 0 ? null :
|
||||
<Typography color="textSecondary" style={{textAlign: "center", }}>
|
||||
{message}
|
||||
{workflow.input_markdown !== undefined && workflow.input_markdown !== null && workflow.input_markdown.length > 0 ?
|
||||
<div style={{marginBottom: 20, }}>
|
||||
<Markdown
|
||||
components={{
|
||||
img: Img,
|
||||
code: CodeHandler,
|
||||
a: OuterLink,
|
||||
}}
|
||||
id="markdown_wrapper"
|
||||
escapeHtml={false}
|
||||
style={{
|
||||
maxWidth: "100%", minWidth: "100%",
|
||||
}}
|
||||
rehypePlugins={[rehypeRaw]}
|
||||
>
|
||||
{workflow.input_markdown}
|
||||
</Markdown>
|
||||
</div>
|
||||
: null}
|
||||
|
||||
<form onSubmit={(e) => {onSubmit(e)}} style={{margin: "25px 0px 15px 0px",}}>
|
||||
{workflow.input_markdown !== undefined && workflow.input_markdown !== null && workflow.input_markdown.length > 0 ? null :
|
||||
<div>
|
||||
<img
|
||||
alt={workflow.name}
|
||||
src={image}
|
||||
style={{
|
||||
marginRight: 20,
|
||||
width: 100,
|
||||
height: 100,
|
||||
border: `2px solid ${green}`,
|
||||
borderRadius: 50,
|
||||
position: "absolute",
|
||||
top: -50,
|
||||
left: 200,
|
||||
}}
|
||||
/>
|
||||
|
||||
<Typography variant="h6" style={{marginBottom: 10, marginTop: 50, textAlign: "center", }}>
|
||||
{organization}
|
||||
</Typography>
|
||||
}
|
||||
<Divider style={{marginTop: 20, marginBottom: 20, }}/>
|
||||
|
||||
{answer !== undefined && answer !== null ? null :
|
||||
<Typography variant="h6" style={{marginBottom: 15, textAlign: "center", }}><b>{workflow.name}</b></Typography>
|
||||
}
|
||||
|
||||
{workflowQuestion.length > 0 ?
|
||||
<div style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
padding: 20,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
marginBottom: 35,
|
||||
marginTop: 30,
|
||||
}}>
|
||||
<Typography variant="body1" style={{ marginRight: 15, textAlign: "center", whiteSpace: "pre-line", }}>
|
||||
{workflowQuestion}
|
||||
{disabledButtons && message.length > 0 ? null :
|
||||
<Typography color="textSecondary" style={{textAlign: "center", }}>
|
||||
{message}
|
||||
</Typography>
|
||||
</div>
|
||||
: null}
|
||||
}
|
||||
|
||||
</div>
|
||||
}
|
||||
|
||||
{workflow.input_questions !== undefined && workflow.input_questions !== null && workflow.input_questions.length > 0 ?
|
||||
<div style={{marginBottom: 5, }}>
|
||||
{workflow.input_questions.map((question, index) => {
|
||||
return (
|
||||
<div style={{marginBottom: 5}}>
|
||||
{question.name}
|
||||
<TextField
|
||||
color="primary"
|
||||
style={{backgroundColor: theme.palette.inputColor, marginTop: 5, }}
|
||||
label={question.value}
|
||||
required
|
||||
{answer !== undefined && answer !== null ? null :
|
||||
<Typography variant="h6" style={{marginBottom: 15, textAlign: "center", }}><b>{workflow.name}</b></Typography>
|
||||
}
|
||||
|
||||
disabled={disabledButtons}
|
||||
fullWidth={true}
|
||||
placeholder=""
|
||||
id="emailfield"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
onBlur={(e) => {
|
||||
//setExecutionArgument(e.target.value)
|
||||
executionArgument[question.value] = e.target.value
|
||||
setUpdate(Math.random())
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
:
|
||||
answer !== undefined && answer !== null ? null :
|
||||
<span>
|
||||
Runtime Argument
|
||||
<div style={{marginBottom: 5}}>
|
||||
<TextField
|
||||
color="primary"
|
||||
style={{backgroundColor: theme.palette.inputColor, marginTop: 5, }}
|
||||
multiLine
|
||||
maxRows={2}
|
||||
InputProps={{
|
||||
style:{
|
||||
height: "50px",
|
||||
color: "white",
|
||||
fontSize: "1em",
|
||||
},
|
||||
}}
|
||||
fullWidth={true}
|
||||
placeholder=""
|
||||
id="emailfield"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
onChange={(e) => {
|
||||
setExecutionArgument(e.target.value)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</span>
|
||||
}
|
||||
|
||||
{executionRunning ?
|
||||
<span style={{width: 50, height: 50, margin: "auto", alignItems: "center", justifyContent: "center", textAlign: "center", }}>
|
||||
<CircularProgress style={{marginTop: 20, marginBottom: 20, marginLeft: 185, }}/>
|
||||
|
||||
{executionData.status !== undefined && executionData.status !== null && executionData.status !== "" ?
|
||||
<Typography variant="body2" style={{margin: "auto", marginTop: 20, marginBottom: 20, textAlign: "center", alignItem: "center", }} color="textSecondary">
|
||||
Status: {executionData.status}
|
||||
</Typography>
|
||||
{workflowQuestion.length > 0 ?
|
||||
<div style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
padding: 20,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
marginBottom: 35,
|
||||
marginTop: 30,
|
||||
}}>
|
||||
<Typography variant="body1" style={{ marginRight: 15, textAlign: "center", whiteSpace: "pre-line", }}>
|
||||
{workflowQuestion}
|
||||
</Typography>
|
||||
</div>
|
||||
: null}
|
||||
</span>
|
||||
:
|
||||
((answer !== undefined && answer !== null) || (foundSourcenode !== undefined && foundSourcenode !== null)) ?
|
||||
<span style={{marginTop: 20, }}>
|
||||
|
||||
{disabledButtons && message.length > 0 ?
|
||||
<Typography variant="body1" style={{textAlign: "center", marginTop: 30, marginBottom: 20, }}>
|
||||
{message}. You may close this window.
|
||||
</Typography>
|
||||
:
|
||||
<Typography variant="body1" style={{textAlign: "center", marginTop: 30, marginBottom: 20, }}>
|
||||
{disabledButtons ? "Answered. You may close this window." : ""}
|
||||
</Typography>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
{workflow.input_questions !== undefined && workflow.input_questions !== null && workflow.input_questions.length > 0 ?
|
||||
<div style={{marginBottom: 5, }}>
|
||||
{workflow.input_questions.map((question, index) => {
|
||||
|
||||
{disabledButtons ? null :
|
||||
<Typography variant="body2" color="textSecondary" style={{textAlign: "center", marginTop: 10, }}>
|
||||
What do you want to do?
|
||||
</Typography>
|
||||
}
|
||||
<div fullWidth style={{width: "100%", marginTop: 10, marginBottom: 10, display: "flex", }}>
|
||||
<Button fullWidth id="continue_execution" variant="contained" disabled={disabledButtons} color="primary" style={{flex: 1,}} onClick={() => {
|
||||
onSubmit(null, execution_id, authorization, true)
|
||||
// Multiple choice checks for semicolon-splits
|
||||
var multiChoiceOptions = question.value !== undefined && question.value !== null && question.value.length > 0 && question.value.includes(";") ? question.value.split(";") : []
|
||||
// Remove empty keys from array
|
||||
multiChoiceOptions = multiChoiceOptions.filter(function(e) { return e !== "" })
|
||||
if (multiChoiceOptions.length > 1 && (executionArgument[multiChoiceOptions[0]] === undefined || executionArgument[multiChoiceOptions[0]] === null || executionArgument[multiChoiceOptions[0]] === "")) {
|
||||
// Set the first item to be default
|
||||
executionArgument[multiChoiceOptions[0]] = multiChoiceOptions[1]
|
||||
}
|
||||
|
||||
setButtonClicked("FINISHED")
|
||||
setExecutionData({
|
||||
status: "FINISHED",
|
||||
})
|
||||
}}>Continue</Button>
|
||||
<Typography variant="body1" style={{marginLeft: 3, marginRight: 3, marginTop: 3, }}>
|
||||
or
|
||||
</Typography>
|
||||
<Button fullWidth id="abort_execution" variant="contained" color="primary" disabled={disabledButtons} style={{ flex: 1, }} onClick={() => {
|
||||
onSubmit(null, execution_id, authorization, false)
|
||||
return (
|
||||
<div style={{marginBottom: 10}} key={index}>
|
||||
{question.name}
|
||||
|
||||
setButtonClicked("ABORTED")
|
||||
setExecutionData({
|
||||
status: "ABORTED",
|
||||
})
|
||||
}}>Stop</Button>
|
||||
{multiChoiceOptions.length > 1 ?
|
||||
<Select
|
||||
fullWidth
|
||||
required
|
||||
label={multiChoiceOptions[0]}
|
||||
value={executionArgument[multiChoiceOptions[0]]}
|
||||
onChange={(e) => {
|
||||
const curQuestion = multiChoiceOptions[0]
|
||||
executionArgument[curQuestion] = e.target.value
|
||||
setUpdate(Math.random())
|
||||
}}
|
||||
>
|
||||
|
||||
{multiChoiceOptions.map((option, menuIndex) => {
|
||||
if (index === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<MenuItem
|
||||
key={menuIndex}
|
||||
value={option}
|
||||
>
|
||||
{option}
|
||||
</MenuItem>
|
||||
)
|
||||
})}
|
||||
|
||||
|
||||
</Select>
|
||||
:
|
||||
<TextField
|
||||
color="primary"
|
||||
style={{backgroundColor: theme.palette.inputColor, marginTop: 5, }}
|
||||
label={question.value}
|
||||
required
|
||||
|
||||
disabled={disabledButtons}
|
||||
fullWidth={true}
|
||||
placeholder=""
|
||||
id="emailfield"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
onChange={(e) => {
|
||||
executionArgument[question.value] = e.target.value
|
||||
setExecutionArgument(executionArgument)
|
||||
setUpdate(Math.random())
|
||||
}}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
:
|
||||
answer !== undefined && answer !== null ? null :
|
||||
<span>
|
||||
Runtime Argument
|
||||
<div style={{marginBottom: 5}}>
|
||||
<TextField
|
||||
color="primary"
|
||||
style={{backgroundColor: theme.palette.inputColor, marginTop: 5, }}
|
||||
multiLine
|
||||
maxRows={2}
|
||||
InputProps={{
|
||||
style:{
|
||||
height: "50px",
|
||||
color: "white",
|
||||
fontSize: "1em",
|
||||
},
|
||||
}}
|
||||
fullWidth={true}
|
||||
placeholder=""
|
||||
id="emailfield"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
onChange={(e) => {
|
||||
setExecutionArgument(e.target.value)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</span>
|
||||
:
|
||||
<div style={{display: "flex", marginTop: "15px"}}>
|
||||
<Button variant="contained" type="submit" color="primary" fullWidth disabled={!handleValidateForm(executionArgument) || executionLoading}>
|
||||
{executionLoading ?
|
||||
<CircularProgress color="secondary" style={{color: "white",}} /> : "Submit"}
|
||||
</Button>
|
||||
}
|
||||
|
||||
{executionRunning ?
|
||||
<span style={{width: 50, height: 50, margin: "auto", alignItems: "center", justifyContent: "center", textAlign: "center", }}>
|
||||
<CircularProgress style={{marginTop: 20, marginBottom: 20, marginLeft: 185, }}/>
|
||||
|
||||
{executionData.status !== undefined && executionData.status !== null && executionData.status !== "" ?
|
||||
<Typography variant="body2" style={{margin: "auto", marginTop: 20, marginBottom: 20, textAlign: "center", alignItem: "center", }} color="textSecondary">
|
||||
Status: {executionData.status}
|
||||
</Typography>
|
||||
: null}
|
||||
</span>
|
||||
:
|
||||
((answer !== undefined && answer !== null) || (foundSourcenode !== undefined && foundSourcenode !== null)) ?
|
||||
<span style={{marginTop: 20, }}>
|
||||
|
||||
{disabledButtons && message.length > 0 ?
|
||||
<Typography variant="body1" style={{textAlign: "center", marginTop: 30, marginBottom: 20, }}>
|
||||
{message}. You may close this window.
|
||||
</Typography>
|
||||
:
|
||||
<Typography variant="body1" style={{textAlign: "center", marginTop: 30, marginBottom: 20, }}>
|
||||
{disabledButtons ? "Answered. You may close this window." : ""}
|
||||
</Typography>
|
||||
}
|
||||
|
||||
{disabledButtons ? null :
|
||||
<Typography variant="body2" color="textSecondary" style={{textAlign: "center", marginTop: 10, }}>
|
||||
What do you want to do?
|
||||
</Typography>
|
||||
}
|
||||
<div fullWidth style={{width: "100%", marginTop: 10, marginBottom: 10, display: "flex", }}>
|
||||
<Button fullWidth id="continue_execution" variant="contained" disabled={disabledButtons} color="primary" style={{flex: 1,}} onClick={() => {
|
||||
onSubmit(null, execution_id, authorization, true)
|
||||
|
||||
setButtonClicked("FINISHED")
|
||||
setExecutionData({
|
||||
status: "FINISHED",
|
||||
})
|
||||
}}>Continue</Button>
|
||||
<Typography variant="body1" style={{marginLeft: 3, marginRight: 3, marginTop: 3, }}>
|
||||
or
|
||||
</Typography>
|
||||
<Button fullWidth id="abort_execution" variant="contained" color="primary" disabled={disabledButtons} style={{ flex: 1, }} onClick={() => {
|
||||
onSubmit(null, execution_id, authorization, false)
|
||||
|
||||
setButtonClicked("ABORTED")
|
||||
setExecutionData({
|
||||
status: "ABORTED",
|
||||
})
|
||||
}}>Stop</Button>
|
||||
</div>
|
||||
</span>
|
||||
:
|
||||
<div style={{display: "flex", marginTop: "15px"}}>
|
||||
<Button
|
||||
variant={executionData.result !== undefined && executionData.result !== null && executionData.result.length > 0 ? "outlined" : "contained"}
|
||||
type="submit"
|
||||
color="primary"
|
||||
fullWidth
|
||||
disabled={!handleValidateForm(executionArgument) || executionLoading}
|
||||
>
|
||||
{executionLoading ?
|
||||
<CircularProgress color="secondary" style={{color: "white",}} /> : "Submit"}
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
|
||||
{/*buttonClicked !== undefined && buttonClicked !== null && buttonClicked !== "finished" && buttonClicked.length > 0 ?
|
||||
<img id="finalize_gif" src="/images/finalize.gif" alt="finalize workflow animation" style={{width: 150, marginLeft: 125, borderRadius: theme.palette.borderRadius, }}
|
||||
onLoad={() => {
|
||||
console.log("Img loaded.")
|
||||
setTimeout(() => {
|
||||
console.log("Img closing.")
|
||||
setButtonClicked("finished")
|
||||
|
||||
}, 1250)
|
||||
|
||||
}}
|
||||
/>
|
||||
: null*/}
|
||||
|
||||
<div style={{marginTop: "10px"}}>
|
||||
{executionInfo}
|
||||
</div>
|
||||
}
|
||||
|
||||
{/*buttonClicked !== undefined && buttonClicked !== null && buttonClicked !== "finished" && buttonClicked.length > 0 ?
|
||||
<img id="finalize_gif" src="/images/finalize.gif" alt="finalize workflow animation" style={{width: 150, marginLeft: 125, borderRadius: theme.palette.borderRadius, }}
|
||||
onLoad={() => {
|
||||
console.log("Img loaded.")
|
||||
setTimeout(() => {
|
||||
console.log("Img closing.")
|
||||
setButtonClicked("finished")
|
||||
|
||||
}, 1250)
|
||||
|
||||
}}
|
||||
/>
|
||||
: null*/}
|
||||
|
||||
<div style={{marginTop: "10px"}}>
|
||||
{executionInfo}
|
||||
</div>
|
||||
|
||||
{answer !== undefined && answer !== null ? null :
|
||||
<ShowExecutionResults executionData={executionData} />
|
||||
}
|
||||
</form>
|
||||
{answer !== undefined && answer !== null ? null :
|
||||
<ShowExecutionResults executionData={executionData} />
|
||||
}
|
||||
</form>
|
||||
</div>
|
||||
}
|
||||
</Paper>
|
||||
</div>
|
||||
|
||||
@@ -1092,6 +1037,7 @@ const RunWorkflow = (defaultprops) => {
|
||||
usecases={undefined}
|
||||
|
||||
expanded={true}
|
||||
scrollTo={"input_markdown"}
|
||||
/>
|
||||
: null}
|
||||
|
||||
@@ -1103,20 +1049,59 @@ const RunWorkflow = (defaultprops) => {
|
||||
PaperProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
minWidth: isMobile ? "90%" : 400,
|
||||
maxWidth: isMobile ? "90%" : 400,
|
||||
minHeight: 350,
|
||||
paddingTop: 25,
|
||||
paddingLeft: 50,
|
||||
minWidth: isMobile ? "90%" : 500,
|
||||
maxWidth: isMobile ? "90%" : 500,
|
||||
minHeight: 400,
|
||||
maxHeight: 400,
|
||||
padding: 25,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
//minWidth: isMobile ? "90%" : newWorkflow === true ? 1000 : 550,
|
||||
//maxWidth: isMobile ? "90%" : newWorkflow === true ? 1000 : 550,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<DialogTitle style={{padding: 30, paddingBottom: 0, zIndex: 1000,}}>
|
||||
Share Landingpage
|
||||
<DialogTitle style={{padding: 30, paddingLeft: 20, paddingBottom: 0, zIndex: 1000,}}>
|
||||
Form Sharing Options for '{workflow.name}'
|
||||
</DialogTitle>
|
||||
<DialogContent style={{paddingTop: 10, display: "flex", minHeight: 300, zIndex: 1001, paddingBottom: 200, }}>
|
||||
<DialogContent style={{marginTop: 20,}}>
|
||||
<Typography variant="body1">
|
||||
<b>General Access</b>
|
||||
</Typography>
|
||||
|
||||
<Typography variant="body2" color="textSecondary">
|
||||
Form sharing and workflow sharing are not the same. By sharing a form, you are enabling anyone with the link to fill out the form AND run the workflow. They will NOT have access to seeing workflow details. By default, anyone with access to an organization can use a form.
|
||||
</Typography>
|
||||
|
||||
<div />
|
||||
{workflow !== undefined && workflow !== null && workflow.sharing !== undefined && workflow.sharing !== null ?
|
||||
<Select
|
||||
fullWidth
|
||||
style={{marginTop: 25, }}
|
||||
value={workflow.sharing === "" ? "private" : workflow.sharing}
|
||||
onChange={(e) => {
|
||||
console.log("SHARING: ", e.target.value)
|
||||
|
||||
workflow.sharing = e.target.value
|
||||
setWorkflow(workflow)
|
||||
saveWorkflow(workflow)
|
||||
setUpdate(Math.random())
|
||||
|
||||
toast("Form sharing updated.")
|
||||
}}
|
||||
>
|
||||
<MenuItem
|
||||
value={"private"}
|
||||
>
|
||||
Organization only
|
||||
</MenuItem>
|
||||
<Divider />
|
||||
<MenuItem
|
||||
value={"form"}
|
||||
>
|
||||
Anyone with the link
|
||||
</MenuItem>
|
||||
</Select>
|
||||
: null}
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
@@ -1135,19 +1120,19 @@ const RunWorkflow = (defaultprops) => {
|
||||
<Button
|
||||
variant={"outlined"}
|
||||
color={"secondary"}
|
||||
disabled
|
||||
onClick={() => {
|
||||
setSharingOpen(true)
|
||||
}}
|
||||
>
|
||||
Manage Sharing
|
||||
Share Form
|
||||
</Button>
|
||||
</div>
|
||||
: null}
|
||||
|
||||
{basedata}
|
||||
</div>
|
||||
:
|
||||
<div>
|
||||
<div style={{width: 100, itemAlign: "center", textAlign: "center", margin: "auto", }}>
|
||||
<CircularProgress />
|
||||
</div>
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ const SetAuthentication = (props) => {
|
||||
const [loadFail, setLoadFail] = useState("");
|
||||
const [appAuthentication, setAppAuthentication] = React.useState([]);
|
||||
|
||||
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
|
||||
const isCloud = (window.location.host === "localhost:3002" || window.location.host === "shuffler.io") ? true : (process.env.IS_SSR === "true");
|
||||
//const alert = useAlert();
|
||||
|
||||
const parseIncomingOpenapiData = (data) => {
|
||||
@@ -142,8 +142,8 @@ const SetAuthentication = (props) => {
|
||||
// 3. Help them set info for the app
|
||||
// Make sure to test both private and public apps
|
||||
|
||||
const appname = app.name !== undefined ? app.name : "";
|
||||
const appLink = "/apps/" + app.id || "";
|
||||
const appname = app.name !== undefined ? app.name : ""
|
||||
const appLink = "/apps/" + app.id || ""
|
||||
|
||||
console.log("App: ", app)
|
||||
|
||||
@@ -154,21 +154,16 @@ const SetAuthentication = (props) => {
|
||||
:
|
||||
<><div>
|
||||
<Typography variant="h4" style={{ marginBottom: 20, }}>
|
||||
A Shuffle Organization has invited you to: Configure <a href={appLink} target="_blank" style={{ color: '#FF8444', textDecoration: 'none' }}>{appname}</a> Authentication
|
||||
You are invited to: Configure <a href={appLink} target="_blank" style={{ color: '#FF8444', textDecoration: 'none' }}>{appname}</a> Authentication
|
||||
</Typography>
|
||||
|
||||
{/* What does this mean box */}
|
||||
<Typography variant="h6" style={{ marginBottom: 20, }}>
|
||||
What does this mean?
|
||||
</Typography>
|
||||
<Typography variant="body1" style={{ marginBottom: 20, }}>
|
||||
<Typography variant="body1" style={{ marginBottom: 20, color: "rgba(255,255,255,0.4)", }}>
|
||||
A Shuffle Organization has invited you to configure authentication for this app so that they can use this authentication in one of their workflows.
|
||||
</Typography>
|
||||
|
||||
<Typography variant="h6">
|
||||
Authenticate Here:
|
||||
</Typography>
|
||||
|
||||
<Typography variant="body1" style={{ marginBottom: 20, }}>
|
||||
{app.authentication === undefined || app.authentication === null || app.authentication.length === 0 ?
|
||||
null
|
||||
@@ -195,7 +190,7 @@ const SetAuthentication = (props) => {
|
||||
appAuthentication={appAuthentication} />}
|
||||
</Typography>
|
||||
|
||||
<Typography variant="h6" style={{ marginBottom: 20, }}>
|
||||
<Typography variant="h6" style={{ marginTop: 50, marginBottom: 20, }}>
|
||||
What can they do with this?
|
||||
</Typography>
|
||||
|
||||
@@ -203,12 +198,11 @@ const SetAuthentication = (props) => {
|
||||
You can check the actions they want to use <a href={appLink} target="_blank" style={{ color: '#FF8444', textDecoration: 'none' }}>here</a>.
|
||||
</Typography>
|
||||
|
||||
<Typography variant="body1" style={{ marginBottom: 20, }}>
|
||||
{/* Add a box below */}
|
||||
<Typography variant="body1" style={{ marginBottom: 20, color: "rgba(255,255,255,0.4)",}}>
|
||||
<div className="collapsible-container">
|
||||
<div className="collapsible-list">
|
||||
{app.actions?.map((item, index) => (
|
||||
<div key={index} className="collapsible-item">
|
||||
<div key={index} className="collapsible-item" style={{cursor: "pointer", }}>
|
||||
<div className="collapsible-label" onClick={() => handleToggle(index)}>
|
||||
{item.label}
|
||||
</div>
|
||||
|
||||
@@ -47,7 +47,7 @@ const Welcome = (props) => {
|
||||
}
|
||||
}, [activeStep])
|
||||
|
||||
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
|
||||
const isCloud = (window.location.host === "localhost:3002" || window.location.host === "shuffler.io") ? true : (process.env.IS_SSR === "true");
|
||||
const [steps, setSteps] = useState([
|
||||
"Help us get to know you",
|
||||
"Find your Apps",
|
||||
|
||||
@@ -3528,7 +3528,7 @@ const Workflows = (props) => {
|
||||
var workflowDelay = -150
|
||||
var appDelay = -75
|
||||
|
||||
const foundPriority = userdata === undefined || userdata === null ? null : userdata.priorities.find(prio => prio.type === "usecase" && prio.active === true)
|
||||
const foundPriority = userdata === undefined || userdata === null || userdata.priorities === undefined || userdata.priorities === null ? null : userdata.priorities.find(prio => prio.type === "usecase" && prio.active === true)
|
||||
return (
|
||||
<div style={viewStyle}>
|
||||
<div style={workflowViewStyle}>
|
||||
|
||||
Reference in New Issue
Block a user