Fixed a lot of the weird suborg distribution workflow issues

This commit is contained in:
Frikky
2025-02-06 02:07:58 +01:00
parent f9e4a86245
commit b430e6c900
10 changed files with 1204 additions and 595 deletions
-9
View File
@@ -1743,15 +1743,6 @@ func handleExecution(id string, workflow shuffle.Workflow, request *http.Request
Environments: execInfo.Environments, Environments: execInfo.Environments,
} }
//executionRequestWrapper, err := getWorkflowQueue(ctx, environment)
//if err != nil {
// executionRequestWrapper = ExecutionRequestWrapper{
// Data: []ExecutionRequest{executionRequest},
// }
//} else {
// executionRequestWrapper.Data = append(executionRequestWrapper.Data, executionRequest)
//}
//log.Printf("Execution request: %#v", executionRequest) //log.Printf("Execution request: %#v", executionRequest)
executionRequest.Priority = workflowExecution.Priority executionRequest.Priority = workflowExecution.Priority
err = shuffle.SetWorkflowQueue(ctx, executionRequest, environment) err = shuffle.SetWorkflowQueue(ctx, executionRequest, environment)
+4 -4
View File
@@ -104,9 +104,10 @@ const EditWorkflow = (props) => {
const foundScroll = document.getElementById(scrollTo) const foundScroll = document.getElementById(scrollTo)
if (foundScroll !== null) { if (foundScroll !== null) {
// Smooth scroll // Smooth scroll
foundScroll.scrollIntoView({ behavior: "smooth" }) foundScroll.scrollIntoView({
behavior: "smooth",
})
} }
}, 200) }, 200)
setScrollDone(true) setScrollDone(true)
@@ -565,13 +566,12 @@ const EditWorkflow = (props) => {
fullWidth fullWidth
/> />
<Divider style={{ marginTop: 20, marginBottom: 20, }} /> <Divider id="mssp_control" style={{ marginTop: 20, marginBottom: 20, }} />
<Typography variant="h4" style={{ marginTop: 50, }}> <Typography variant="h4" style={{ marginTop: 50, }}>
MSSP controls MSSP controls
</Typography> </Typography>
<Typography variant="body1" style={{ marginTop: 50, }}> <Typography variant="body1" style={{ marginTop: 50, }}>
MSSP Suborg Distribution (<b>beta</b> - contact support@shuffler.io for more info) MSSP Suborg Distribution (<b>beta</b> - contact support@shuffler.io for more info)
</Typography> </Typography>
+4 -4
View File
@@ -889,7 +889,7 @@ useEffect(() => {
</Button> </Button>
</Box> </Box>
</Box> </Box>
<Box sx={{ display: "flex", flexDirection: "column", width:"100%", height: "100%", overflowY: "auto", overflowX: "hidden",transition: 'display 0.3s ease',paddingTop: 0.5 }} onMouseOver={()=>{!leftSideBarOpenByClick && setExpandLeftNav(true)}} onMouseLeave={()=>{!leftSideBarOpenByClick && setExpandLeftNav(false);setOpenAutocomplete(false)}}> <Box sx={{ display: "flex", flexDirection: "column", width:"100%", height: "100%", overflowY: "auto", overflowX: "hidden",transition: 'display 0.3s ease',paddingTop: 0.5 }} onMouseOver={()=>{(!leftSideBarOpenByClick || window?.location?.pathname?.includes("/workflows/")) && setExpandLeftNav(true)}} onMouseLeave={()=>{(!leftSideBarOpenByClick || window?.location?.pathname?.includes("/workflows/")) && setExpandLeftNav(false);setOpenAutocomplete(false)}}>
<Box <Box
sx={{ sx={{
display: "flex", display: "flex",
@@ -1194,7 +1194,7 @@ useEffect(() => {
sx={{ sx={{
display: "flex", display: "flex",
flexDirection: "row", flexDirection: "row",
marginTop: 1 marginTop: 0
}} }}
> >
<span style={{ display: "inline-block", width: "100%" }}> <span style={{ display: "inline-block", width: "100%" }}>
@@ -1229,7 +1229,7 @@ useEffect(() => {
width: 18, width: 18,
height: 18, height: 18,
marginRight: expandLeftNav ? 10 : 0, marginRight: expandLeftNav ? 10 : 0,
color: userdata?.support ? "inherit" : "#6F6F6F", color: "inherit",
}} }}
/> />
<span <span
@@ -1527,7 +1527,7 @@ useEffect(() => {
</Link> </Link>
<Link to="/admin?admin_tab=billingstats" style={hrefStyle}> <Link to={isCloud ? "/admin?admin_tab=billingstats" : "/admin?admin_tab=locations"} style={hrefStyle}>
<Button <Button
onClick={(event) => { onClick={(event) => {
setCurrentOpenTab("admin"); setCurrentOpenTab("admin");
+70 -21
View File
@@ -177,8 +177,8 @@ const ParsedAction = (props) => {
setAiQueryModalOpen, setAiQueryModalOpen,
} = props; } = props;
let navigate = useNavigate(); let navigate = useNavigate()
const classes = useStyles(); const classes = useStyles()
const [hideBody, setHideBody] = React.useState(false) const [hideBody, setHideBody] = React.useState(false)
const [activateHidingBodyButton, setActivateHidingBodyButton] = React.useState(false) const [activateHidingBodyButton, setActivateHidingBodyButton] = React.useState(false)
@@ -200,6 +200,7 @@ const ParsedAction = (props) => {
const [menuPosition, setMenuPosition] = useState(null); const [menuPosition, setMenuPosition] = useState(null);
const [uiBox, setUiBox] = useState(null); const [uiBox, setUiBox] = useState(null);
const isIntegration = selectedAction.app_id === "integration" const isIntegration = selectedAction.app_id === "integration"
const isAgent = selectedAction.app_id === "shuffle_agent"
const [distributeAuthToSuborgs, setDistributeAuthToSuborgs] = useState(selectedAction?.selectedAuthentication?.suborg_distributed || false) const [distributeAuthToSuborgs, setDistributeAuthToSuborgs] = useState(selectedAction?.selectedAuthentication?.suborg_distributed || false)
useEffect(() => { useEffect(() => {
@@ -208,7 +209,6 @@ const ParsedAction = (props) => {
} }
}, [expansionModalOpen]) }, [expansionModalOpen])
useEffect(() => { useEffect(() => {
// Changes the order of params to show in order: // Changes the order of params to show in order:
// auth, required, optional // auth, required, optional
@@ -244,15 +244,37 @@ const ParsedAction = (props) => {
var param = selectedActionParameters[paramkey] var param = selectedActionParameters[paramkey]
keyorder.push(param.name) keyorder.push(param.name)
if (param.configuration) { if (param?.configuration === true) {
auth.push(param) auth.push(param)
continue continue
} }
if (param?.value?.toLowerCase().includes("secret. replace")) { if (param?.value === undefined || param?.value === null) {
param.value = "" console.log("Invalid value: ", param)
continue
} }
if (typeof param.value === "array") {
param.value = param.value.join(",")
}
if (typeof param.value === "object") {
try {
// Check if it's an array or object
if (param.value.length !== undefined) {
param.value = param.value.join(",")
} else {
param.value = JSON.stringify(param.value)
}
} catch (e) {
console.log("Error parsing JSON for param value: ", param, e)
continue
}
}
if (param?.value?.toLowerCase()?.includes("secret. replace")) {
param.value = ""
}
if (selectedApp?.generated === true && param?.name === "body") { if (selectedApp?.generated === true && param?.name === "body") {
param.required = true param.required = true
@@ -260,11 +282,7 @@ const ParsedAction = (props) => {
continue continue
} }
//if (param.name.startsWith("${") && param.name.endsWith("}")) { if (param?.required === false && param?.name?.startsWith("${") && param?.name?.endsWith("}")) {
// console.log("PARAM: ", param)
//}
if (param.required === false && param.name.startsWith("${") && param.name.endsWith("}")) {
// Check if it's a required param // Check if it's a required param
param.autocompleted = true param.autocompleted = true
if (selectedAction.required_body_fields !== undefined && selectedAction.required_body_fields !== null && selectedAction.required_body_fields.length > 0) { if (selectedAction.required_body_fields !== undefined && selectedAction.required_body_fields !== null && selectedAction.required_body_fields.length > 0) {
@@ -280,16 +298,16 @@ const ParsedAction = (props) => {
continue continue
} }
if (param.name === "headers" || param.name === "queries") { if (param?.name === "headers" || param?.name === "queries") {
special_optional.push(param) special_optional.push(param)
continue continue
} }
if (hideBody && param?.description.includes("Generated")) { if (hideBody && param?.description?.includes("Generated")) {
continue continue
} }
if (param.field_active === true) { if (param?.field_active === true) {
param.autocompleted = true param.autocompleted = true
generated_optional.push(param) generated_optional.push(param)
continue continue
@@ -350,6 +368,17 @@ const ParsedAction = (props) => {
} }
} }
} }
// Fix apps with fewer actions
if (selectedApp !== undefined && selectedApp !== null && selectedApp.actions !== undefined && selectedApp.actions !== null && selectedApp.actions.length <= 1 && apps !== undefined && apps !== null && apps.length > 0) {
// 1. Check local storage (?)
// 2. Check the "apps" list
const foundApp = apps.find(app => app.id === selectedApp.id)
if (foundApp !== undefined && foundApp !== null && foundApp.actions !== undefined && foundApp.actions !== null && foundApp.actions.length > 1) {
setSelectedApp(foundApp)
}
}
}, []) }, [])
const keywords = [ const keywords = [
@@ -2040,6 +2069,7 @@ const ParsedAction = (props) => {
<div style={{ marginTop: 15, position: "relative", }}> <div style={{ marginTop: 15, position: "relative", }}>
<Typography style={{ color: "rgba(255,255,255,0.7)" }}>Authentication</Typography> <Typography style={{ color: "rgba(255,255,255,0.7)" }}>Authentication</Typography>
<Tooltip <Tooltip
arrow
title={ title={
workflow?.suborg_distribution?.length > 0 && Object.getOwnPropertyNames(selectedAction?.selectedAuthentication).length !== 0 ? ( workflow?.suborg_distribution?.length > 0 && Object.getOwnPropertyNames(selectedAction?.selectedAuthentication).length !== 0 ? (
<React.Fragment> <React.Fragment>
@@ -2049,7 +2079,7 @@ const ParsedAction = (props) => {
<Checkbox <Checkbox
checked={distributeAuthToSuborgs} checked={distributeAuthToSuborgs}
onChange={(event) => { onChange={(event) => {
changeDistribution(selectedAction?.selectedAuthentication) changeDistribution(selectedAction?.selectedAuthentication, "auth")
}} }}
name="distributeAuth" name="distributeAuth"
color="primary" color="primary"
@@ -2428,6 +2458,7 @@ const ParsedAction = (props) => {
{setNewSelectedAction !== undefined ? ( {setNewSelectedAction !== undefined ? (
<Autocomplete <Autocomplete
id="action_search" id="action_search"
disabled={isAgent || (selectedAction?.parent_controlled === true && workflow?.parentorg_workflow?.length > 0)}
autoHighlight autoHighlight
value={selectedAction} value={selectedAction}
classes={{ inputRoot: classes.inputRoot }} classes={{ inputRoot: classes.inputRoot }}
@@ -2628,6 +2659,7 @@ const ParsedAction = (props) => {
label={isIntegration ? "Choose a category" : "Find Actions"} label={isIntegration ? "Choose a category" : "Find Actions"}
variant="outlined" variant="outlined"
name={`disable_autocomplete_${Math.random()}`} name={`disable_autocomplete_${Math.random()}`}
disabled={isAgent || (selectedAction?.parent_controlled === true && workflow?.parentorg_workflow?.length > 0)}
/> />
</Tooltip> </Tooltip>
); );
@@ -2645,7 +2677,7 @@ const ParsedAction = (props) => {
> { > {
selectedActionParameters !== undefined && selectedActionParameters !== null && Object.getOwnPropertyNames(selectedAction).length > 0 && selectedActionParameters.length > 0 ? selectedActionParameters !== undefined && selectedActionParameters !== null && Object.getOwnPropertyNames(selectedAction).length > 0 && selectedActionParameters.length > 0 ?
<div style={{ marginTop: hideExtraTypes ? 10 : 30 }}> <div style={{ marginTop: hideExtraTypes ? 10 : 30 }}>
{isIntegration ? {isIntegration || isAgent ?
apps !== undefined && apps !== null && apps.length > 0 ? apps !== undefined && apps !== null && apps.length > 0 ?
<div style={{ display: "flex", maxWidth: 335, overflowX: "auto", overflowY: "hidden", }}> <div style={{ display: "flex", maxWidth: 335, overflowX: "auto", overflowY: "hidden", }}>
<div onClick={() => { <div onClick={() => {
@@ -2703,16 +2735,20 @@ const ParsedAction = (props) => {
</div> </div>
</Tooltip> </Tooltip>
</div> </div>
{apps.map((app, appIndex) => { {apps.map((app, appIndex) => {
if (app.categories === undefined || app.categories === null || app.categories.length === 0) { if (app.categories === undefined || app.categories === null || app.categories.length === 0) {
return null return null
} }
var newactionname = actionname.toLowerCase()
if (isAgent === true) {
newactionname = "ai"
}
var found = false var found = false
for (var key in app.categories) { for (var key in app.categories) {
if (app.categories[key].toLowerCase() !== actionname) { if (app.categories[key].toLowerCase() !== newactionname) {
continue continue
} }
@@ -2943,10 +2979,17 @@ const ParsedAction = (props) => {
data.variant = "STATIC_VALUE"; data.variant = "STATIC_VALUE";
} }
if (isIntegration && data.name === "app_name") { if ((isIntegration || isAgent) && data.name === "app_name") {
return null return null
} }
/*
// Somehow autogenerate from the app itself
if (isAgent && data.name == "model") {
//console.log("Options: ", data.options)
}
*/
if (data.value === "authgroup controlled") { if (data.value === "authgroup controlled") {
if (data?.name === "url" && authenticationType?.type === "oauth2-app") { if (data?.name === "url" && authenticationType?.type === "oauth2-app") {
} else { } else {
@@ -3891,11 +3934,17 @@ const ParsedAction = (props) => {
viewed_data = (viewed_data.charAt(0).toUpperCase() + viewed_data.slice(1)).replaceAll("_", " ") viewed_data = (viewed_data.charAt(0).toUpperCase() + viewed_data.slice(1)).replaceAll("_", " ")
// Check if it's selected or not and highlight
var selected = false
if (selectedActionParameters[count].value.includes(data)) {
selected = true
}
return ( return (
<MenuItem <MenuItem
key={data} key={data}
style={{ style={{
backgroundColor: theme.palette.inputColor, backgroundColor: selected ? theme.palette.backgroundColor : theme.palette.inputColor,
color: "white", color: "white",
}} }}
value={data} value={data}
+177 -94
View File
@@ -15,6 +15,7 @@ import {
Tooltip, Tooltip,
Typography, Typography,
IconButton, IconButton,
Switch,
} from '@mui/material'; } from '@mui/material';
import { toast } from "react-toastify" import { toast } from "react-toastify"
@@ -36,6 +37,9 @@ import {
Insights as InsightsIcon, Insights as InsightsIcon,
Replay as ReplayIcon, Replay as ReplayIcon,
EditNote as EditNoteIcon, EditNote as EditNoteIcon,
AccountTree as AccountTreeIcon,
Cached as CachedIcon,
FilterAltOff as FilterAltOffIcon
} from '@mui/icons-material'; } from '@mui/icons-material';
import { DataGrid, GridColDef, GridValueGetterParams } from '@mui/x-data-grid' import { DataGrid, GridColDef, GridValueGetterParams } from '@mui/x-data-grid'
@@ -73,6 +77,8 @@ const RuntimeDebugger = (props) => {
const [rowsPerPage, setRowsPerPage] = useState(10) const [rowsPerPage, setRowsPerPage] = useState(10)
const [resultRows, setResultRows] = useState([]) const [resultRows, setResultRows] = useState([])
const [selectedWorkflowExecutions, setSelectedWorkflowExecutions] = useState([]) const [selectedWorkflowExecutions, setSelectedWorkflowExecutions] = useState([])
const [suborgWorkflowRuns, setSuborgWorkflowRuns] = useState(false)
const [openWorkflowMenu, setOpenWorkflowMenu] = useState(false)
const [workflows, setWorkflows] = useState([ const [workflows, setWorkflows] = useState([
{"id": "", "name": "All Workflows",} {"id": "", "name": "All Workflows",}
]) ])
@@ -162,7 +168,7 @@ const RuntimeDebugger = (props) => {
}, maxworkflows*300) }, maxworkflows*300)
} }
const submitSearch = (workflowId, status, startTime, endTime, cursor, limit) => { const submitSearch = (workflowId, status, startTime, endTime, cursor, limit, suborg_runs) => {
handleWorkflowUsageCount(workflows) handleWorkflowUsageCount(workflows)
//setResultRows([]) //setResultRows([])
setSearchLoading(true) setSearchLoading(true)
@@ -175,6 +181,7 @@ const RuntimeDebugger = (props) => {
start_time: startTime, start_time: startTime,
end_time: endTime, end_time: endTime,
ignore_org: ignoreOrg, ignore_org: ignoreOrg,
suborg_runs: suborg_runs,
} }
fetch(`${globalUrl}/api/v1/workflows/search`, { fetch(`${globalUrl}/api/v1/workflows/search`, {
@@ -344,10 +351,28 @@ const RuntimeDebugger = (props) => {
source = "manual" source = "manual"
} }
var imageSource = "";
if (params?.row?.org?.id?.length > 0) {
if (params?.row?.org?.image?.length > 0){
imageSource = params.row.org.image
}else {
imageSource = "/images/no_image.png"
}
}else {
if (userdata.active_org.image?.length > 0){
imageSource = userdata.active_org.image
}else {
imageSource = "/images/no_image.png"
}
}
return ( return (
<span style={{}} onClick={() => { <span style={{}} onClick={() => {
//setStatus(params.row.status) //setStatus(params.row.status)
}}> }}>
{userdata?.active_org?.creator_org?.length === 0 ? (
<img src={imageSource} alt={source} style={{borderRadius: theme.palette?.borderRadius, height: imageSize, width: imageSize, }} />
) : null}
<Tooltip title={source} placement="top"> <Tooltip title={source} placement="top">
{foundSource} {foundSource}
</Tooltip> </Tooltip>
@@ -372,7 +397,7 @@ const RuntimeDebugger = (props) => {
headerName: 'Workflow Name', headerName: 'Workflow Name',
width: 250, width: 250,
renderCell: (params) => ( renderCell: (params) => (
<span style={{cursor: "pointer", }} onClick={() => { <div style={{ display: "flex", justifyContent: "center", alignItems: "center", cursor: "pointer", }} onClick={() => {
setWorkflowId(params.row.workflow.id) setWorkflowId(params.row.workflow.id)
for (let key in workflows) { for (let key in workflows) {
@@ -382,8 +407,18 @@ const RuntimeDebugger = (props) => {
} }
} }
}}> }}>
{params.row.workflow.name} <span>{params.row.workflow.name}</span>
</span> {params?.row?.org?.id?.length > 0 &&
<Tooltip title={(
<div style={{display: "flex", }}>
<img src={params.row.org.image || "/images/no_image.png"} alt={params.row.org.name} style={{height: 24, width: 24, borderRadius: 12, }} />
<Typography variant="body2" style={{marginLeft: 5, }}>{params.row.org.name}</Typography>
</div>
)} placement="top" arrow>
<AccountTreeIcon style={{color: "#B0B0B0", height: "24px", width: "24px", marginLeft: 10}} />
</Tooltip>
}
</div>
), ),
}, },
@@ -630,7 +665,7 @@ const RuntimeDebugger = (props) => {
return return
} }
submitSearch(workflowId, status, startTime, endTime, rowCursor, rowsPerPage) submitSearch(workflowId, status, startTime, endTime, rowCursor, rowsPerPage, suborgWorkflowRuns)
}, [workflowId, status, startTime, endTime]) }, [workflowId, status, startTime, endTime])
const textfieldStyle = { const textfieldStyle = {
@@ -679,8 +714,9 @@ const RuntimeDebugger = (props) => {
setWorkflow(e.target.value) setWorkflow(e.target.value)
setWorkflowId(e.target.value.id) setWorkflowId(e.target.value.id)
setSuborgWorkflowRuns(false)
submitSearch(e.target.value.id, status, startTime, endTime, rowCursor, rowsPerPage) submitSearch(e.target.value.id, status, startTime, endTime, rowCursor, rowsPerPage, false)
} }
const executeWorkflow = (execution) => { const executeWorkflow = (execution) => {
@@ -773,84 +809,17 @@ const RuntimeDebugger = (props) => {
return ( return (
<div style={{minWidth: 1150, maxWidth: 1150, margin: "auto", }}> <div style={{minWidth: 1150, maxWidth: 1150, margin: "auto", }}>
<div style={{display: "flex", }}> <div style={{display: "flex", paddingTop: 50, }}>
<div style={{display: 'flex', flexDirection: 'column'}}> <div style={{display: 'flex', flexDirection: 'column'}}>
<h1 style={{flex: 3, }}>Workflow Run Debugger {totalCount !== 0 ? ` (~${totalCount})` : ""}</h1> <div style={{display: "flex", width: "100%", }}>
<div style={{position: 'relative', right: 10, marginBottom: 10}}> <h1 style={{flex: 3, whiteSpace: "nowrap" }}>Workflow Run Debugger {totalCount !== 0 ? ` (~${totalCount})` : ""}</h1>
<TextField {selectedWorkflowExecutions.length > 0 ?
fullWidth
value={searchQuery}
style={{
backgroundColor: theme.palette.inputColor,
marginTop: 20,
marginLeft: 10,
marginRight: 12,
width: 693,
height: 55,
borderRadius: 8,
fontSize: 16,
marginBottom: 15,
}}
InputProps={{
style: {
color: "white",
fontSize: "1em",
height: 55,
width: 693,
borderRadius: 8,
},
startAdornment: (
<InputAdornment position="start">
<SearchIcon style={{ marginLeft: 5}} />
</InputAdornment>
),
endAdornment: (
<InputAdornment position="end">
{searchQuery.length > 0 && (
<ClearIcon
style={{
color: "white",
cursor: "pointer",
marginRight: 10
}}
onClick={() => setSearchQuery('')}
/>
)}
<button
type="button"
style={{
backgroundImage:
"linear-gradient(to right, rgb(248, 106, 62), rgb(243, 64, 121))",
color: "white",
border: "none",
padding: "10px 20px",
width: 100,
height: 35,
borderRadius: 17.5,
cursor: "pointer",
}}
>
Search
</button>
</InputAdornment>
),
}}
onChange={(e)=>{handleQueryChange(e)}}
color="primary"
placeholder="Filter by Workflow Name, Status, Execution Argument, Results.."
id="shuffle_search_field"
/>
</div>
</div>
{selectedWorkflowExecutions.length > 0 ?
<ButtonGroup> <ButtonGroup>
<Tooltip title="Reruns ALL selected workflows. This will make a new execution for them, and not continue the existing."> <Tooltip title="Reruns ALL selected workflows. This will make a new execution for them, and not continue the existing.">
<Button <Button
variant="outlined" variant="outlined"
color="secondary" color="secondary"
style={{maxHeight: 40, marginTop: 25, }} style={{maxHeight: 40, marginTop: 25, marginLeft: 20, }}
onClick={() => { onClick={() => {
for (var i = 0; i < selectedWorkflowExecutions.length; i++) { for (var i = 0; i < selectedWorkflowExecutions.length; i++) {
@@ -888,7 +857,7 @@ const RuntimeDebugger = (props) => {
} else { } else {
toast("Aborted "+aborted+" workflows.") toast("Aborted "+aborted+" workflows.")
// Research // Research
submitSearch(workflowId, status, startTime, endTime, rowCursor, rowsPerPage) submitSearch(workflowId, status, startTime, endTime, rowCursor, rowsPerPage, suborgWorkflowRuns)
setSelectedWorkflowExecutions([]) setSelectedWorkflowExecutions([])
} }
@@ -906,7 +875,7 @@ const RuntimeDebugger = (props) => {
<Button <Button
variant={ignoreOrg ? "contained" : "outlined"} variant={ignoreOrg ? "contained" : "outlined"}
color="secondary" color="secondary"
style={{marginLeft: 100, maxHeight: 40, marginTop: 25, }} style={{marginLeft: 20, maxHeight: 40, marginTop: 25, }}
onClick={() => { onClick={() => {
setIgnoreOrg(!ignoreOrg) setIgnoreOrg(!ignoreOrg)
}} }}
@@ -914,10 +883,98 @@ const RuntimeDebugger = (props) => {
{ignoreOrg ? "Ignoring Org" : "Ignore Org (Support Only)"} {ignoreOrg ? "Ignoring Org" : "Ignore Org (Support Only)"}
</Button> </Button>
: null} : null}
</div>
<div style={{display: "flex", justifyContent: "space-between", width: "100%"}}>
<div style={{position: 'relative', right: 10, marginBottom: 10}}>
<TextField
fullWidth
value={searchQuery}
style={{
backgroundColor: "#1a1a1a",
marginTop: 20,
marginLeft: 10,
marginRight: 12,
width: 693,
height: 51,
borderRadius: 4,
fontSize: 16,
}}
InputProps={{
style: {
backgroundColor: "#1a1a1a",
fontSize: "1em",
height: 51,
width: 693,
borderRadius: 4,
},
startAdornment: (
<InputAdornment position="start">
<SearchIcon style={{ marginLeft: 5}} />
</InputAdornment>
),
endAdornment: (
<InputAdornment position="end">
{searchQuery.length > 0 && (
<ClearIcon
style={{
color: "white",
cursor: "pointer",
marginRight: 10
}}
onClick={() => setSearchQuery('')}
/>
)}
<button
type="button"
style={{
color: "#1A1A1A",
border: "none",
padding: "10px 20px",
width: 100,
height: 35,
borderRadius: 4,
backgroundColor: "#FF8544",
cursor: "pointer",
}}
>
Search
</button>
</InputAdornment>
),
}}
onChange={(e)=>{handleQueryChange(e)}}
color="primary"
placeholder="Filter by Workflow Name, Status, Execution Argument, Results.."
id="shuffle_search_field"
/>
</div>
{userdata?.active_org?.creator_org?.length === 0 ? (
<div style={{display: "flex", margin: 'auto',marginTop: 20,justifyContent: 'center', alignItems: 'center', }}>
<Switch
checked={suborgWorkflowRuns}
onChange={() => {
setSuborgWorkflowRuns(!suborgWorkflowRuns);
//set selected workflow to all workflows when switching between suborg and all workflows
setWorkflowId("")
setWorkflow({"id": "", "name": "All Workflows"})
setStatus("")
setStartTime("")
setEndTime("")
setSearchQuery("")
submitSearch("", "", "", "", rowCursor, rowsPerPage, !suborgWorkflowRuns)}
}
color="secondary"
/>
<Typography variant="body2" style={{color: "white", }}>Show workflow runs from suborgs</Typography>
</div>
) : null}
</div>
</div>
</div> </div>
<form onSubmit={(e) => { <form onSubmit={(e) => {
submitSearch(workflowId, status, startTime, endTime, rowCursor, rowsPerPage) submitSearch(workflowId, status, startTime, endTime, rowCursor, rowsPerPage, suborgWorkflowRuns)
}} style={{display: "flex", }}> }} style={{display: "flex", justifyContent: "center", alignItems: "center", }}>
<FormControl fullWidth style={{marginTop: 5, }}> <FormControl fullWidth style={{marginTop: 5, }}>
<InputLabel id="status-label">Status</InputLabel> <InputLabel id="status-label">Status</InputLabel>
<Select <Select
@@ -951,10 +1008,13 @@ const RuntimeDebugger = (props) => {
<Autocomplete <Autocomplete
id="workflow_search" id="workflow_search"
value={workflow} value={workflow}
open={openWorkflowMenu}
onOpen={()=>{setOpenWorkflowMenu(true)}}
onClose={() => {setOpenWorkflowMenu(false)}}
classes={{ inputRoot: classes.inputRoot }} classes={{ inputRoot: classes.inputRoot }}
ListboxProps={{ ListboxProps={{
style: { style: {
backgroundColor: theme.palette.inputColor, backgroundColor: "#1a1a1a",
color: "white", color: "white",
}, },
}} }}
@@ -973,11 +1033,11 @@ const RuntimeDebugger = (props) => {
options={workflows} options={workflows}
fullWidth fullWidth
style={{ style={{
backgroundColor: theme.palette.inputColor, backgroundColor: "#1a1a1a",
height: 50, height: 50,
borderRadius: theme.palette?.borderRadius, borderRadius: 4,
marginTop: 5,
marginLeft: 5, marginLeft: 5,
marginBottom: 3,
}} }}
onChange={(event, newValue) => { onChange={(event, newValue) => {
console.log("Found value: ", newValue) console.log("Found value: ", newValue)
@@ -1018,12 +1078,13 @@ const RuntimeDebugger = (props) => {
}> }>
<MenuItem <MenuItem
style={{ style={{
backgroundColor: theme.palette.inputColor, backgroundColor: "#1a1a1a",
color: data.id === workflow.id ? "red" : "white", color: data.id === workflow.id ? "red" : "white",
}} }}
value={data} value={data}
onClick={(e) => { onClick={(e) => {
var parsedinput = { target: { value: data } } var parsedinput = { target: { value: data } }
setOpenWorkflowMenu(false)
handleWorkflowSelectionUpdate(parsedinput) handleWorkflowSelectionUpdate(parsedinput)
}} }}
> >
@@ -1036,8 +1097,8 @@ const RuntimeDebugger = (props) => {
return ( return (
<TextField <TextField
style={{ style={{
backgroundColor: theme.palette.inputColor, backgroundColor: "#1a1a1a",
borderRadius: theme.palette?.borderRadius, borderRadius: 4,
}} }}
{...params} {...params}
label="Workflow" label="Workflow"
@@ -1050,8 +1111,9 @@ const RuntimeDebugger = (props) => {
<LocalizationProvider dateAdapter={AdapterDayjs}> <LocalizationProvider dateAdapter={AdapterDayjs}>
<DateTimePicker <DateTimePicker
sx={{ sx={{
marginTop: 1,
marginLeft: 1, marginLeft: 1,
minHeight: 50,
maxHeight: 50,
minWidth: 240, minWidth: 240,
maxWidth: 240, maxWidth: 240,
}} }}
@@ -1064,10 +1126,11 @@ const RuntimeDebugger = (props) => {
/> />
<DateTimePicker <DateTimePicker
sx={{ sx={{
marginTop: 1,
marginLeft: 1, marginLeft: 1,
minWidth: 240, minWidth: 240,
maxWidth: 240, maxWidth: 240,
minHeight: 50,
maxHeight: 50,
}} }}
ampm={false} ampm={false}
label="Search until" label="Search until"
@@ -1078,14 +1141,34 @@ const RuntimeDebugger = (props) => {
/> />
</LocalizationProvider> </LocalizationProvider>
<Tooltip title="Clear all filters and search parameters">
<Button
style={{ marginLeft: 10, minHeight: 60, marginTop: 10, backgroundColor: "#1a1a1a", border: "1px solid #424242", boxShadow: 'none', borderRadius: 4, width: 81, height: 51, marginRight: 15 }}
variant="contained"
color="primary"
onClick={() => {
setWorkflowId("")
setWorkflow({"id": "", "name": "All Workflows"})
setStatus("")
setStartTime("")
setEndTime("")
setSearchQuery("")
setSuborgWorkflowRuns(false)
submitSearch("", "", "", "", rowCursor, rowsPerPage, false)
}}
>
<FilterAltOffIcon />
</Button>
</Tooltip>
<Button <Button
variant="outlined" variant="outlined"
color="primary" color="primary"
onClick={() => { onClick={() => {
submitSearch(workflowId, status, startTime, endTime, rowCursor, rowsPerPage) submitSearch(workflowId, status, startTime, endTime, rowCursor, rowsPerPage, suborgWorkflowRuns)
}} }}
disabled={searchLoading} disabled={searchLoading}
style={{height: 50, minWidth: 100, marginTop: 15, marginLeft: 10, }} style={{height: 50, minWidth: 100, marginTop: 15, }}
> >
{searchLoading ? <CircularProgress size={30} /> : "Search"} {searchLoading ? <CircularProgress size={30} /> : "Search"}
</Button> </Button>
@@ -1100,7 +1183,7 @@ const RuntimeDebugger = (props) => {
disableSelectionOnClick disableSelectionOnClick
onPageSizeChange={(newPageSize) => { onPageSizeChange={(newPageSize) => {
setRowsPerPage(newPageSize) setRowsPerPage(newPageSize)
submitSearch(workflowId, status, startTime, endTime, rowCursor, newPageSize) submitSearch(workflowId, status, startTime, endTime, rowCursor, newPageSize, suborgWorkflowRuns)
}} }}
// event for when clicking next page // event for when clicking next page
// Hide page changer // Hide page changer
+1 -1
View File
@@ -58,7 +58,7 @@ const data = [
"curve-style": "unbundled-bezier", "curve-style": "unbundled-bezier",
label: "data(label)", label: "data(label)",
"text-margin-y": "-15px", "text-margin-y": "-15px",
width: "3px", width: "2px",
color: "white", color: "white",
"line-fill": "linear-gradient", "line-fill": "linear-gradient",
"line-gradient-stop-positions": ["0.0", "100"], "line-gradient-stop-positions": ["0.0", "100"],
+10 -1
View File
@@ -12,6 +12,14 @@ const Admin2 = (props) => {
const [orgRequest, setOrgRequest] = React.useState(true); const [orgRequest, setOrgRequest] = React.useState(true);
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
if (document !== undefined) {
if (selectedOrganization?.name !== undefined) {
document.title = selectedOrganization?.name + " - Admin - Shuffle"
} else {
document.title = "Admin - Shuffle"
}
}
const handleGetOrg = (orgId) => { const handleGetOrg = (orgId) => {
fetch(`${globalUrl}/api/v1/orgs/${orgId}`, { fetch(`${globalUrl}/api/v1/orgs/${orgId}`, {
@@ -103,7 +111,8 @@ const Admin2 = (props) => {
setSelectedStatus(leads); setSelectedStatus(leads);
} }
setSelectedOrganization(responseJson);
setSelectedOrganization(responseJson)
var lists = { var lists = {
active: { active: {
triggers: [], triggers: [],
File diff suppressed because it is too large Load Diff
+58 -19
View File
@@ -276,7 +276,14 @@ export const appCategories = [
"color": "#FFC107", "color": "#FFC107",
"icon": "network", "icon": "network",
"action_labels": ["Get Rules", "Allow IP", "Block IP",], "action_labels": ["Get Rules", "Allow IP", "Block IP",],
}, { },
{
"name": "AI",
"color": "#FFC107",
"icon": "AI",
"action_labels": ["Answer Question", "Run Action"],
},
{
"name": "Other", "name": "Other",
"color": "#FFC107", "color": "#FFC107",
"icon": "other", "icon": "other",
@@ -418,6 +425,7 @@ const AppCreator = (defaultprops) => {
const [appBuilding, setAppBuilding] = useState(false); const [appBuilding, setAppBuilding] = useState(false);
const [fileDownloadEnabled, setFileDownloadEnabled] = useState(false); const [fileDownloadEnabled, setFileDownloadEnabled] = useState(false);
const [actionAmount, setActionAmount] = useState(increaseAmount); const [actionAmount, setActionAmount] = useState(increaseAmount);
const [newAppGroup, setNewAppGroup] = useState("")
const [oauth2Scopes, setOauth2Scopes] = useState([]); const [oauth2Scopes, setOauth2Scopes] = useState([]);
const [oauth2Type, setOauth2Type] = useState("delegated"); const [oauth2Type, setOauth2Type] = useState("delegated");
@@ -4622,23 +4630,25 @@ const AppCreator = (defaultprops) => {
}} }}
/> />
*/} */}
<h4>Choose a Category</h4> <div style={{display: "flex", }}>
<Select <div style={{flex: 2, }}>
fullWidth <h4>Choose a Category</h4>
SelectDisplayProps={{ <Select
style: { fullWidth
marginLeft: 10, SelectDisplayProps={{
}, style: {
}} marginLeft: 10,
onChange={(e) => { },
setNewWorkflowCategories([e.target.value]); }}
setUpdate("added " + e.target.value); onChange={(e) => {
}} setNewWorkflowCategories([e.target.value]);
value={newWorkflowCategories.length === 0 ? "Select a category" : newWorkflowCategories[0]} setUpdate("added " + e.target.value);
}}
value={newWorkflowCategories.length === 0 ? "Select a category" : newWorkflowCategories[0]}
style={{ backgroundColor: inputColor, color: "white", height: "50px" }} style={{ backgroundColor: inputColor, color: "white", height: "50px" }}
> >
{categories.map((data, index) => { {categories.map((data, index) => {
if (data === undefined || data === null || data === "" || data === undefined || data === null || data === "") { if (data === undefined || data === null || data === "" || data === undefined || data === null || data === "") {
return null return null
} }
@@ -4651,9 +4661,38 @@ const AppCreator = (defaultprops) => {
> >
{data.name} {data.name}
</MenuItem> </MenuItem>
) )
})} })}
</Select> </Select>
</div>
{/*
<div style={{flex: 1, marginLeft: 25, }}>
<Tooltip title="Helps when searching for apps. E.g. Google or Microsoft are their own groups." placement="top">
<h4 style={{marginBottom: 0, }}>Group</h4>
</Tooltip>
<TextField
style={{
}}
fullWidth
placeholder="Group"
type="text"
id="standard-required"
margin="normal"
variant="outlined"
defaultValue={newAppGroup}
onChange={(e) => {
setNewAppGroup(e.target.value)
setUpdate("group added "+e.target.value)
}}
InputProps={{
style: {
color: "white",
},
}}
/>
</div>
*/}
</div>
<h4>Tags</h4> <h4>Tags</h4>
<MuiChipsInput <MuiChipsInput
style={{ marginTop: 10 }} style={{ marginTop: 10 }}
+2 -2
View File
@@ -136,7 +136,7 @@ export const GetIconInfo = (action) => {
{ key: "cache_add", values: ["set_cache"] }, { key: "cache_add", values: ["set_cache"] },
{ key: "cache_get", values: ["get_cache"] }, { key: "cache_get", values: ["get_cache"] },
{ key: "filter", values: ["filter"] }, { key: "filter", values: ["filter"] },
{ key: "merge", values: ["join", "merge", "route", "router"] }, { key: "merge", values: ["join", "merge", "route", "router", "routing"] },
{ {
key: "search", key: "search",
values: ["search", "find", "locate", "index", "analyze", "anal", "match", "check cache", "check", "verify", "validate"], values: ["search", "find", "locate", "index", "analyze", "anal", "match", "check cache", "check", "verify", "validate"],
@@ -200,7 +200,7 @@ export const GetIconInfo = (action) => {
}, },
{ {
key: "compare", key: "compare",
values: ["compare", "convert", "to", "filter", "translate", "parse"], values: ["compare", "convert", "to", "filter", "translate", "parse", "generate", ],
}, },
{ key: "close", values: ["close", "stop", "cancel", "block"] }, { key: "close", values: ["close", "stop", "cancel", "block"] },
{ key: "communication", values: ["communication", "comms", "email", "mail",] }, { key: "communication", values: ["communication", "comms", "email", "mail",] },