Loads of frontend fixes

This commit is contained in:
Frikky
2024-02-05 23:47:05 +01:00
parent cb9be52245
commit 8c640cbfd1
14 changed files with 885 additions and 608 deletions
+3 -3
View File
@@ -59,9 +59,9 @@ SHUFFLE_ORBORUS_STARTUP_DELAY= # Used for setting up a startup delay for Orbor
SHUFFLE_SKIPSSL_VERIFY=true SHUFFLE_SKIPSSL_VERIFY=true
IS_KUBERNETES=false # Used for controlling if the environment should run in kubernetes or not IS_KUBERNETES=false # Used for controlling if the environment should run in kubernetes or not
SHUFFLE_BASE_IMAGE_NAME=shuffle #SHUFFLE_BASE_IMAGE_NAME=shuffle
SHUFFLE_BASE_IMAGE_REGISTRY=ghcr.io #SHUFFLE_BASE_IMAGE_REGISTRY=ghcr.io
SHUFFLE_BASE_IMAGE_TAG_SUFFIX="-1.3.1" #SHUFFLE_BASE_IMAGE_TAG_SUFFIX="-1.3.1"
# The eth0 interface inside a container corresponds # The eth0 interface inside a container corresponds
# to the virtual Ethernet interface that connects # to the virtual Ethernet interface that connects
+1 -1
View File
@@ -466,8 +466,8 @@ class AppBase:
"success": True, "success": True,
"status": request.status_code, "status": request.status_code,
"url": request.url, "url": request.url,
"headers": parsedheaders,
"body": jsondata, "body": jsondata,
"headers": parsedheaders,
"cookies":cookies, "cookies":cookies,
}) })
except Exception as e: except Exception as e:
+1 -1
View File
@@ -615,7 +615,7 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho
</List> </List>
</div> </div>
<div style={{flex: 1, marginTop: 10,}}> <div style={{flex: 1, marginTop: 10,}}>
<SearchField serverside={serverside} userdata={userdata} /> <SearchField globalUrl={globalUrl} serverside={serverside} userdata={userdata} />
</div> </div>
<div style={{flex: 1, display: "flex", flexDirection: "row-reverse"}}> <div style={{flex: 1, display: "flex", flexDirection: "row-reverse"}}>
<List style={{display: 'flex', flexDirection: 'row-reverse'}} component="nav"> <List style={{display: 'flex', flexDirection: 'row-reverse'}} component="nav">
+2 -2
View File
@@ -741,7 +741,7 @@ const Header = (props) => {
> >
<div style={{maxWidth: 70, minWidth: 70, }}/> <div style={{maxWidth: 70, minWidth: 70, }}/>
<span style={{marginTop: 8, marginRight: 15, }}> <span style={{marginTop: 8, marginRight: 15, }}>
<SearchField isHeader={true} isLoggedIn={isLoggedIn} isLoaded={isLoaded} serverside={serverside} userdata={userdata} small={true} rounded={true} /> <SearchField globalUrl={globalUrl} isHeader={true} isLoggedIn={isLoggedIn} isLoaded={isLoaded} serverside={serverside} userdata={userdata} small={true} rounded={true} />
</span> </span>
<Link to="/register" style={hrefStyle}> <Link to="/register" style={hrefStyle}>
<Button <Button
@@ -912,7 +912,7 @@ const Header = (props) => {
</List> </List>
</div> </div>
<div style={{ flex: 1, marginTop: 10, }}> <div style={{ flex: 1, marginTop: 10, }}>
<SearchField isHeader={true} isLoggedIn={isLoggedIn} isLoaded={isLoaded} serverside={serverside} userdata={userdata} hidemargins={true}/> <SearchField globalUrl={globalUrl} isHeader={true} isLoggedIn={isLoggedIn} isLoaded={isLoaded} serverside={serverside} userdata={userdata} hidemargins={true}/>
</div> </div>
<div style={{ flex: isLoggedIn ? null : 1, display: "flex", flexDirection: "row-reverse" }}> <div style={{ flex: isLoggedIn ? null : 1, display: "flex", flexDirection: "row-reverse" }}>
<List <List
+1 -1
View File
@@ -374,7 +374,7 @@ const AuthenticationOauth2 = (props) => {
}, },
"fields": parsedFields, "fields": parsedFields,
"type": "oauth2-app", "type": "oauth2-app",
"reference_workflow": workflowId, //"reference_workflow": workflowId,
} }
if (setNewAppAuth !== undefined) { if (setNewAppAuth !== undefined) {
+5 -2
View File
@@ -1644,13 +1644,11 @@ const ParsedAction = (props) => {
multiline={data.name.startsWith("${") && data.name.endsWith("}") ? true : multiline} multiline={data.name.startsWith("${") && data.name.endsWith("}") ? true : multiline}
helperText={returnHelperText(data.name, data.value)} helperText={returnHelperText(data.name, data.value)}
onClick={() => { onClick={() => {
console.log("Clicked field: ", clickedFieldId, data.name)
/* /*
setExpansionModalOpen(false); setExpansionModalOpen(false);
*/ */
if (setScrollConfig !== undefined && scrollConfig !== null && scrollConfig !== undefined && scrollConfig.selected !== clickedFieldId) { if (setScrollConfig !== undefined && scrollConfig !== null && scrollConfig !== undefined && scrollConfig.selected !== clickedFieldId) {
console.log("IN SCROLL CONFIG!")
scrollConfig.selected = clickedFieldId scrollConfig.selected = clickedFieldId
setScrollConfig(scrollConfig) setScrollConfig(scrollConfig)
@@ -3737,6 +3735,11 @@ const ParsedAction = (props) => {
break break
} }
} }
// Check if it starts with "Get List" and method is "Get"
if (params.inputProps.value.startsWith("Get List")) {
console.log("Get List")
}
} }
return ( return (
+76 -2
View File
@@ -2,6 +2,7 @@ import React, { useState, useEffect, useRef } from 'react';
import theme from '../theme.jsx'; import theme from '../theme.jsx';
import { useNavigate, Link, useParams } from "react-router-dom"; import { useNavigate, Link, useParams } from "react-router-dom";
import { toast } from "react-toastify"
import { import {
Chip, Chip,
@@ -41,7 +42,7 @@ const chipStyle = {
const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240") const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240")
const SearchData = props => { const SearchData = props => {
const { serverside, userdata, setModalOpen, modalOpen } = props const { serverside, globalUrl, userdata, setModalOpen, modalOpen } = props
let navigate = useNavigate(); let navigate = useNavigate();
const borderRadius = 3 const borderRadius = 3
const node = useRef() const node = useRef()
@@ -326,6 +327,57 @@ const SearchData = props => {
) )
} }
const activateApp = (name, appid, type) => {
if (globalUrl === undefined || globalUrl === null) {
console.log(`Global URL not set`)
return
}
if (name === undefined || name === null) {
name = ""
}
name = name.replaceAll("_", " ")
if (userdata === undefined || userdata === null || userdata.id === undefined) {
toast(`You need to be logged in to activate the ${name} app. Redirecting`)
setTimeout(() => {
navigate(`/register?message=You need to be logged in to use the ${name} app.`)
}, 500)
return
}
const url = `${globalUrl}/api/v1/apps/${appid}/${type}`
fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
credentials: "include",
})
.then((response) => {
if (response.status !== 200) {
toast(`Failed to ${type} the app for your organization. Please try again or contact support@shuffler.io`)
}
return response.json()
})
.then((responseJson) => {
if (responseJson.success === false) {
toast(`Failed to ${type} the app for your organization. Please try again or contact support@shuffler.io for more info`)
} else {
toast(`App successfully ${type}d. Please refresh the page to use it.`)
}
})
.catch(error => {
//toast(error.toString())
console.log("Activate app error: ", error.toString())
});
}
const AppHits = ({ hits }) => { const AppHits = ({ hits }) => {
const [mouseHoverIndex, setMouseHoverIndex] = useState(0) const [mouseHoverIndex, setMouseHoverIndex] = useState(0)
@@ -431,7 +483,6 @@ const SearchData = props => {
return ( return (
<Link key={hit.objectID} to={parsedUrl} style={{ textDecoration: "none", color: "white", }} onClick={(event) => { <Link key={hit.objectID} to={parsedUrl} style={{ textDecoration: "none", color: "white", }} onClick={(event) => {
setSearchOpen(true) setSearchOpen(true)
setModalOpen(false) setModalOpen(false)
aa('init', { aa('init', {
appId: searchClient.appId, appId: searchClient.appId,
@@ -474,6 +525,29 @@ const SearchData = props => {
</IconButton> </IconButton>
</ListItemSecondaryAction> </ListItemSecondaryAction>
*/} */}
<Button
variant="outlined"
color="secondary"
onClick={(e) => {
e.preventDefault()
e.stopPropagation()
console.log("OBJECT CHANGE: ", hit.objectID)
// This does nothing rofl
if (userdata.active_apps === undefined || userdata.active_apps === null) {
activateApp(hit.name, hit.objectID, "activate")
} else {
if (userdata.active_apps.includes(hit.objectID)) {
activateApp(hit.name, hit.objectID, "deactivate")
} else {
activateApp(hit.name, hit.objectID, "activate")
}
}
}}
>
{userdata.active_apps !== undefined && userdata.active_apps !== null && userdata.active_apps.includes(hit.objectID) ? "Deactivate" : "Activate"}
</Button>
</ListItem> </ListItem>
</Link> </Link>
) )
+1 -1
View File
@@ -98,7 +98,7 @@ const SearchField = props => {
</div> </div>
: null} : null}
<DialogContent style={{}}> <DialogContent style={{}}>
<SearchBox setModalOpen={setModalOpen} modalOpen={modalOpen} serverside={serverside} userdata={userdata} /> <SearchBox globalUrl={globalUrl} setModalOpen={setModalOpen} modalOpen={modalOpen} serverside={serverside} userdata={userdata} />
</DialogContent> </DialogContent>
<Divider style={{overflow: "hidden"}}/> <Divider style={{overflow: "hidden"}}/>
<span style={{display:"flex", width:"100%", height:30}}> <span style={{display:"flex", width:"100%", height:30}}>
+117 -112
View File
@@ -236,8 +236,6 @@ const CodeEditor = (props) => {
//var newitem = JSON.parse(base); //var newitem = JSON.parse(base);
var newitem = validateJson(base).result var newitem = validateJson(base).result
to_be_copied = "$" + base_node_name.toLowerCase().replaceAll(" ", "_"); to_be_copied = "$" + base_node_name.toLowerCase().replaceAll(" ", "_");
for (let copykey in copy.namespace) { for (let copykey in copy.namespace) {
if (copy.namespace[copykey].includes("Results for")) { if (copy.namespace[copykey].includes("Results for")) {
@@ -825,7 +823,7 @@ const CodeEditor = (props) => {
console.log("ERR IN INPUT: ", e) console.log("ERR IN INPUT: ", e)
} }
console.log("Got output for: ", fullpath, new_input, actionlist[k].example, typeof new_input) //console.log("Got output for: ", fullpath, new_input, actionlist[k].example, typeof new_input)
if (typeof new_input === "object") { if (typeof new_input === "object") {
new_input = JSON.stringify(new_input) new_input = JSON.stringify(new_input)
@@ -1078,114 +1076,122 @@ const CodeEditor = (props) => {
*/} */}
{ isFileEditor ? null : { isFileEditor ? null :
<div style={{display: "flex", maxHeight: 40, }}> <div style={{display: "flex", maxHeight: 40, }}>
<Button {selectedAction.name === "execute_python" ?
id="basic-button" <Typography variant="body1" style={{marginTop: 5, }}>
aria-haspopup="true" Run Python Code
aria-controls={liquidOpen ? 'basic-menu' : undefined} </Typography>
aria-expanded={liquidOpen ? 'true' : undefined} :
variant="outlined" <div style={{display: "flex", }}>
color="secondary" <Button
style={{ id="basic-button"
textTransform: "none", aria-haspopup="true"
width: 100, aria-controls={liquidOpen ? 'basic-menu' : undefined}
}} aria-expanded={liquidOpen ? 'true' : undefined}
onClick={(event) => { variant="outlined"
setAnchorEl(event.currentTarget); color="secondary"
}} style={{
> textTransform: "none",
Filters width: 100,
</Button> }}
<Menu onClick={(event) => {
id="basic-menu" setAnchorEl(event.currentTarget);
anchorEl={anchorEl} }}
open={liquidOpen} >
onClose={() => { Filters
setAnchorEl(null); </Button>
}} <Menu
MenuListProps={{ id="basic-menu"
'aria-labelledby': 'basic-button', anchorEl={anchorEl}
}} open={liquidOpen}
> onClose={() => {
{liquidFilters.map((item, index) => { setAnchorEl(null);
return ( }}
<MenuItem key={index} onClick={() => { MenuListProps={{
handleClick(item) 'aria-labelledby': 'basic-button',
}}>{item.name}</MenuItem> }}
) >
})} {liquidFilters.map((item, index) => {
</Menu> return (
<Button <MenuItem key={index} onClick={() => {
id="basic-button" handleClick(item)
aria-haspopup="true" }}>{item.name}</MenuItem>
aria-controls={mathOpen ? 'basic-menu' : undefined} )
aria-expanded={mathOpen ? 'true' : undefined} })}
variant="outlined" </Menu>
color="secondary" <Button
style={{ id="basic-button"
textTransform: "none", aria-haspopup="true"
width: 100, aria-controls={mathOpen ? 'basic-menu' : undefined}
}} aria-expanded={mathOpen ? 'true' : undefined}
onClick={(event) => { variant="outlined"
setAnchorEl2(event.currentTarget); color="secondary"
}} style={{
> textTransform: "none",
Math width: 100,
</Button> }}
<Menu onClick={(event) => {
id="basic-menu" setAnchorEl2(event.currentTarget);
anchorEl={anchorEl2} }}
open={mathOpen} >
onClose={() => { Math
setAnchorEl2(null); </Button>
}} <Menu
MenuListProps={{ id="basic-menu"
'aria-labelledby': 'basic-button', anchorEl={anchorEl2}
}} open={mathOpen}
> onClose={() => {
{mathFilters.map((item, index) => { setAnchorEl2(null);
return ( }}
<MenuItem key={index} onClick={() => { MenuListProps={{
handleClick(item) 'aria-labelledby': 'basic-button',
}}>{item.name}</MenuItem> }}
) >
})} {mathFilters.map((item, index) => {
</Menu> return (
<Button <MenuItem key={index} onClick={() => {
id="basic-button" handleClick(item)
aria-haspopup="true" }}>{item.name}</MenuItem>
aria-controls={pythonOpen ? 'basic-menu' : undefined} )
aria-expanded={pythonOpen ? 'true' : undefined} })}
variant="outlined" </Menu>
color="secondary" <Button
style={{ id="basic-button"
textTransform: "none", aria-haspopup="true"
width: 100, aria-controls={pythonOpen ? 'basic-menu' : undefined}
}} aria-expanded={pythonOpen ? 'true' : undefined}
onClick={(event) => { variant="outlined"
setAnchorEl3(event.currentTarget); color="secondary"
}} style={{
> textTransform: "none",
Python width: 100,
</Button> }}
<Menu onClick={(event) => {
id="basic-menu" setAnchorEl3(event.currentTarget);
anchorEl={anchorEl3} }}
open={pythonOpen} >
onClose={() => { Python
setAnchorEl3(null); </Button>
}} <Menu
MenuListProps={{ id="basic-menu"
'aria-labelledby': 'basic-button', anchorEl={anchorEl3}
}} open={pythonOpen}
> onClose={() => {
{pythonFilters.map((item, index) => { setAnchorEl3(null);
return ( }}
<MenuItem key={index} onClick={() => { MenuListProps={{
handleClick(item) 'aria-labelledby': 'basic-button',
}}>{item.name}</MenuItem> }}
) >
})} {pythonFilters.map((item, index) => {
</Menu> return (
<MenuItem key={index} onClick={() => {
handleClick(item)
}}>{item.name}</MenuItem>
)
})}
</Menu>
</div>
}
<Button <Button
id="basic-button" id="basic-button"
aria-haspopup="true" aria-haspopup="true"
@@ -1589,7 +1595,6 @@ const CodeEditor = (props) => {
maxHeight: 35, maxHeight: 35,
minWidth: 70, minWidth: 70,
}} }}
variant="contained"
onClick={() => { onClick={() => {
executeSingleAction(expOutput) executeSingleAction(expOutput)
}} }}
+365 -205
View File
@@ -140,6 +140,11 @@ const Admin = (props) => {
const classes = useStyles(); const classes = useStyles();
let navigate = useNavigate(); let navigate = useNavigate();
const [logsViewModal, setLogsViewModal] = React.useState(false);
const [userLogViewing, setUserLogViewing] = React.useState({});
const [ipSelected, setIpSelected] = React.useState("");
const [logsLoading, setLogsLoading] = React.useState(true);
const [logs, setLogs] = React.useState([]);
const [firstRequest, setFirstRequest] = React.useState(true); const [firstRequest, setFirstRequest] = React.useState(true);
const [orgRequest, setOrgRequest] = React.useState(true); const [orgRequest, setOrgRequest] = React.useState(true);
const [modalUser, setModalUser] = React.useState({}); const [modalUser, setModalUser] = React.useState({});
@@ -3343,6 +3348,111 @@ If you're interested, please let me know a time that works for you, or set up a
backgroundColor: theme.palette.inputColor, backgroundColor: theme.palette.inputColor,
}} }}
/> />
{logsViewModal ?
<Dialog
open={logsViewModal}
onClose={() => {
setLogsViewModal(false);
}}
PaperProps={{
style: {
backgroundColor: theme.palette.surfaceColor,
color: "white",
minWidth: "1200px",
minHeight: "320px",
},
}}
>
<DialogTitle>
<span style={{ color: "white" }}>User Logs</span>
</DialogTitle>
<DialogContent>
{/* ask user for which IP they want to see logs for by iterating of user.login_info */}
<FormControl fullWidth>
<InputLabel style={{ size: 10 }} id="user-ip-simple-select-label">
User IP
</InputLabel>
<Select
labelId="user-ip-simple-select-label"
id="user-ip-simple-select"
onChange={async (event) => {
setIpSelected(event.target.value);
await getLogs(event.target.value, userLogViewing.id);
}}
>
{(() => {
const uniqueIPs = new Set();
return userLogViewing.login_info.map((data, index) => {
if (data.ip.includes("127.0.0.1") || uniqueIPs.has(data.ip)) {
return null;
}
uniqueIPs.add(data.ip);
return (
<MenuItem key={index} value={data.ip}>
{data.ip}
</MenuItem>
);
});
})()}
</Select>
</FormControl>
{logsLoading && ipSelected.length !== 0 ?
<div style={{ marginTop: 20, marginBottom: 20, display: 'flex', alignItems: 'center' }}>
<CircularProgress style={{ marginRight: 10 }} />
<Typography>Loading logs</Typography>
</div>
: null}
<List>
{logs.map((data, index) => (
// redirect user to logs
// using request id or trace id
<ListItem key={index} style={{ backgroundColor: index % 2 === 0 ? "#1f2023" : "#27292d" }}>
<ListItemText
primary={new Date(data.start_time.seconds * 1000).toISOString().slice(0, 10)}
style={{
minWidth: 150,
maxWidth: 150,
}}
/>
<ListItemText
primary={data.status}
style={{
minWidth: 70,
maxWidth: 70,
}}
/>
<ListItemText
primary={data.method}
style={{
minWidth: 150,
maxWidth: 150,
}}
/>
<ListItemText
primary={data.resource}
style={{
minWidth: 700,
maxWidth: 700,
overflow: "hidden",
}}
/>
</ListItem>
))}
</List>
</DialogContent>
</Dialog>
: null}
<List> <List>
<ListItem> <ListItem>
<ListItemText <ListItemText
@@ -3392,6 +3502,7 @@ If you're interested, please let me know a time that works for you, or set up a
primary="Last Login" primary="Last Login"
style={{ minWidth: 150, maxWidth: 150, }} style={{ minWidth: 150, maxWidth: 150, }}
/> />
</ListItem> </ListItem>
{users === undefined || users === null {users === undefined || users === null
? null ? null
@@ -3418,223 +3529,232 @@ If you're interested, please let me know a time that works for you, or set up a
} }
} }
return ( var userData = data.username
<ListItem key={index} style={{ backgroundColor: bgColor }}> if (userdata.support === true) {
<ListItemText userData = <a style={{ cursor: "pointer", textDecoration: 'underline', textDecorationColor: '#F76742', color: '#F76742' }} onClick={() => {
primary={data.username} setLogsViewModal(true)
style={{ setUserLogViewing(data)
minWidth: 350, }}
maxWidth: 350, >{data.username}</a>
overflow: "hidden", }
}}
/>
<ListItemText return (
style={{ marginLeft: 10, maxWidth: 100, minWidth: 100 }} <ListItem key={index} style={{ backgroundColor: bgColor }}>
primary={ <ListItemText
data.apikey === undefined || primary={userData}
data.apikey.length === 0 ? ( style={{
"" minWidth: 350,
) : ( maxWidth: 350,
<Tooltip overflow: "hidden",
title={"Copy Api Key"} }}
style={{}} />
aria-label={"Copy APIkey"}
>
<IconButton
style={{}}
onClick={() => {
const elementName = "copy_element_shuffle";
var copyText =
document.getElementById(elementName);
if (
copyText !== null &&
copyText !== undefined
) {
const clipboard = navigator.clipboard;
if (clipboard === undefined) {
toast(
"Can only copy over HTTPS (port 3443)"
);
return;
}
navigator.clipboard.writeText(data.apikey); <ListItemText
copyText.select(); style={{ marginLeft: 10, maxWidth: 100, minWidth: 100 }}
copyText.setSelectionRange( primary={
0, data.apikey === undefined ||
99999 data.apikey.length === 0 ? (
); /* For mobile devices */ ""
) : (
<Tooltip
title={"Copy Api Key"}
style={{}}
aria-label={"Copy APIkey"}
>
<IconButton
style={{}}
onClick={() => {
const elementName = "copy_element_shuffle";
var copyText =
document.getElementById(elementName);
if (
copyText !== null &&
copyText !== undefined
) {
const clipboard = navigator.clipboard;
if (clipboard === undefined) {
toast(
"Can only copy over HTTPS (port 3443)"
);
return;
}
/* Copy the text inside the text field */ navigator.clipboard.writeText(data.apikey);
document.execCommand("copy"); copyText.select();
copyText.setSelectionRange(
0,
99999
); /* For mobile devices */
toast("Apikey copied to clipboard"); /* Copy the text inside the text field */
} document.execCommand("copy");
}}
>
<FileCopyIcon
style={{ color: "rgba(255,255,255,0.8)" }}
/>
</IconButton>
</Tooltip>
)
}
/>
<ListItemText toast("Apikey copied to clipboard");
primary={ }
<Select
SelectDisplayProps={{
style: {
marginLeft: 10,
},
}}
value={data.role}
fullWidth
onChange={(e) => {
console.log("VALUE: ", e.target.value);
setUser(data.id, "role", e.target.value);
}}
style={{
backgroundColor: theme.palette.surfaceColor,
color: "white",
height: "50px",
}} }}
> >
<MenuItem <FileCopyIcon
style={{ style={{ color: "rgba(255,255,255,0.8)" }}
backgroundColor: theme.palette.inputColor, />
color: "white", </IconButton>
}} </Tooltip>
value={"admin"} )
> }
Org Admin />
</MenuItem>
<MenuItem
style={{
backgroundColor: theme.palette.inputColor,
color: "white",
}}
value={"user"}
>
Org User
</MenuItem>
<MenuItem
style={{
backgroundColor: theme.palette.inputColor,
color: "white",
}}
value={"org-reader"}
>
Org Reader
</MenuItem>
</Select>
}
style={{ minWidth: 135, maxWidth: 135, marginRight: 15 }}
/>
<ListItemText
primary={data.active ? "True" : "False"}
style={{ minWidth: 100, maxWidth: 100 }}
/>
<ListItemText
primary={
data.login_type === undefined ||
data.login_type === null ||
data.login_type.length === 0
? "Normal"
: data.login_type
}
style={{ minWidth: 100, maxWidth: 100 }}
/>
<ListItemText
primary={
data.mfa_info !== undefined &&
data.mfa_info !== null &&
data.mfa_info.active === true
? "Active"
: "Inactive"
}
style={{ minWidth: 100, maxWidth: 100 }}
/>
{selectedOrganization.child_orgs !== undefined &&
selectedOrganization.child_orgs !== null &&
selectedOrganization.child_orgs.length > 0 ? (
<ListItemText
style={{ display: "flex" }}
primary={
data.orgs === undefined || data.orgs === null
? 0
: data.orgs.length - 1
}
/>
) : null}
<ListItemText style={{ display: "flex", minWidth: 100, maxWidth: 100, }}>
<IconButton
onClick={() => {
setSelectedUserModalOpen(true);
setSelectedUser(data);
// Find matching orgs between current org and current user's access to those orgs <ListItemText
if ( primary={
userdata.orgs !== undefined && <Select
userdata.orgs !== null && SelectDisplayProps={{
userdata.orgs.length > 0 && style: {
selectedOrganization.child_orgs !== undefined && marginLeft: 10,
selectedOrganization.child_orgs !== null && },
selectedOrganization.child_orgs.length > 0 }}
) { value={data.role}
var active = []; fullWidth
for (var key in userdata.orgs) { onChange={(e) => {
const found = console.log("VALUE: ", e.target.value);
selectedOrganization.child_orgs.find( setUser(data.id, "role", e.target.value);
(item) => item.id === userdata.orgs[key].id }}
); style={{
if (found !== null && found !== undefined) { backgroundColor: theme.palette.surfaceColor,
if ( color: "white",
data.orgs === undefined || height: "50px",
data.orgs === null }}
) { >
continue; <MenuItem
} style={{
backgroundColor: theme.palette.inputColor,
color: "white",
}}
value={"admin"}
>
Org Admin
</MenuItem>
<MenuItem
style={{
backgroundColor: theme.palette.inputColor,
color: "white",
}}
value={"user"}
>
Org User
</MenuItem>
<MenuItem
style={{
backgroundColor: theme.palette.inputColor,
color: "white",
}}
value={"org-reader"}
>
Org Reader
</MenuItem>
</Select>
}
style={{ minWidth: 135, maxWidth: 135, marginRight: 15 }}
/>
<ListItemText
primary={data.active ? "True" : "False"}
style={{ minWidth: 100, maxWidth: 100 }}
/>
<ListItemText
primary={
data.login_type === undefined ||
data.login_type === null ||
data.login_type.length === 0
? "Normal"
: data.login_type
}
style={{ minWidth: 100, maxWidth: 100 }}
/>
<ListItemText
primary={
data.mfa_info !== undefined &&
data.mfa_info !== null &&
data.mfa_info.active === true
? "Active"
: "Inactive"
}
style={{ minWidth: 100, maxWidth: 100 }}
/>
{selectedOrganization.child_orgs !== undefined &&
selectedOrganization.child_orgs !== null &&
selectedOrganization.child_orgs.length > 0 ? (
<ListItemText
style={{ display: "flex" }}
primary={
data.orgs === undefined || data.orgs === null
? 0
: data.orgs.length - 1
}
/>
) : null}
<ListItemText style={{ display: "flex", minWidth: 100, maxWidth: 100, }}>
<IconButton
onClick={() => {
setSelectedUserModalOpen(true);
setSelectedUser(data);
const subfound = data.orgs.find( // Find matching orgs between current org and current user's access to those orgs
(item) => item === found.id if (
); userdata.orgs !== undefined &&
if ( userdata.orgs !== null &&
subfound !== null && userdata.orgs.length > 0 &&
subfound !== undefined selectedOrganization.child_orgs !== undefined &&
) { selectedOrganization.child_orgs !== null &&
active.push(subfound); selectedOrganization.child_orgs.length > 0
} ) {
} var active = [];
for (var key in userdata.orgs) {
const found =
selectedOrganization.child_orgs.find(
(item) => item.id === userdata.orgs[key].id
);
if (found !== null && found !== undefined) {
if (
data.orgs === undefined ||
data.orgs === null
) {
continue;
} }
setMatchingOrganizations(active); const subfound = data.orgs.find(
(item) => item === found.id
);
if (
subfound !== null &&
subfound !== undefined
) {
active.push(subfound);
}
} }
}} }
>
<EditIcon color="primary" /> setMatchingOrganizations(active);
</IconButton> }
{/*<Button }}
onClick={() => { >
generateApikey(data) <EditIcon color="primary" />
}} </IconButton>
disabled={data.role === "admin" && data.username !== userdata.username} {/*<Button
variant="outlined" onClick={() => {
color="primary" generateApikey(data)
> }}
New apikey disabled={data.role === "admin" && data.username !== userdata.username}
</Button>*/} variant="outlined"
</ListItemText> color="primary"
<ListItemText >
style={{ minWidth: 150, maxWidth: 150, }} New apikey
primary={lastLogin} </Button>*/}
><span/> </ListItemText>
</ListItemText> <ListItemText
</ListItem> style={{ minWidth: 150, maxWidth: 150, }}
); primary={lastLogin}
})} ><span/>
</ListItemText>
</ListItem>
);
})}
</List> </List>
</div> </div>
) : null; ) : null;
@@ -4131,6 +4251,46 @@ If you're interested, please let me know a time that works for you, or set up a
</div> </div>
) : null; ) : null;
const getLogs = async (ip, userId) => {
setLogsLoading(true);
console.log("logs loading: ", logsLoading);
fetch(`${globalUrl}/api/v1/users/${userId}/audit?user_ip=${ip}`, {
mode: "cors",
method: "GET",
credentials: "include",
crossDomain: true,
withCredentials: true,
headers: {
"Content-Type": "application/json; charset=utf-8",
},
})
.then((response) => {
return response.json();
})
.then((responseJson) => {
console.log("ResponseJSON: ", responseJson);
if (responseJson.success === true) {
setLogs(responseJson.logs);
} else {
if (responseJson.success === false || responseJson.reason !== undefined) {
console.log("Reason given: ", responseJson.reason)
toast("Failed getting logs: " + responseJson.reason)
setLogs([])
} else {
toast("Failed getting logs");
}
}
console.log("logs loading now: ", logsLoading);
setLogsLoading(false);
})
.catch((error) => {
console.log("Error: ", error);
toast("Failed getting logs. Please contact: ", error);
console.log("logs loading now: ", logsLoading);
setLogsLoading(false);
});
};
const changeRecommendation = (recommendation, action) => { const changeRecommendation = (recommendation, action) => {
const data = { const data = {
action: action, action: action,
+59 -82
View File
@@ -17,7 +17,7 @@ import { ToastContainer, toast } from "react-toastify"
import { isMobile } from "react-device-detect" import { isMobile } from "react-device-detect"
import aa from 'search-insights' import aa from 'search-insights'
import Drift from "react-driftjs"; import Drift from "react-driftjs";
import ShuffleCodeEditor from "../components/ShuffleCodeEditor.jsx"; import { CodeHandler, Img, OuterLink, } from "../views/Docs.jsx";
import { InstantSearch, Configure, connectSearchBox, connectHits, Index } from 'react-instantsearch-dom'; import { InstantSearch, Configure, connectSearchBox, connectHits, Index } from 'react-instantsearch-dom';
import algoliasearch from 'algoliasearch/lite'; import algoliasearch from 'algoliasearch/lite';
@@ -116,17 +116,12 @@ import {
AutoAwesome as AutoAwesomeIcon, AutoAwesome as AutoAwesomeIcon,
} from "@mui/icons-material"; } from "@mui/icons-material";
import * as cytoscape from "cytoscape"; import * as cytoscape from "cytoscape";
import * as edgehandles from "cytoscape-edgehandles"; import * as edgehandles from "cytoscape-edgehandles";
//import * as clipboard from "cytoscape-clipboard";
//import undoRedo from "cytoscape-undo-redo";
//import cxtmenu from "cytoscape-cxtmenu";
import CytoscapeComponent from "react-cytoscapejs"; import CytoscapeComponent from "react-cytoscapejs";
import Draggable from "react-draggable"; import Draggable from "react-draggable";
import cytoscapestyle from "../defaultCytoscapeStyle.jsx"; import cytoscapestyle from "../defaultCytoscapeStyle.jsx";
import ShuffleCodeEditor from "../components/ShuffleCodeEditor.jsx";
import { validateJson, GetIconInfo } from "../views/Workflows.jsx"; import { validateJson, GetIconInfo } from "../views/Workflows.jsx";
import { GetParsedPaths, internalIds, } from "../views/Apps.jsx"; import { GetParsedPaths, internalIds, } from "../views/Apps.jsx";
@@ -842,48 +837,6 @@ const AngularWorkflow = (defaultprops) => {
}); });
}; };
function OuterLink(props) {
if (props.href.includes("http") || props.href.includes("mailto")) {
return (
<a
href={props.href}
style={{ color: "#f85a3e", textDecoration: "none" }}
>
{props.children}
</a>
);
}
return (
<Link
to={props.href}
style={{ color: "#f85a3e", textDecoration: "none" }}
>
{props.children}
</Link>
);
}
function Img(props) {
return <img style={{ maxWidth: "100%" }} alt={props.alt} src={props.src} />;
}
function CodeHandler(props) {
return (
<pre
style={{
padding: 15,
minWidth: "50%",
maxWidth: "100%",
backgroundColor: theme.palette.inputColor,
overflowX: "auto",
overflowY: "hidden",
}}
>
<code>{props.value}</code>
</pre>
);
}
function Heading(props) { function Heading(props) {
const element = React.createElement( const element = React.createElement(
`h${props.level}`, `h${props.level}`,
@@ -1021,8 +974,6 @@ const AngularWorkflow = (defaultprops) => {
return response.json(); return response.json();
}) })
.then((responseJson) => { .then((responseJson) => {
console.log("GOT A RESPONSE??")
// getWorkflowExecutionCount(id);
if (responseJson !== undefined && responseJson !== null && responseJson.executions !== undefined && responseJson.executions !== null) { if (responseJson !== undefined && responseJson !== null && responseJson.executions !== undefined && responseJson.executions !== null) {
// - means it's opposite // - means it's opposite
@@ -1035,8 +986,6 @@ const AngularWorkflow = (defaultprops) => {
tmpView = execution_id; tmpView = execution_id;
} }
console.log("EXECUTION ID: ", tmpView)
// Compare with currently selected item // Compare with currently selected item
if (tmpView !== undefined && tmpView !== null && tmpView.length > 0) { if (tmpView !== undefined && tmpView !== null && tmpView.length > 0) {
// Don't clean up if it's already open // Don't clean up if it's already open
@@ -1820,7 +1769,6 @@ const AngularWorkflow = (defaultprops) => {
} }
} }
console.log("FOUNDMISSING: ", foundmissing)
if (foundmissing) { if (foundmissing) {
//toast("This workflow contains a node that requires an execution argument. Please provide one.") //toast("This workflow contains a node that requires an execution argument. Please provide one.")
setExecutionRequestStarted(false) setExecutionRequestStarted(false)
@@ -2788,11 +2736,17 @@ const AngularWorkflow = (defaultprops) => {
if (response.status >= 500) { if (response.status >= 500) {
toast("Something went wrong while loading the workflow. Please reload.") toast("Something went wrong while loading the workflow. Please reload.")
} else { } else {
toast("You don't access to this workflow or loading failed. Redirecting to workflows in a few seconds..")
setTimeout(() => { // Check for execution_id in URL
window.location.pathname = "/workflows"; // don't redirect if it exists
}, 2000); const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search;
var execFound = new URLSearchParams(cursearch).get("execution_id");
if (execFound === null) {
toast(`You don't access to this workflow or loading failed. Redirecting to workflows in a few seconds..`)
setTimeout(() => {
window.location.pathname = "/workflows";
}, 2000);
}
} }
} }
@@ -8518,8 +8472,6 @@ const AngularWorkflow = (defaultprops) => {
} }
if (param.name === "headers") { if (param.name === "headers") {
console.log("Swap header? For now, yes. File found: ", fileid_found)
if (fileid_found) { if (fileid_found) {
newSelectedAction.parameters[paramkey].value = "" newSelectedAction.parameters[paramkey].value = ""
newSelectedAction.parameters[paramkey].autocompleted = true newSelectedAction.parameters[paramkey].autocompleted = true
@@ -8614,7 +8566,6 @@ const AngularWorkflow = (defaultprops) => {
if (workflow.actions !== undefined && workflow.actions !== null && workflow.actions.length > 0) { if (workflow.actions !== undefined && workflow.actions !== null && workflow.actions.length > 0) {
const foundActionIndex = workflow.actions.findIndex(actiondata => actiondata.id === newSelectedAction.id) const foundActionIndex = workflow.actions.findIndex(actiondata => actiondata.id === newSelectedAction.id)
console.log("Found action on index ", foundActionIndex)
if (foundActionIndex >= 0) { if (foundActionIndex >= 0) {
workflow.actions[foundActionIndex] = newSelectedAction workflow.actions[foundActionIndex] = newSelectedAction
setWorkflow(workflow) setWorkflow(workflow)
@@ -9444,14 +9395,18 @@ const AngularWorkflow = (defaultprops) => {
bottom: 10, bottom: 10,
left: 10, left: 10,
color: "rgba(255,255,255,0.6)", color: "rgba(255,255,255,0.6)",
zIndex: 10000,
}} }}
> >
Conditions can't be used for loops [ .# ]{" "} Conditions can't be used for loops [ .# ]{" "}
<a <a
rel="noopener noreferrer" rel="noopener noreferrer"
target="_blank" target="_blank"
href="https://shuffler.io/docs/workflows#conditions" href="/docs/workflows#conditions"
style={{ textDecoration: "none", color: "#f85a3e" }} style={{
textDecoration: "none",
color: "#f85a3e",
}}
> >
Learn more Learn more
</a> </a>
@@ -15935,7 +15890,10 @@ const AngularWorkflow = (defaultprops) => {
setSelectedResult(data); setSelectedResult(data);
setCodeModalOpen(true); setCodeModalOpen(true);
} else { } else {
toast("Please wait until the workflow is loaded and try again") toast("Please wait until the workflow is loaded and try again")
setCodeModalOpen(true)
setSelectedResult(data)
} }
}} }}
> >
@@ -16197,6 +16155,25 @@ const AngularWorkflow = (defaultprops) => {
}} }}
> >
<span id="top_bar"> <span id="top_bar">
<Tooltip
title="Suggest solution"
placement="top"
style={{ zIndex: 50000 }}
>
<IconButton
style={{
zIndex: 5000,
position: "absolute",
top: 34,
right: 210,
}}
onClick={(e) => {
e.preventDefault()
}}
>
<AutoFixHighIcon />
</IconButton>
</Tooltip>
<Tooltip <Tooltip
title="Find successful execution" title="Find successful execution"
placement="top" placement="top"
@@ -16210,32 +16187,32 @@ const AngularWorkflow = (defaultprops) => {
right: 170, right: 170,
}} }}
onClick={(e) => { onClick={(e) => {
e.preventDefault(); e.preventDefault()
if (workflowExecutions !== null) { if (workflowExecutions !== null) {
for (let execkey in workflowExecutions) { for (let execkey in workflowExecutions) {
const execution = workflowExecutions[execkey]; const execution = workflowExecutions[execkey];
if (execution.execution_argument.includes("too large")) { if (execution.execution_argument.includes("too large")) {
continue continue
} }
const result = execution.results.find((data) => data.status === "SUCCESS" && data.action.id === selectedResult.action.id) const result = execution.results.find((data) => data.status === "SUCCESS" && data.action.id === selectedResult.action.id)
if (result !== undefined) { if (result !== undefined) {
const oldstartnode = cy.getElementById(selectedResult.action.id); const oldstartnode = cy.getElementById(selectedResult.action.id);
if (oldstartnode !== undefined && oldstartnode !== null) { if (oldstartnode !== undefined && oldstartnode !== null) {
const foundname = oldstartnode.data("label") const foundname = oldstartnode.data("label")
if (foundname !== undefined && foundname !== null) { if (foundname !== undefined && foundname !== null) {
result.action.label = foundname result.action.label = foundname
} }
} }
setSelectedResult(result); setSelectedResult(result);
setUpdate(Math.random()); setUpdate(Math.random());
break; break;
} }
} }
} }
}} }}
> >
<DoneIcon style={{ color: "white" }} /> <DoneIcon style={{ color: "white" }} />
+7 -3
View File
@@ -264,7 +264,7 @@ export const appCategories = [
"name": "IAM", "name": "IAM",
"color": "#FFC107", "color": "#FFC107",
"icon": "iam", "icon": "iam",
"action_labels": ["Reset Password", "Enable user", "Disable user", "Get Identity", "Get Asset", "Search Identity", ], "action_labels": ["Reset Password", "Enable user", "Disable user", "Get Identity", "Get Asset", "Search Identity", "Get KMS Key",],
}, { }, {
"name": "Network", "name": "Network",
"color": "#FFC107", "color": "#FFC107",
@@ -1240,7 +1240,7 @@ const AppCreator = (defaultprops) => {
if (methodvalue.responses.default.content["text/plain"]["schema"]["format"] === "binary" && methodvalue.responses.default.content["text/plain"]["schema"]["type"] === "string") { if (methodvalue.responses.default.content["text/plain"]["schema"]["format"] === "binary" && methodvalue.responses.default.content["text/plain"]["schema"]["type"] === "string") {
newaction.example_response = "shuffle_file_download" newaction.example_response = "shuffle_file_download"
} }
} }
} }
} }
@@ -2174,6 +2174,10 @@ const AppCreator = (defaultprops) => {
queryitem.name.toLowerCase() == "ssl_verify" || queryitem.name.toLowerCase() == "ssl_verify" ||
queryitem.name.toLowerCase() == "queries" || queryitem.name.toLowerCase() == "queries" ||
queryitem.name.toLowerCase() == "headers" || queryitem.name.toLowerCase() == "headers" ||
queryitem.name.toLowerCase() == "list" ||
queryitem.name.toLowerCase() == "dict" ||
queryitem.name.toLowerCase() == "str" ||
queryitem.name.toLowerCase() == "int" ||
queryitem.name.toLowerCase() == "access_token") { queryitem.name.toLowerCase() == "access_token") {
/* /*
@@ -3748,7 +3752,7 @@ const AppCreator = (defaultprops) => {
}} }}
> >
<FormControl style={{ backgroundColor: surfaceColor, color: "white" }}> <FormControl style={{ backgroundColor: surfaceColor, color: "white" }}>
<DialogTitle style={{marginTop: 30, }}> <DialogTitle style={{marginTop: 45, }}>
<div style={{ color: "white" }}>New action</div> <div style={{ color: "white" }}>New action</div>
</DialogTitle> </DialogTitle>
<DialogContent style={{paddingBottom: 100, }}> <DialogContent style={{paddingBottom: 100, }}>
+237 -186
View File
@@ -3,10 +3,12 @@ import React, { useEffect, useState } from "react";
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import Markdown from 'react-markdown' import Markdown from 'react-markdown'
import theme from '../theme.jsx';
import ReactJson from "react-json-view";
import { isMobile } from "react-device-detect";
import { BrowserView, MobileView } from "react-device-detect"; import { BrowserView, MobileView } from "react-device-detect";
import { useParams, useNavigate, Link } from "react-router-dom"; import { useParams, useNavigate, Link } from "react-router-dom";
import { isMobile } from "react-device-detect"; import { validateJson, GetIconInfo } from "../views/Workflows.jsx";
import theme from '../theme.jsx';
import { import {
Grid, Grid,
@@ -30,6 +32,7 @@ import {
Edit as EditIcon, Edit as EditIcon,
KeyboardArrowRight as KeyboardArrowRightIcon, KeyboardArrowRight as KeyboardArrowRightIcon,
ExpandMore as ExpandMoreIcon, ExpandMore as ExpandMoreIcon,
FileCopy as FileCopyIcon
} from "@mui/icons-material"; } from "@mui/icons-material";
const Body = { const Body = {
@@ -60,6 +63,120 @@ const innerHrefStyle = {
textDecoration: "none", textDecoration: "none",
}; };
export const CopyToClipboard = (props) => {
const {text, style, onCopy} = props;
const parsedstyle = style !== undefined ? style : {
position: "absolute",
right: 0,
top: -10,
}
return (
<div
style={parsedstyle}
>
<IconButton
onClick={() => {
navigator.clipboard.writeText(text);
toast("Copied to clipboard")
}}
>
<FileCopyIcon />
</IconButton>
</div>
)
}
export const OuterLink = (props) => {
if (props.href.includes("http") || props.href.includes("mailto")) {
return (
<a
href={props.href}
style={{ color: "#f85a3e", textDecoration: "none" }}
>
{props.children}
</a>
);
}
return (
<Link
to={props.href}
style={{ color: "#f85a3e", textDecoration: "none" }}
>
{props.children}
</Link>
);
}
export const Img = (props) => {
return <img style={{ borderRadius: theme.palette.borderRadius, width: 750, maxWidth: "100%", marginTop: 15, marginBottom: 15, }} alt={props.alt} src={props.src} />;
}
export const CodeHandler = (props) => {
const propvalue = props.value !== undefined && props.value !== null ? props.value : props.children !== undefined && props.children !== null && props.children.length > 0 ? props.children[0] : ""
const validate = validateJson(propvalue)
var newprop = propvalue
if (validate.valid === false) {
// Check if https://shuffler.io in the url
// if so, then we change it for the current url
if (propvalue.includes("https://shuffler.io")) {
newprop = propvalue.replace("https://shuffler.io", window.location.origin)
}
// Check if it contains Bearer APIKEY
// If so, replace apikey
//if (newprop.includes("Bearer APIKEY")) {
// newprop = newprop.replace("Bearer APIKEY", "Bearer API
//}
}
return (
<div
style={{
padding: 15,
minWidth: "50%",
maxWidth: "100%",
backgroundColor: theme.palette.inputColor,
overflowY: "auto",
}}
>
{validate.valid === true ?
<ReactJson
src={validate.result}
theme={theme.palette.jsonTheme}
style={theme.palette.reactJsonStyle}
collapsed={false}
displayDataTypes={false}
name={""}
/>
:
<div style={{display: "flex", position: "relative", }}>
<code
style={{
// Wrap if larger than X
whiteSpace: "pre-wrap",
overflow: "auto",
marginRight: 40,
}}
>
{newprop}
</code>
<CopyToClipboard
text={newprop}
/>
</div>
}
</div>
)
}
const Docs = (defaultprops) => { const Docs = (defaultprops) => {
const { globalUrl, selectedDoc, serverside, serverMobile } = defaultprops; const { globalUrl, selectedDoc, serverside, serverMobile } = defaultprops;
@@ -121,6 +238,123 @@ const Docs = (defaultprops) => {
//height: "50vh", //height: "50vh",
}; };
const Heading = (props) => {
const element = React.createElement(
`h${props.level}`,
{ style: { marginTop: props.level === 1 ? 20 : 50 } },
props.children
);
const [hover, setHover] = useState(false);
var extraInfo = "";
if (props.level === 1) {
extraInfo = (
<div
style={{
backgroundColor: theme.palette.inputColor,
padding: 15,
borderRadius: theme.palette.borderRadius,
marginBottom: 30,
display: "flex",
}}
>
<div style={{ flex: 3, display: "flex", vAlign: "center", position: "sticky", top: 50, }}>
{isMobile ? null : (
<Typography style={{ display: "inline", marginTop: 6 }}>
<a
rel="noopener noreferrer"
target="_blank"
href={selectedMeta.link}
style={{ textDecoration: "none", color: "#f85a3e" }}
>
<Button style={{ color: "white", }} variant="outlined" color="secondary">
<EditIcon /> &nbsp;&nbsp;Edit
</Button>
</a>
</Typography>
)}
{isMobile ? null : (
<div
style={{
height: "100%",
width: 1,
backgroundColor: "white",
marginLeft: 50,
marginRight: 50,
}}
/>
)}
<Typography style={{ display: "inline", marginTop: 11 }}>
{selectedMeta.read_time} minute
{selectedMeta.read_time === 1 ? "" : "s"} to read
</Typography>
</div>
<div style={{ flex: 2 }}>
{isMobile ||
selectedMeta.contributors === undefined ||
selectedMeta.contributors === null ? (
""
) : (
<div style={{ margin: 10, height: "100%", display: "inline" }}>
{selectedMeta.contributors.slice(0, 7).map((data, index) => {
return (
<a
key={index}
rel="noopener noreferrer"
target="_blank"
href={data.url}
target="_blank"
style={{ textDecoration: "none", color: "#f85a3e" }}
>
<Tooltip title={data.url} placement="bottom">
<img
alt={data.url}
src={data.image}
style={{
marginTop: 5,
marginRight: 10,
height: 40,
borderRadius: 40,
}}
/>
</Tooltip>
</a>
);
})}
</div>
)}
</div>
</div>
);
}
if (extraInfo !== "" && props.level === 1 && props.children !== undefined && props.children !== null && props.children.length > 0) {
if (props.children[0].toLowerCase().includes("privacy") || props.children[0].toLowerCase().includes("terms")) {
extraInfo = ""
}
}
return (
<Typography
onMouseOver={() => {
setHover(true);
}}
>
{props.level !== 1 ? (
<Divider
style={{
width: "90%",
marginTop: 40,
backgroundColor: theme.palette.inputColor,
}}
/>
) : null}
{element}
{extraInfo}
</Typography>
)
}
const SideBar = { const SideBar = {
minWidth: 300, minWidth: 300,
width: "20%", width: "20%",
@@ -382,190 +616,7 @@ const Docs = (defaultprops) => {
fontSize: isMobile ? "1.3rem" : "1.1rem", fontSize: isMobile ? "1.3rem" : "1.1rem",
}; };
function OuterLink(props) {
if (props.href.includes("http") || props.href.includes("mailto")) {
return (
<a
href={props.href}
style={{ color: "#f85a3e", textDecoration: "none" }}
>
{props.children}
</a>
);
}
return (
<Link
to={props.href}
style={{ color: "#f85a3e", textDecoration: "none" }}
>
{props.children}
</Link>
);
}
function Img(props) {
return <img style={{ borderRadius: theme.palette.borderRadius, width: 750, maxWidth: "100%", marginTop: 15, marginBottom: 15, }} alt={props.alt} src={props.src} />;
}
function CodeHandler(props) {
//console.log("Codehandler PROPS: ", props)
const propvalue = props.value !== undefined && props.value !== null ? props.value : props.children !== undefined && props.children !== null && props.children.length > 0 ? props.children[0] : ""
return (
<div
style={{
padding: 15,
minWidth: "50%",
maxWidth: "100%",
backgroundColor: theme.palette.inputColor,
overflowY: "auto",
}}
>
<code
style={{
// Wrap if larger than X
whiteSpace: "pre-wrap",
overflow: "auto",
}}
>{propvalue}</code>
</div>
);
}
const Heading = (props) => {
const element = React.createElement(
`h${props.level}`,
{ style: { marginTop: props.level === 1 ? 20 : 50 } },
props.children
);
const [hover, setHover] = useState(false);
var extraInfo = "";
if (props.level === 1) {
extraInfo = (
<div
style={{
backgroundColor: theme.palette.inputColor,
padding: 15,
borderRadius: theme.palette.borderRadius,
marginBottom: 30,
display: "flex",
}}
>
<div style={{ flex: 3, display: "flex", vAlign: "center", position: "sticky", top: 50, }}>
{mobile ? null : (
<Typography style={{ display: "inline", marginTop: 6 }}>
<a
rel="noopener noreferrer"
target="_blank"
href={selectedMeta.link}
style={{ textDecoration: "none", color: "#f85a3e" }}
>
<Button style={{ color: "white", }} variant="outlined" color="secondary">
<EditIcon /> &nbsp;&nbsp;Edit
</Button>
</a>
</Typography>
)}
{mobile ? null : (
<div
style={{
height: "100%",
width: 1,
backgroundColor: "white",
marginLeft: 50,
marginRight: 50,
}}
/>
)}
<Typography style={{ display: "inline", marginTop: 11 }}>
{selectedMeta.read_time} minute
{selectedMeta.read_time === 1 ? "" : "s"} to read
</Typography>
</div>
<div style={{ flex: 2 }}>
{mobile ||
selectedMeta.contributors === undefined ||
selectedMeta.contributors === null ? (
""
) : (
<div style={{ margin: 10, height: "100%", display: "inline" }}>
{selectedMeta.contributors.slice(0, 7).map((data, index) => {
return (
<a
key={index}
rel="noopener noreferrer"
target="_blank"
href={data.url}
target="_blank"
style={{ textDecoration: "none", color: "#f85a3e" }}
>
<Tooltip title={data.url} placement="bottom">
<img
alt={data.url}
src={data.image}
style={{
marginTop: 5,
marginRight: 10,
height: 40,
borderRadius: 40,
}}
/>
</Tooltip>
</a>
);
})}
</div>
)}
</div>
</div>
);
}
if (extraInfo !== "" && props.level === 1 && props.children !== undefined && props.children !== null && props.children.length > 0) {
if (props.children[0].toLowerCase().includes("privacy") || props.children[0].toLowerCase().includes("terms")) {
extraInfo = ""
}
}
return (
<Typography
onMouseOver={() => {
setHover(true);
}}
>
{props.level !== 1 ? (
<Divider
style={{
width: "90%",
marginTop: 40,
backgroundColor: theme.palette.inputColor,
}}
/>
) : null}
{element}
{/*hover ? <LinkIcon onMouseOver={() => {setHover(true)}} style={{cursor: "pointer", display: "inline", }} onClick={() => {
window.location.href += "#hello"
console.log(window.location)
//window.history.pushState('page2', 'Title', '/page2.php');
//window.history.replaceState('page2', 'Title', '/page2.php');
}} />
: ""
*/}
{extraInfo}
</Typography>
);
};
//React.createElement("p", {style: {color: "red", backgroundColor: "blue"}}, this.props.paragraph)
//function unicodeToChar(text) {
// return text.replace(/\\u[\dA-F]{4}/gi,
// function (match) {
// return String.fromCharCode(parseInt(match.replace(/\\u/g, ''), 16));
// }
// );
//}
const CustomButton = (props) => { const CustomButton = (props) => {
+10 -7
View File
@@ -484,7 +484,6 @@ export const validateJson = (showResult) => {
// This is where we start recursing // This is where we start recursing
if (jsonvalid) { if (jsonvalid) {
// Check fields if they can be parsed too // Check fields if they can be parsed too
//console.log("In this window for the data. Should look for list in result! Does recursion.")
try { try {
for (const [key, value] of Object.entries(result)) { for (const [key, value] of Object.entries(result)) {
if (typeof value === "string" && (value.startsWith("{") || value.startsWith("["))) { if (typeof value === "string" && (value.startsWith("{") || value.startsWith("["))) {
@@ -1649,6 +1648,7 @@ const Workflows = (props) => {
addFilter(e.target.innerHTML); addFilter(e.target.innerHTML);
}; };
const hasWorkflows = workflows === undefined || workflows === null || workflows.length === 0
const NewWorkflowPaper = () => { const NewWorkflowPaper = () => {
const [hover, setHover] = React.useState(false); const [hover, setHover] = React.useState(false);
@@ -1659,17 +1659,17 @@ const Workflows = (props) => {
minWidth: paperAppStyle.width, minWidth: paperAppStyle.width,
color: innerColor, color: innerColor,
padding: paperAppStyle.padding, padding: paperAppStyle.padding,
borderRadius: paperAppStyle.borderRadius,
display: "flex", display: "flex",
boxSizing: "border-box", boxSizing: "border-box",
position: "relative", position: "relative",
border: `2px solid ${innerColor}`, border: hasWorkflows ? `2px solid #f85a3e` : `2px solid ${innerColor}`,
cursor: "pointer", cursor: "pointer",
backgroundColor: hover ? "rgba(39,41,45,0.5)" : "rgba(39,41,45,1)", backgroundColor: hover ? "rgba(39,41,45,0.5)" : "rgba(39,41,45,1)",
borderRadius: paperAppStyle.borderRadius,
}; };
return ( return (
<Grid item xs={isMobile ? 12 : 4} style={{ padding: "12px 10px 12px 10px" }}> <Grid item xs={isMobile ? 12 : hasWorkflows ? 12 : 4} style={{ padding: "12px 10px 12px 10px" }}>
<Paper <Paper
square square
style={setupPaperStyle} style={setupPaperStyle}
@@ -1685,8 +1685,11 @@ const Workflows = (props) => {
}} }}
> >
<Tooltip title={`New Workflow`} placement="bottom"> <Tooltip title={`New Workflow`} placement="bottom">
<span style={{ textAlign: "center", minWidth: 300, margin: "auto" }}> <span style={{ textAlign: "center", minWidth: 240, margin: "auto" }}>
<AddCircleIcon style={{ height: 65, width: 65 }} /> <AddCircleIcon style={{ height: 65, width: 65 }} />
<Typography variant="h6" style={{ color: innerColor, margin: "auto" }}>
New Workflow
</Typography>
</span> </span>
</Tooltip> </Tooltip>
</Paper> </Paper>
@@ -3328,8 +3331,8 @@ const Workflows = (props) => {
</div> </div>
</div> </div>
<div style={{width: "100%", minHeight: isMobile ? 0 : 51, maxHeight: isMobile ? 0 : 51, marginTop: 10, }}> <div style={{width: "100%", minHeight: isMobile ? 0 : hasWorkflows ? 0 : 51, maxHeight: isMobile ? 0 : 51, marginTop: 10, }}>
{!isMobile && usecases !== null && usecases !== undefined && usecases.length > 0 ? {!isMobile && !hasWorkflows && usecases !== null && usecases !== undefined && usecases.length > 0 ?
<div style={{ display: "flex", }}> <div style={{ display: "flex", }}>
{usecases.map((usecase, index) => { {usecases.map((usecase, index) => {
//console.log(usecase) //console.log(usecase)