Merge branch 'frikky:launch' into launch

This commit is contained in:
Jay Gohil
2022-03-03 16:09:25 +05:30
committed by GitHub
48 changed files with 5726 additions and 2447 deletions
@@ -556,7 +556,7 @@ const ConfigureWorkflow = (props) => {
{action.must_activate ? (
<Button
color="primary"
variant="outlined"
variant="contained"
onClick={() => {
console.log("ACTION: ", action)
activateApp(action.action.app_id, action.app_name, action.app_version);
@@ -666,7 +666,7 @@ const ConfigureWorkflow = (props) => {
}
}}
>
Finish setup
Close window
</Button>
</ButtonGroup>
</div>
@@ -1499,7 +1499,7 @@ const Framework = (props) => {
/>
:
<div>
TBD: Coming in 1.0.0.
Coming in 1.0.0. <a style={{ textDecoration: "none", color: "#f85a3e" }} href="https://shuffler.io/register" target="_blank">Register for Shuffle cloud</a> to try an early version now.
</div>
: null}
</div>
+11
View File
@@ -35,6 +35,7 @@ import {
import {
Analytics as AnalyticsIcon,
Lightbulb as LightbulbIcon,
} from "@mui/icons-material";
//import LogoutIcon from '@mui/icons-material/Logout';
import { useAlert } from "react-alert";
@@ -466,6 +467,16 @@ const Header = (props) => {
<AnalyticsIcon style={{marginRight: 5 }}/> Get Started
</Link>
</MenuItem>
<MenuItem
onClick={(event) => {
event.preventDefault();
handleClose();
}}
>
<Link to="/usecases" style={hrefStyle}>
<LightbulbIcon style={{marginRight: 5 }}/> Use Cases
</Link>
</MenuItem>
<MenuItem
onClick={(event) => {
event.preventDefault();
+202
View File
@@ -0,0 +1,202 @@
import React, {useState, useRef, useImperativeHandle} from 'react'
import {makeStyles} from '@material-ui/core/styles'
import Menu, {MenuProps} from '@material-ui/core/Menu'
import MenuItem, {MenuItemProps} from '@material-ui/core/MenuItem'
import ArrowRight from '@material-ui/icons/ArrowRight'
import clsx from 'clsx'
export interface NestedMenuItemProps extends Omit<MenuItemProps, 'button'> {
/**
* Open state of parent `<Menu />`, used to close decendent menus when the
* root menu is closed.
*/
parentMenuOpen: boolean
/**
* Component for the container element.
* @default 'div'
*/
component?: React.ElementType
/**
* Effectively becomes the `children` prop passed to the `<MenuItem/>`
* element.
*/
label?: React.ReactNode
/**
* @default <ArrowRight />
*/
rightIcon?: React.ReactNode
/**
* Props passed to container element.
*/
ContainerProps?: React.HTMLAttributes<HTMLElement> &
React.RefAttributes<HTMLElement | null>
/**
* Props passed to sub `<Menu/>` element
*/
MenuProps?: Omit<MenuProps, 'children'>
/**
* @see https://material-ui.com/api/list-item/
*/
button?: true | undefined
}
const TRANSPARENT = 'rgba(0,0,0,0)'
const useMenuItemStyles = makeStyles((theme) => ({
root: (props: any) => ({
backgroundColor: props.open ? theme.palette.action.hover : TRANSPARENT
})
}))
/**
* Use as a drop-in replacement for `<MenuItem>` when you need to add cascading
* menu elements as children to this component.
*/
const NestedMenuItem = React.forwardRef<
HTMLLIElement | null,
NestedMenuItemProps
>(function NestedMenuItem(props, ref) {
const {
parentMenuOpen,
component = 'div',
label,
rightIcon = <ArrowRight />,
children,
className,
tabIndex: tabIndexProp,
MenuProps = {},
ContainerProps: ContainerPropsProp = {},
...MenuItemProps
} = props
const {ref: containerRefProp, ...ContainerProps} = ContainerPropsProp
const menuItemRef = useRef<HTMLLIElement>(null)
useImperativeHandle(ref, () => menuItemRef.current)
const containerRef = useRef<HTMLDivElement>(null)
useImperativeHandle(containerRefProp, () => containerRef.current)
const menuContainerRef = useRef<HTMLDivElement>(null)
const [isSubMenuOpen, setIsSubMenuOpen] = useState(false)
const handleMouseEnter = (event: React.MouseEvent<HTMLElement>) => {
setIsSubMenuOpen(true)
if (ContainerProps?.onMouseEnter) {
ContainerProps.onMouseEnter(event)
}
}
const handleMouseLeave = (event: React.MouseEvent<HTMLElement>) => {
setIsSubMenuOpen(false)
if (ContainerProps?.onMouseLeave) {
ContainerProps.onMouseLeave(event)
}
}
// Check if any immediate children are active
const isSubmenuFocused = () => {
const active = containerRef.current?.ownerDocument?.activeElement
for (const child of menuContainerRef.current?.children ?? []) {
if (child === active) {
return true
}
}
return false
}
const handleFocus = (event: React.FocusEvent<HTMLElement>) => {
if (event.target === containerRef.current) {
setIsSubMenuOpen(true)
}
if (ContainerProps?.onFocus) {
ContainerProps.onFocus(event)
}
}
const handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {
if (event.key === 'Escape') {
return
}
if (isSubmenuFocused()) {
event.stopPropagation()
}
const active = containerRef.current?.ownerDocument?.activeElement
if (event.key === 'ArrowLeft' && isSubmenuFocused()) {
containerRef.current?.focus()
}
if (
event.key === 'ArrowRight' &&
event.target === containerRef.current &&
event.target === active
) {
const firstChild = menuContainerRef.current?.children[0] as
| HTMLElement
| undefined
firstChild?.focus()
}
}
const open = isSubMenuOpen && parentMenuOpen
const menuItemClasses = useMenuItemStyles({open})
// Root element must have a `tabIndex` attribute for keyboard navigation
let tabIndex
if (!props.disabled) {
tabIndex = tabIndexProp !== undefined ? tabIndexProp : -1
}
return (
<div
{...ContainerProps}
ref={containerRef}
onFocus={handleFocus}
tabIndex={tabIndex}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
onKeyDown={handleKeyDown}
>
<MenuItem
{...MenuItemProps}
className={clsx(menuItemClasses.root, className)}
ref={menuItemRef}
>
{label}
{rightIcon}
</MenuItem>
<Menu
// Set pointer events to 'none' to prevent the invisible Popover div
// from capturing events for clicks and hovers
style={{pointerEvents: 'none'}}
anchorEl={menuItemRef.current}
anchorOrigin={{
vertical: 'top',
horizontal: 'right'
}}
transformOrigin={{
vertical: 'top',
horizontal: 'left'
}}
open={open}
autoFocus={false}
disableAutoFocus
disableEnforceFocus
onClose={() => {
setIsSubMenuOpen(false)
}}
>
<div ref={menuContainerRef} style={{pointerEvents: 'auto'}}>
{children}
</div>
</Menu>
</div>
)
})
export default NestedMenuItem
+57 -28
View File
@@ -49,6 +49,8 @@ const MenuProps = {
scrollX: "auto",
},
},
variant: "menu",
getContentAnchorEl: null,
};
const AuthenticationOauth2 = (props) => {
@@ -84,6 +86,7 @@ const AuthenticationOauth2 = (props) => {
const [oauthUrl, setOauthUrl] = React.useState("");
const [buttonClicked, setButtonClicked] = React.useState(false);
const [selectedScopes, setSelectedScopes] = React.useState([]);
const [offlineAccess, setOfflineAccess] = React.useState(true);
const allscopes =
authenticationType.scope !== undefined ? authenticationType.scope : [];
@@ -111,8 +114,19 @@ const AuthenticationOauth2 = (props) => {
setButtonClicked(true);
console.log("SCOPES: ", scopes);
client_id = client_id.trim()
client_secret = client_secret.trim()
oauth_url = oauth_url.trim()
var resources = "";
if (scopes !== undefined && (scopes !== null) & (scopes.length > 0)) {
if (offlineAccess === true && !scopes.includes("offline_access")) {
if (authenticationType.redirect_uri.includes("microsoft")) {
console.log("Appending offline access")
scopes.push("offline_access")
}
}
resources = scopes.join(" ");
//resources = scopes.join(",");
}
@@ -496,34 +510,49 @@ const AuthenticationOauth2 = (props) => {
}}
/>
{allscopes.length === 0 ? null : (
<span style={{marginTop: 10}}>
Scopes
<Select
multiple
value={selectedScopes}
style={{
backgroundColor: theme.palette.inputColor,
color: "white",
padding: 5,
}}
onChange={(e) => {
handleScopeChange(e);
}}
fullWidth
input={<Input id="select-multiple-native" />}
renderValue={(selected) => selected.join(", ")}
MenuProps={MenuProps}
>
{allscopes.map((data, index) => {
return (
<MenuItem key={index} value={data}>
<Checkbox checked={selectedScopes.indexOf(data) > -1} />
<ListItemText primary={data} />
</MenuItem>
);
})}
</Select>
</span>
<div style={{width: "100%", marginTop: 10, display: "flex"}}>
<span>
Scopes
<Select
multiple
value={selectedScopes}
style={{
backgroundColor: theme.palette.inputColor,
color: "white",
padding: 5,
minWidth: 300,
maxWidth: 300,
}}
onChange={(e) => {
handleScopeChange(e)
}}
fullWidth
input={<Input id="select-multiple-native" />}
renderValue={(selected) => selected.join(", ")}
MenuProps={MenuProps}
>
{allscopes.map((data, index) => {
return (
<MenuItem key={index} value={data}>
<Checkbox checked={selectedScopes.indexOf(data) > -1} />
<ListItemText primary={data} />
</MenuItem>
);
})}
</Select>
</span>
<span>
<Tooltip
color="primary"
title={"Automatic Refresh (default: true)"}
placement="top"
>
<Checkbox style={{paddingTop: 20}} color="secondary" checked={offlineAccess} onClick={() => {
setOfflineAccess(!offlineAccess)
}}/>
</Tooltip>
</span>
</div>
)}
</span>
)}
+222 -73
View File
@@ -97,6 +97,30 @@ const OrgHeader = (props) => {
? ""
: selectedOrganization.defaults.notification_workflow
);
const [openidClientId, setOpenidClientId] = React.useState(
selectedOrganization.sso_config === undefined
? ""
: selectedOrganization.sso_config.client_id === undefined ||
selectedOrganization.sso_config.client_id.length === 0
? ""
: selectedOrganization.sso_config.client_id
);
const [openidAuthorization, setOpenidAuthorization] = React.useState(
selectedOrganization.sso_config === undefined
? ""
: selectedOrganization.sso_config.openid_authorization === undefined ||
selectedOrganization.sso_config.openid_authorization.length === 0
? ""
: selectedOrganization.sso_config.openid_authorization
);
const [openidToken, setOpenidToken] = React.useState(
selectedOrganization.sso_config === undefined
? ""
: selectedOrganization.sso_config.openid_token === undefined ||
selectedOrganization.sso_config.openid_token.length === 0
? ""
: selectedOrganization.sso_config.openid_token
)
const [file, setFile] = React.useState("");
const [fileBase64, setFileBase64] = React.useState(
@@ -145,6 +169,7 @@ const OrgHeader = (props) => {
defaults,
sso_config
) => {
const data = {
name: name,
description: description,
@@ -216,6 +241,9 @@ const OrgHeader = (props) => {
{
sso_entrypoint: ssoEntrypoint,
sso_certificate: ssoCertificate,
client_id: openidClientId,
openid_authorization: openidAuthorization,
openid_token: openidToken,
}
)
}
@@ -548,79 +576,200 @@ const OrgHeader = (props) => {
</span>
</Grid>
)}
<Grid item xs={6} style={{}}>
<span>
<Typography>SSO Entrypoint (IdP)</Typography>
<TextField
required
style={{
flex: "1",
marginTop: "5px",
marginRight: "15px",
backgroundColor: theme.palette.inputColor,
}}
fullWidth={true}
type="name"
multiline={true}
rows={2}
disabled={
selectedOrganization.manager_orgs !== undefined &&
selectedOrganization.manager_orgs !== null &&
selectedOrganization.manager_orgs.length > 0
}
id="outlined-with-placeholder"
margin="normal"
variant="outlined"
placeholder="The entrypoint URL from your provider"
value={ssoEntrypoint}
onChange={(e) => {
setSsoEntrypoint(e.target.value);
}}
InputProps={{
classes: {
notchedOutline: classes.notchedOutline,
},
style: {
color: "white",
},
}}
/>
</span>
</Grid>
<Grid item xs={6} style={{}}>
<span>
<Typography>SSO Certificate (X509)</Typography>
<TextField
required
style={{
flex: "1",
marginTop: "5px",
marginRight: "15px",
backgroundColor: theme.palette.inputColor,
}}
fullWidth={true}
type="name"
id="outlined-with-placeholder"
margin="normal"
variant="outlined"
multiline={true}
rows={2}
placeholder="The X509 certificate to use"
value={ssoCertificate}
onChange={(e) => {
setSsoCertificate(e.target.value);
}}
InputProps={{
classes: {
notchedOutline: classes.notchedOutline,
},
style: {
color: "white",
},
}}
/>
</span>
</Grid>
{isCloud ? null :
<Grid item xs={12} style={{marginTop: 50 }}>
<Typography variant="h4" style={{textAlign: "center",}}>OpenID connect</Typography>
<Grid container style={{marginTop: 10, }}>
<Grid item xs={4} style={{}}>
<span>
<Typography>Client ID</Typography>
<TextField
required
style={{
flex: "1",
marginTop: "5px",
marginRight: "15px",
backgroundColor: theme.palette.inputColor,
}}
fullWidth={true}
type="name"
multiline={true}
rows={2}
disabled={
selectedOrganization.manager_orgs !== undefined &&
selectedOrganization.manager_orgs !== null &&
selectedOrganization.manager_orgs.length > 0
}
id="outlined-with-placeholder"
margin="normal"
variant="outlined"
placeholder="The OpenID client ID from the identity provider"
value={openidClientId}
onChange={(e) => {
setOpenidClientId(e.target.value);
}}
InputProps={{
classes: {
notchedOutline: classes.notchedOutline,
},
style: {
color: "white",
},
}}
/>
</span>
</Grid>
<Grid item xs={4} style={{}}>
<span>
<Typography>Authorization URL</Typography>
<TextField
required
style={{
flex: "1",
marginTop: "5px",
marginRight: "15px",
backgroundColor: theme.palette.inputColor,
}}
fullWidth={true}
type="name"
id="outlined-with-placeholder"
margin="normal"
variant="outlined"
multiline={true}
rows={2}
placeholder="The OpenID authorization URL (usually ends with /authorize)"
value={openidAuthorization}
onChange={(e) => {
setOpenidAuthorization(e.target.value)
}}
InputProps={{
classes: {
notchedOutline: classes.notchedOutline,
},
style: {
color: "white",
},
}}
/>
</span>
</Grid>
<Grid item xs={4} style={{}}>
<span>
<Typography>Token URL</Typography>
<TextField
required
style={{
flex: "1",
marginTop: "5px",
marginRight: "15px",
backgroundColor: theme.palette.inputColor,
}}
fullWidth={true}
type="name"
id="outlined-with-placeholder"
margin="normal"
variant="outlined"
multiline={true}
rows={2}
placeholder="The OpenID token URL (usually ends with /token)"
value={openidToken}
onChange={(e) => {
setOpenidToken(e.target.value)
}}
InputProps={{
classes: {
notchedOutline: classes.notchedOutline,
},
style: {
color: "white",
},
}}
/>
</span>
</Grid>
</Grid>
</Grid>
}
{isCloud ? null :
<Grid item xs={12} style={{marginTop: 50,}}>
<Typography variant="h4" style={{textAlign: "center",}}>SAML SSO (v1.1)</Typography>
<Grid container style={{marginTop: 10, }}>
<Grid item xs={6} style={{}}>
<span>
<Typography>SSO Entrypoint (IdP)</Typography>
<TextField
required
style={{
flex: "1",
marginTop: "5px",
marginRight: "15px",
backgroundColor: theme.palette.inputColor,
}}
fullWidth={true}
type="name"
multiline={true}
rows={2}
disabled={
selectedOrganization.manager_orgs !== undefined &&
selectedOrganization.manager_orgs !== null &&
selectedOrganization.manager_orgs.length > 0
}
id="outlined-with-placeholder"
margin="normal"
variant="outlined"
placeholder="The entrypoint URL from your provider"
value={ssoEntrypoint}
onChange={(e) => {
setSsoEntrypoint(e.target.value);
}}
InputProps={{
classes: {
notchedOutline: classes.notchedOutline,
},
style: {
color: "white",
},
}}
/>
</span>
</Grid>
<Grid item xs={6} style={{}}>
<span>
<Typography>SSO Certificate (X509)</Typography>
<TextField
required
style={{
flex: "1",
marginTop: "5px",
marginRight: "15px",
backgroundColor: theme.palette.inputColor,
}}
fullWidth={true}
type="name"
id="outlined-with-placeholder"
margin="normal"
variant="outlined"
multiline={true}
rows={2}
placeholder="The X509 certificate to use"
value={ssoCertificate}
onChange={(e) => {
setSsoCertificate(e.target.value);
}}
InputProps={{
classes: {
notchedOutline: classes.notchedOutline,
},
style: {
color: "white",
},
}}
/>
</span>
</Grid>
</Grid>
</Grid>
}
{/*
<span style={{textAlign: "center"}}>
{expanded ?
@@ -0,0 +1,19 @@
import React, {useState, useEffect, useLayoutEffect} from 'react';
import Draggable from "react-draggable";
import {
Paper
} from "@material-ui/core";
const PaperComponent = (props) => {
return (
<Draggable
handle="#draggable-dialog-title"
cancel={'[class*="MuiDialogContent-root"]'}
>
<Paper {...props} />
</Draggable>
)
}
export default PaperComponent;
+173 -91
View File
@@ -1,11 +1,12 @@
import React, { useState, useEffect, useLayoutEffect } from "react";
import { makeStyles, createStyles } from "@material-ui/core/styles";
import { validateJson, GetIconInfo } from "../views/Workflows.jsx";
import { GetParsedPaths } from "../views/Apps.jsx";
import { GetIconInfo } from "../views/Workflows.jsx";
import { sortByKey } from "../views/AngularWorkflow.jsx";
import { useTheme } from "@material-ui/core/styles";
import NestedMenuItem from "material-ui-nested-menu-item";
import { useAlert } from "react-alert";
import theme from '../theme';
//import NestedMenuItem from "./NestedMenu.jsx";
@@ -168,6 +169,7 @@ const ParsedAction = (props) => {
//const theme = useTheme();
const classes = useStyles();
const alert = useAlert()
const [expansionModalOpen, setExpansionModalOpen] = React.useState(false);
const [hideBody, setHideBody] = React.useState(true);
@@ -178,23 +180,21 @@ const ParsedAction = (props) => {
const [hiddenDescription, setHiddenDescription] = React.useState(true);
useEffect(() => {
//if (data.startsWith("${") && data.endsWith("}")) {
//}
// PARAM FIX - Gonna use the ID field, even though it's a hack
const paramcheck = selectedAction.parameters.find(param => param.name === "body")
console.log("LOADED! Change hideBody based on input? Action: ", selectedAction, paramcheck)
if (paramcheck !== undefined && paramcheck !== null) {
if (paramcheck.id === "TOGGLED"){
setHideBody(false)
setActivateHidingBodyButton(false)
console.log("TOGGLED BODY!")
} else {
setHideBody(true)
if (paramcheck.id === "UNTOGGLED") {
if (selectedAction.parameters !== null && selectedAction.parameters !== undefined) {
const paramcheck = selectedAction.parameters.find(param => param.name === "body")
//console.log("LOADED! Change hideBody based on input? Action: ", selectedAction, paramcheck)
if (paramcheck !== undefined && paramcheck !== null) {
if (paramcheck.id === "TOGGLED"){
setHideBody(false)
setActivateHidingBodyButton(false)
console.log("UNTOGGLED!")
console.log("TOGGLED BODY!")
} else {
setHideBody(true)
if (paramcheck.id === "UNTOGGLED") {
setActivateHidingBodyButton(false)
console.log("UNTOGGLED!")
}
}
}
}
@@ -304,6 +304,8 @@ const ParsedAction = (props) => {
});
};
const defineStartnode = () => {
if (cy === undefined) {
return;
@@ -391,14 +393,46 @@ const ParsedAction = (props) => {
if (actionlist.length === 0) {
// FIXME: Have previous execution values in here
actionlist.push({
type: "Execution Argument",
name: "Execution Argument",
value: "$exec",
highlight: "exec",
autocomplete: "exec",
example: "",
});
if (workflowExecutions.length > 0) {
for (var key in workflowExecutions) {
if (
workflowExecutions[key].execution_argument === undefined ||
workflowExecutions[key].execution_argument === null ||
workflowExecutions[key].execution_argument.length === 0
) {
continue;
}
console.log("EXEC: ", workflowExecutions[key].execution_argument)
const valid = validateJson(workflowExecutions[key].execution_argument)
console.log("VALID: ", valid)
if (valid.valid) {
actionlist.push({
type: "Execution Argument",
name: "Execution Argument",
value: "$exec",
highlight: "exec",
autocomplete: "exec",
example: valid.result,
})
break
}
}
}
if (actionlist.length === 0) {
actionlist.push({
type: "Execution Argument",
name: "Execution Argument",
value: "$exec",
highlight: "exec",
autocomplete: "exec",
example: "",
})
}
actionlist.push({
type: "Shuffle DB",
name: "Shuffle DB",
@@ -454,7 +488,8 @@ const ParsedAction = (props) => {
continue;
}
var exampledata = item.example === undefined ? "" : item.example;
var exampledata = item.example === undefined || item.example === null ? "" : item.example;
console.log("EXAMPLE: ", exampledata)
// Find previous execution and their variables
//exampledata === "" &&
if (workflowExecutions.length > 0) {
@@ -471,55 +506,22 @@ const ParsedAction = (props) => {
var foundResult = workflowExecutions[key].results.find(
(result) => result.action.id === item.id
);
if (foundResult === undefined) {
if (foundResult === undefined || foundResult === null) {
continue;
}
foundResult.result = foundResult.result.trim();
foundResult.result = foundResult.result
.split(" None")
.join(' "None"');
foundResult.result = foundResult.result
.split(" False")
.join(" false");
foundResult.result = foundResult.result
.split(" True")
.join(" true");
if (foundResult.result !== undefined && foundResult.result !== null) {
foundResult = foundResult.result
}
console.log("VALID RESULT: ", foundResult)
var jsonvalid = true;
try {
const tmp = String(JSON.parse(foundResult.result));
if (
!foundResult.result.includes("{") &&
!foundResult.result.includes("[")
) {
jsonvalid = false;
}
} catch (e) {
try {
foundResult.result = foundResult.result
.split("'")
.join('"');
const tmp = String(JSON.parse(foundResult.result));
if (
!foundResult.result.includes("{") &&
!foundResult.result.includes("[")
) {
jsonvalid = false;
}
} catch (e) {
jsonvalid = false;
}
}
// Finds the FIRST json only
if (jsonvalid) {
exampledata = JSON.parse(foundResult.result);
const valid = validateJson(foundResult)
if (valid.valid) {
exampledata = valid.result;
break;
}
//else {
// console.log("Invalid JSON: ", foundResult.result)
//}
} else {
exampledata = foundResult;
}
}
}
@@ -528,6 +530,7 @@ const ParsedAction = (props) => {
item.label === null || item.label === undefined
? ""
: item.label.split(" ").join("_");
const actionvalue = {
type: "action",
id: item.id,
@@ -539,11 +542,65 @@ const ParsedAction = (props) => {
}
}
//console.log("ACTIONLIST: ", actionlist)
setActionlist(actionlist);
}
}
});
const calculateHelpertext = (input_data) => {
var helperText = ""
var looperText = ""
const found = input_data.match(/[$]{1}([a-zA-Z0-9_-]+\.?){1}([a-zA-Z0-9#_-]+\.?){0,}/g)
if (found !== null) {
try {
// When the found array is empty.
for (var i = 0; i < found.length; i++) {
const variableSplit = found[i].split(".#")
if ((variableSplit.length-1) > 1) {
//console.log("Larger than 1: ", variableSplit)
if (looperText.length === 0) {
looperText += "PS: Double looping (.#) may cause problems."
}
}
var foundSlice = false
for (var j = 0; j < actionlist.length; j++) {
//console.log("ACTION: ", found[i], actionlist[j])
//console.log("ACTION :", found[i].split(".")[0].slice(1,).toLowerCase(), actionlist[j].autocomplete.toLowerCase())
if(found[i].split(".")[0].slice(1,).toLowerCase() == actionlist[j].autocomplete.toLowerCase()){
//console.log("Found: ", found[i])
// Validate path?
foundSlice = true
}
}
if (!foundSlice) {
if (!helperText.includes("Invalid variables")) {
helperText+= "Invalid variables: "
}
helperText+= found[i] + ", "
}
}
} catch (e) {
console.log("Parsing error: ", e)
}
}
if (looperText.length > 0) {
if (helperText.length > 0) {
helperText += ". "
}
helperText += looperText
}
return helperText
}
const changeActionParameter = (event, count, data) => {
//console.log("Action change: ", selectedAction, data)
if (data.name.startsWith("${") && data.name.endsWith("}")) {
@@ -1282,21 +1339,27 @@ const ParsedAction = (props) => {
const clickedFieldId = "rightside_field_" + count;
const shufflecode = <ShuffleCodeEditor
fieldCount = {fieldCount}
setFieldCount = {setFieldCount}
actionlist = {actionlist}
changeActionParameterCodeMirror = {changeActionParameterCodeMirror}
codedata={codedata}
setcodedata={setcodedata}
expansionModalOpen={expansionModalOpen}
setExpansionModalOpen={setExpansionModalOpen}
/>
const shufflecode = fieldCount !== count ? null :
(
<ShuffleCodeEditor
fieldCount = {fieldCount}
setFieldCount = {setFieldCount}
actionlist = {actionlist}
changeActionParameterCodeMirror = {changeActionParameterCodeMirror}
codedata={codedata}
setcodedata={setcodedata}
expansionModalOpen={expansionModalOpen}
setExpansionModalOpen={setExpansionModalOpen}
/>
)
//<TextareaAutosize
// <CodeMirror
//fullWidth
var baseHelperText = ""
if (data !== undefined && data !== null && data.value !== undefined && data.value !== null && data.value.length > 0) {
baseHelperText = calculateHelpertext(data.value)
}
var datafield = (
<TextField
@@ -1405,7 +1468,7 @@ const ParsedAction = (props) => {
//changeActionParameterCodemirror(event, count, data)
changeActionParameter(event, count, data);
}}
helperText={
helperText={baseHelperText.length > 0 ? baseHelperText :
selectedApp.generated &&
selectedApp.activated &&
data.name === "body" ? (
@@ -1423,15 +1486,7 @@ const ParsedAction = (props) => {
) : null
}
onBlur={(event) => {
// Super basic check
//if (event.target.value.startsWith("{")) {
// console.log("VALIDATING JSON")
// try {
// JSON.parse(event.target.value)
// } catch (e) {
// alert.error("Failed to parse json: ", e)
// }
//}
baseHelperText = calculateHelpertext(event.target.value)
}}
/>
);
@@ -1536,6 +1591,9 @@ const ParsedAction = (props) => {
datafield = (
<Select
MenuProps={{
disableScrollLock: true,
}}
SelectDisplayProps={{
style: {
marginLeft: 10,
@@ -2036,6 +2094,9 @@ const ParsedAction = (props) => {
Autocomplete
</InputLabel>
<Select
MenuProps={{
disableScrollLock: true,
}}
labelId="action-autocompleter"
SelectDisplayProps={{
style: {
@@ -2333,11 +2394,17 @@ const ParsedAction = (props) => {
selectedApp.versions !== undefined &&
selectedApp.versions.length > 1 ? (
<Select
MenuProps={{
disableScrollLock: true,
}}
defaultValue={selectedAction.app_version}
onChange={(event) => {
console.log("VAL: ", event.target.value)
console.log("App: ", selectedApp)
const newversion = selectedApp.versions.find(
(tmpApp) => tmpApp.version == event.target.value
);
console.log("NEWVERSION: ", newversion);
if (newversion !== undefined && newversion !== null) {
getApp(newversion.id, true);
@@ -2408,6 +2475,7 @@ const ParsedAction = (props) => {
if (param.value.includes(baselabel)) {
//if (param.value.toLowerCase().includes(baselabel)) {
console.log("FOUND: ", param);
workflow.actions[key].parameters[subkey].value.replaceAll(
baselabel,
e.target.value
@@ -2497,6 +2565,9 @@ const ParsedAction = (props) => {
<Typography>Authentication</Typography>
<div style={{ display: "flex" }}>
<Select
MenuProps={{
disableScrollLock: true,
}}
labelId="select-app-auth"
value={
Object.getOwnPropertyNames(
@@ -2508,6 +2579,7 @@ const ParsedAction = (props) => {
SelectDisplayProps={{
style: {
marginLeft: 10,
maxWidth: 250,
},
}}
fullWidth
@@ -2581,6 +2653,7 @@ const ParsedAction = (props) => {
>
<IconButton
color="primary"
variant="outlined"
style={{}}
onClick={() => {
setAuthenticationModalOpen(true);
@@ -2597,6 +2670,9 @@ const ParsedAction = (props) => {
<div style={{ marginTop: "20px" }}>
<Typography>Environment</Typography>
<Select
MenuProps={{
disableScrollLock: true,
}}
value={
selectedActionEnvironment === undefined ||
selectedActionEnvironment.Name === undefined
@@ -2651,6 +2727,9 @@ const ParsedAction = (props) => {
<div style={{ marginTop: "20px" }}>
<Typography>Set execution variable (optional)</Typography>
<Select
MenuProps={{
disableScrollLock: true,
}}
value={
selectedAction.execution_variable !== undefined
? selectedAction.execution_variable.name
@@ -2843,6 +2922,9 @@ const ParsedAction = (props) => {
{/*setNewSelectedAction !== undefined ?
<Select
MenuProps={{
disableScrollLock: true,
}}
value={selectedAction.name}
fullWidth
onChange={setNewSelectedAction}
+12 -1
View File
@@ -23,6 +23,7 @@ import {
import { useTheme } from '@material-ui/core/styles';
import { validateJson } from "../views/Workflows.jsx";
import ReactJson from "react-json-view";
import PaperComponent from "../components/PaperComponent.jsx"
import CodeMirror from '@uiw/react-codemirror';
import 'codemirror/keymap/sublime';
@@ -89,16 +90,24 @@ const CodeEditor = (props) => {
return (
<Dialog
disableEnforceFocus={true}
hideBackdrop={true}
disableBackdropClick={true}
open={expansionModalOpen}
onClose={() => {
//setExpansionModalOpen(false)
console.log("In closer")
changeActionParameterCodeMirror({target: {value: ""}}, fieldCount, localcodedata)
}}
PaperComponent={PaperComponent}
aria-labelledby="draggable-dialog-title"
PaperProps={{
style: {
backgroundColor: theme.palette.surfaceColor,
color: "white",
minWidth: 600,
padding: 25,
border: theme.palette.defaultBorder,
zIndex: 10012,
},
}}
>
@@ -109,7 +118,9 @@ const CodeEditor = (props) => {
>
<div style={{display: "flex"}}>
<DialogTitle
id="draggable-dialog-title"
style={{
cursor: "move",
paddingBottom:20,
paddingLeft: 10,
}}
+287
View File
@@ -0,0 +1,287 @@
import React, { useState, useEffect, useLayoutEffect } from "react";
import theme from '../theme';
import {
Chip,
Typography,
Paper,
Avatar,
Grid,
Tooltip,
} from "@material-ui/core";
import {
AvatarGroup,
} from "@mui/material"
import {
Restore as RestoreIcon,
Edit as EditIcon,
BubbleChart as BubbleChartIcon,
MoreVert as MoreVertIcon,
} from '@material-ui/icons';
import { useNavigate, Link, useParams } from "react-router-dom";
const workflowActionStyle = {
display: "flex",
width: 160,
height: 44,
justifyContent: "space-between",
}
const paperAppStyle = {
minHeight: 130,
maxHeight: 130,
overflow: "hidden",
width: "100%",
color: "white",
backgroundColor: theme.palette.surfaceColor,
padding: "12px 12px 0px 15px",
borderRadius: 5,
display: "flex",
boxSizing: "border-box",
position: "relative",
}
const chipStyle = {
backgroundColor: "#3d3f43",
marginRight: 5,
paddingLeft: 5,
paddingRight: 5,
height: 28,
cursor: "pointer",
borderColor: "#3d3f43",
color: "white",
}
const WorkflowPaper = (props) => {
const { data } = props;
let navigate = useNavigate();
const [open, setOpen] = React.useState(false);
const [anchorEl, setAnchorEl] = React.useState(null);
const appGroup = data.action_references === undefined || data.action_references === null ? [] : data.action_references
//console.log("Workflow: ", data)
var boxColor = "#86c142";
var parsedName = data.name;
if (
parsedName !== undefined &&
parsedName !== null &&
parsedName.length > 20
) {
parsedName = parsedName.slice(0, 21) + "..";
}
const imageStyle = {
width: 24,
height: 24,
marginRight: 10,
border: "1px solid rgba(255,255,255,0.3)",
}
var image = data.creator_info !== undefined && data.creator_info !== null && data.creator_info.image !== undefined && data.creator_info.image !== null && data.creator_info.image.length > 0 ? <Avatar alt={data.creator} src={data.creator_info.image} style={imageStyle}/> : <Avatar alt={"shuffle_image"} src={theme.palette.defaultImage} style={imageStyle}/>
const creatorname = data.creator_info !== undefined && data.creator_info !== null && data.creator_info.username !== undefined && data.creator_info.username !== null && data.creator_info.username.length > 0 ? data.creator_info.username : ""
var orgName = "";
var orgId = "";
if ((data.objectID === undefined || data.objectID === null) && data.id !== undefined && data.id !== null) {
data.objectID = data.id
}
//console.log("IMG: ", data)
return (
<div style={{width: "100%", position: "relative",}}>
<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={{ display: "flex", maxHeight: 34 }}>
<Tooltip title={`${creatorname}`} placement="bottom">
<div
style={{ cursor: data.creator_info !== undefined ? "pointer" : "inherit" }}
onClick={() => {
if (data.creator_info !== undefined) {
navigate("/creators/"+data.creator_info.username)
}
}}
>
{image}
</div>
</Tooltip>
<Tooltip title={`Edit ${data.name}`} placement="bottom">
<Typography
variant="body1"
style={{
marginBottom: 0,
paddingBottom: 0,
maxHeight: 30,
flex: 10,
}}
>
<Link
to={"/workflows/" + data.objectID}
style={{ textDecoration: "none", color: "inherit" }}
>
{parsedName}
</Link>
</Typography>
</Tooltip>
</Grid>
<Grid item style={workflowActionStyle}>
{appGroup.length > 0 ?
<div style={{display: "flex", marginTop: 8, }}>
<AvatarGroup max={4} style={{marginLeft: 5, maxHeight: 24,}}>
{appGroup.map((app, index) => {
return (
<div
key={index}
style={{
height: 24,
width: 24,
filter: "brightness(0.6)",
cursor: "pointer",
}}
onClick={() => {
navigate("/apps/"+app.id)
}}
>
<Tooltip color="primary" title={app.name} placement="bottom">
<Avatar alt={app.name} src={app.image_url} style={{width: 24, height: 24}}/>
</Tooltip>
</div>
)
})}
</AvatarGroup>
</div>
:
<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",
}}
>
{data.actions === undefined || data.actions === null ? 1 : data.actions.length}
</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",
}}
>
{data.triggers === undefined || data.triggers === null ? 1 : data.triggers.length}
</Typography>
</span>
</Tooltip>
<Tooltip color="primary" title="Subflows used" placement="bottom">
<span
style={{
marginLeft: 15,
display: "flex",
color: "#979797",
cursor: "pointer",
}}
onClick={() => {
}}
>
<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",
}}
>
{0}
</Typography>
</span>
</Tooltip>
</Grid>
<Grid
item
style={{
justifyContent: "left",
overflow: "hidden",
marginTop: 5,
}}
>
{data.tags !== undefined && data.tags !== null
? data.tags.map((tag, index) => {
if (index >= 3) {
return null;
}
return (
<Chip
key={index}
style={chipStyle}
label={tag}
variant="outlined"
color="primary"
/>
);
})
: null}
</Grid>
</Grid>
</Paper>
</div>
)
}
export default WorkflowPaper