cloud frontend sync

This commit is contained in:
yashsinghcodes
2025-12-19 18:05:04 +05:30
parent 1fd102a754
commit 4cbacf20ef
41 changed files with 2189 additions and 668 deletions
+468 -179
View File
@@ -24,6 +24,9 @@ import {
TextField,
Popover,
Divider,
AvatarGroup,
Avatar,
} from '@mui/material'
import {
@@ -40,6 +43,7 @@ import {
Refresh as RefreshIcon,
Add as AddIcon,
Warning as WarningIcon,
Pause as PauseIcon,
} from '@mui/icons-material'
import {
@@ -68,6 +72,27 @@ const AgentUI = (props) => {
const [appPickerAnchor, setAppPickerAnchor] = React.useState(null)
const [chosenApps, setChosenApps] = useState([])
const activateApp = (appId) => {
if (appId === undefined || appId === null || appId === "") {
return
}
const url = `${globalUrl}/api/v1/apps/${appId}/activate`
fetch(url, {
method: "GET",
credentials: "include",
})
.then((response) => {
return response.json()
})
.then((responseJson) => {
if (responseJson.success !== false) {
getApps()
} else {
toast.warn("Failed to activate app: " + responseJson.reason)
}
})
}
useEffect(() => {
if (newSelectedApp.objectID === undefined || newSelectedApp.objectID === null || newSelectedApp.objectID === "") {
@@ -83,9 +108,76 @@ const AgentUI = (props) => {
id: newSelectedApp.objectID,
image: newSelectedApp.image_url,
}]))
var found = false
if (apps?.length > 0) {
for (var key in apps) {
const app = apps[key]
if (app?.id !== newSelectedApp?.objectID) {
continue
}
found = true
break
}
}
if (!found) {
activateApp(newSelectedApp.objectID)
}
}
}, [newSelectedApp])
useEffect(() => {
if (chosenApps?.length > 0) {
return
}
if (apps?.length === 0) {
console.log("No apps loaded yet")
return
}
if (data === undefined || data === null) {
return
}
if (data?.allowed_actions === undefined || data?.allowed_actions === null) {
return
}
console.log("HERE!")
console.log("Data, apps: ", data, apps)
var newChosenApps = []
for (var key in data?.allowed_actions) {
const allowedAction = data?.allowed_actions[key]
if (!allowedAction?.startsWith("app:")) {
newChosenApps.push({
"name": allowedAction,
"large_image": "",
})
continue
}
const appId = allowedAction.split(":")[1]
for (var appKey in apps) {
const app = apps[appKey]
if (app.id !== appId) {
continue
}
newChosenApps.push(app)
break
}
}
if (newChosenApps.length > 0) {
setChosenApps(newChosenApps)
}
}, [data])
const {themeMode} = useContext(Context)
const theme = getTheme(themeMode)
const navigate = useNavigate();
@@ -95,12 +187,13 @@ const AgentUI = (props) => {
}
const agentWrapperStyle = {
width: 1000,
height: 1000,
width: "100%",
maxHeight: "100vh",
margin: "auto",
paddingTop: 100,
paddingBottom: 1000,
backgroundColor: theme.palette.backgroundColor,
paddingBottom: showAgentStarter ? 0 : 1500,
}
if (data.input === undefined || data.input === null) {
@@ -412,9 +505,7 @@ const AgentUI = (props) => {
} else {
for (var key in questionAnswers) {
const answer = questionAnswers[key]
if (isContinuation === true) {
newArgument["question_"+(answer.index)] = answer.value
}
newArgument["question_"+(answer.index)] = answer.value
}
}
@@ -678,8 +769,8 @@ const AgentUI = (props) => {
</Tooltip>
const rerunButton =
<Tooltip title="Rerun FROM this decision. This can be used if an agent decision action somehow stopped and didn't get a result. Clears out all decisions AFTER this one." placement="right">
const rerunButton = item?.type === "processing" ? null :
<Tooltip title={item?.type === "decision" ? "Rerun FROM this decision. This can be used if an agent decision action somehow stopped and didn't get a result. Clears out all decisions AFTER this one." : ""} placement="right">
<span>
<IconButton
disabled={item.type !== "decision" || disableButtons}
@@ -698,6 +789,11 @@ const AgentUI = (props) => {
</span>
</Tooltip>
var itemLabel = item?.label?.replaceAll("_", " ") || ""
if (item?.details?.reason !== undefined && item?.details?.reason !== null && item?.details?.reason !== "") {
itemLabel = item?.details?.reason
}
return (
<div
style={{
@@ -778,10 +874,22 @@ const AgentUI = (props) => {
paddingTop: defaultTopPadding,
paddingBottom: defaultTopPadding,
}}>
{item.label}
{itemLabel}
</div>
<Tooltip title={`Time taken: ${currentDuration} seconds. Started: ${new Date(itemStartTime * 1000).toLocaleString()}\nFinished: ${new Date(itemEndTime * 1000).toLocaleString()}`} placement="right">
<Tooltip title={`Time taken: ${currentDuration} seconds. Started: ${new Date(itemStartTime * 1000).toLocaleString()}\nFinished: ${new Date(itemEndTime * 1000).toLocaleString()}`} placement="right"
PopperProps={{
modifiers: [
{
name: 'offset',
options: {
offset: [0, 130], // [skid, distance]
},
},
],
}}
style={theme.palette.tooltip}
>
<div style={{
minWidth: maxTimelineWidth,
maxWidth: maxTimelineWidth,
@@ -874,7 +982,7 @@ const AgentUI = (props) => {
item?.type === "decision" ?
<div style={{display: "flex", }}>
{rerunButton}
<Tooltip title="Explore/debug execution" placement="left">
<Tooltip title="Explore/debug app run" placement="left">
<span>
<IconButton
disabled={item?.details?.run_details?.debug_url === undefined || item?.details?.run_details?.debug_url === null || item?.details?.run_details?.debug_url === ""}
@@ -895,21 +1003,24 @@ const AgentUI = (props) => {
:
rerunButton
}
<Tooltip title="Explore results" placement="right">
<span>
<IconButton
disabled={item.details === undefined || item.details === null || item.details === ""}
style={{marginLeft: 10, }}
>
{open ?
<ExpandLessIcon />
:
<ExpandMoreIcon />
}
</IconButton>
</span>
</Tooltip>
{item?.type === "processing" ? null :
<Tooltip title="Explore results" placement="right">
<span>
<IconButton
disabled={item.details === undefined || item.details === null || item.details === ""}
style={{marginLeft: 10, }}
>
{open ?
<ExpandLessIcon />
:
<ExpandMoreIcon />
}
</IconButton>
</span>
</Tooltip>
}
</div>
</div>
@@ -976,13 +1087,17 @@ const AgentUI = (props) => {
<Button
variant="contained"
style={{marginTop: 16, }}
disabled={questionSubmitDisabled}
disabled={questionSubmitDisabled || agentRequestLoading}
onClick={() => {
submitQuestions(item?.details?.run_details?.id, questionAnswers)
}}
>
Submit
{agentRequestLoading ?
<CircularProgress size={20} style={{marginRight: 10, }} />
:
"Submit"
}
</Button>
</div>
: null}
@@ -1078,6 +1193,7 @@ const AgentUI = (props) => {
}
var finishDecisionId = ""
var finishAnswer = ""
var sortedTimelineItems = []
for (var key in agent_data?.decisions) {
const item = agent_data.decisions[key]
@@ -1105,17 +1221,71 @@ const AgentUI = (props) => {
if (item?.details?.action === "finish" || item.action === "finish" || item.category == "finish" || item?.details?.action == "finalise") {
finishDecisionId = item?.run_details?.id
}
finishAnswer = item?.reason || ""
if (item?.fields !== undefined && item?.fields !== null) {
// Just choosing the first one
for (var fieldKey in item?.fields) {
const field = item?.fields[fieldKey]
finishAnswer = field?.value || finishAnswer
break
}
}
}
}
timelineItems.sort((a, b) => {
if (a.start_time < b.start_time) {
return 1;
// Fill blank space between items in timeline by adding "empty" decisions:
// 1. Sort
// 2. Fill blank space
// 3. Sort again
timelineItems.sort((a, b) => a.start_time - b.start_time)
for (var i = 0; i < timelineItems.length - 1; i++) {
if (timelineItems?.length <= 2) {
break
}
return 0;
})
if (i === timelineItems.length - 1) {
break
}
const currentItem = timelineItems[i]
if (currentItem?.type === "processing") {
continue
}
const nextItem = timelineItems[i + 1]
if (nextItem?.type === "processing") {
continue
}
if (nextItem.start_time - currentItem.end_time > 1) {
timelineItems?.push({
label: "",
type: "processing",
category: "processing",
status: "FINISHED",
start_time: currentItem.end_time,
end_time: nextItem.start_time,
})
}
}
timelineItems.sort((a, b) => a.start_time - b.start_time)
const handleKeyDownCont = (e) => {
if (finishDecisionId === "") {
return
}
const isCmdEnter = e.metaKey && e.key === "Enter"; // macOS
const isCtrlEnter = e.ctrlKey && e.key === "Enter"; // Windows/Linux
if (isCmdEnter || isCtrlEnter) {
e.preventDefault()
submitQuestions(finishDecisionId, {
"continue": continuationText,
}, true)
}
}
return (
<div style={{marginTop: 20, }}>
@@ -1151,6 +1321,7 @@ const AgentUI = (props) => {
<Box
component="form"
style={{width: "100%", textAlign: "center",}}
onKeyDown={handleKeyDownCont}
onSubmit={(e) => {
e.preventDefault();
@@ -1161,15 +1332,35 @@ const AgentUI = (props) => {
}, true)
}}
>
<div style={{display: "flex", maxWidth: 550, minWidth: 550, margin: "auto", marginTop: 50, }}>
{finishAnswer !== "" ?
<div style={{marginTop: 50, textAlign: "left", }}>
<Markdown
components={markdownComponents}
id="markdown_wrapper"
className={"style.reactMarkdown"}
escapeHtml={false}
skipHtml={false}
remarkPlugins={[remarkGfm]}
style={{
maxWidth: "100%",
minWidth: "100%",
}}
>
{finishAnswer}
</Markdown>
</div>
: null}
<div style={{display: "flex", maxWidth: 600, minWidth: 600, margin: "auto", marginTop: 50, }}>
<div>
<TextField
label="Add more details to the current task"
label="Add more details to continue the current task"
variant="outlined"
disabled={agentRequestLoading}
style={{width: 400, margin: "auto", }}
style={{width: 450, margin: "auto", }}
multiline
minRows={1}
minRows={2}
value={continuationText}
onChange={(e) => {
console.log("Value: ", e.target.value)
//setActionInput(e.target.value)
@@ -1195,7 +1386,7 @@ const AgentUI = (props) => {
}}
/>
<Typography variant="body2" color="textSecondary" style={{marginTop: 10, }}>
Any failed tasks will be set to ignored (TBD). <a href="/docs/AI#agent-continuations" target="_blank" rel="noreferrer" style={{color: theme.palette.main, textDecoration: "none", }}>Learn more</a>
Any failed tasks will be set to ignored. <a href="/docs/AI#agent-continuations" target="_blank" rel="noreferrer" style={{color: theme.palette.main, textDecoration: "none", }}>Learn more</a>
</Typography>
</div>
<Typography color="textSecondary" variant="body1" style={{marginTop: 25, marginLeft: 20, }}>
@@ -1242,7 +1433,7 @@ const AgentUI = (props) => {
// 1. Run the execution. Can this be a single-action run?
// 2. Get the execution ID and node ID from the response.
const uuid = uuidv4()
var parsedAction = "list_tickets,API" // Default action for now
var parsedAction = "API" // Default action for now
if (chosenApps.length > 0) {
parsedAction = ""
for (var appKey in chosenApps) {
@@ -1319,155 +1510,253 @@ const AgentUI = (props) => {
cursor: "pointer",
}
const typeButtonGroup =
<ButtonGroup style={{}}>
<Button
variant={showAgentStarter ? "contained" : "outlined"}
color="secondary"
onClick={() => {
if (actionInput === "" || actionInput === undefined || actionInput === null) {
setActionInput(data?.original_input || "")
}
//setChosenApps(chosenApps.concat([{
setTimeout(() => {
setShowAgentStarter(true)
}, 200)
}}
>
Restart
</Button>
<Button
variant={buttonState === "default" ? "contained" : "outlined"}
color="secondary"
disabled
onClick={() => {
setButtonState("default");
setShowAgentStarter(false)
}}
>
Default
</Button>
<Button
variant={buttonState === "timeline" ? "contained" : "outlined"}
color="secondary"
onClick={() => {
setButtonState("timeline");
setShowAgentStarter(false)
}}
>
Timeline
</Button>
</ButtonGroup>
return (
<div style={agentWrapperStyle}>
<TextField
id="copy_element_shuffle"
style={{ display: "none" }}
/>
{showAgentStarter ?
<Box
component="form"
style={{textAlign: "center", }}
onKeyDown={handleKeyDownRoot}
onSubmit={(e) => {
e.preventDefault();
submitInput(actionInput);
}}
>
<img src="/images/logos/agent.svg" style={{
width: 200,
height: 200,
borderRadius: theme.palette.borderRadius,
}} />
<div />
<Typography variant="h5" style={{marginTop: 30, }}>
Shuffle AI Agents
</Typography>
<div>
<div style={{height: showAgentStarter ? "25vh" : "5vh", }}>
</div>
<div style={agentWrapperStyle}>
<div style={{
minWidth: 1000, maxWidth: 1000, margin: "auto",
}}>
<TextField
label="What do you want to do?"
variant="outlined"
disabled={agentRequestLoading}
style={{width: 450, marginRight: 20, marginTop: 30, }}
multiline
minRows={1}
defaultValue={actionInput || ""}
onChange={(e) => {
setActionInput(e.target.value)
}}
InputProps={{
endAdornment: (
agentRequestLoading ?
<CircularProgress size={24} style={{marginRight: 10, }} />
:
<Tooltip title="This is the input for the AI Agent. It can be any valid JSON.">
<IconButton type="submit">
<SendIcon
color="primary"
/>
</IconButton>
</Tooltip>
),
}}
id="copy_element_shuffle"
style={{ display: "none" }}
/>
<div style={{display: "flex", margin: "auto", paddingTop: 10, minWidth: 300, maxWidth: 300, justifyContent: "center", overflowWrap: "wrap", }}>
<div>
<Chip
id="add_app_chip"
icon={<AddIcon />} label="Select Apps"
style={chipStyle}
onClick={() => {
setAppPickerAnchor(document.getElementById("add_app_chip"))
{showAgentStarter ?
<Box
component="form"
style={{textAlign: "center", }}
onKeyDown={handleKeyDownRoot}
onSubmit={(e) => {
e.preventDefault();
submitInput(actionInput);
}}
>
{/*
<img src="/images/logos/agent.svg" style={{
width: 200,
height: 200,
borderRadius: theme.palette.borderRadius,
}} />
*/}
<div />
<Typography variant="h2" style={{marginTop: 30, }}>
What do you want to do?
</Typography>
<TextField
label="Get my emails for today and summarise them"
variant="outlined"
disabled={agentRequestLoading}
style={{width: 500, marginTop: 30, }}
multiline
minRows={1}
defaultValue={actionInput || ""}
onChange={(e) => {
setActionInput(e.target.value)
}}
InputProps={{
endAdornment: (
agentRequestLoading ?
<CircularProgress size={24} style={{marginRight: 10, }} />
:
<Tooltip title="This is the input for the AI Agent. It can be any valid JSON.">
<IconButton type="submit">
<SendIcon
color="primary"
/>
</IconButton>
</Tooltip>
),
}}
/>
<Popover
open={appPickerAnchor !== null}
anchorEl={appPickerAnchor}
onClose={() => {
setAppPickerAnchor(null)
}}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'left',
}}
>
<AppSearch
userdata={userdata}
defaultSearch={""}
newSelectedApp={newSelectedApp}
setNewSelectedApp={setNewSelectedApp}
inputHeight={200}
<div style={{display: "flex", margin: "auto", paddingTop: 30, minWidth: 300, maxWidth: 300, justifyContent: "center", overflowWrap: "wrap", }}>
<div>
<Chip
id="add_app_chip"
icon={<AddIcon />} label="Select Apps / MCPs"
style={chipStyle}
onClick={() => {
setAppPickerAnchor(document.getElementById("add_app_chip"))
}}
/>
</Popover>
<Popover
open={appPickerAnchor !== null}
anchorEl={appPickerAnchor}
onClose={() => {
setAppPickerAnchor(null)
}}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'left',
}}
>
<AppSearch
userdata={userdata}
defaultSearch={""}
newSelectedApp={newSelectedApp}
setNewSelectedApp={setNewSelectedApp}
inputHeight={200}
/>
</Popover>
</div>
{chosenApps.map((app, index) => {
const chosenName = (app?.name?.charAt(0).toUpperCase() + app?.name?.slice(1))?.replaceAll("_", " ").replaceAll("-", " ")
const chosenImagePath = app?.image
const chosenImage = <img src={chosenImagePath} style={{width: 24, height: 24, borderRadius: 20, marginRight: 1, }} />
return (
<Chip icon={chosenImage} label={chosenName} variant="outlined"
style={chipStyle}
onDelete={() => {
const newChosenApps = chosenApps.filter((a, i) => i !== index)
setChosenApps(newChosenApps)
}}
/>
)
})}
</div>
<div style={{marginTop: 20, }}>
{execution === undefined || execution === null || execution?.execution_id === undefined || execution?.execution_id === null ? null : typeButtonGroup}
</div>
{/*
<div style={{
height: 300,
width: "100%",
background: showAgentStarter ? `linear-gradient(to bottom, ${theme.palette.backgroundColor}, #C9591D)` : "inherit",
backgroundSize: "100% 100%",
backgroundRepeat: "no-repeat",
backgroundPosition: "0px 100px",
}} />
*/}
</Box>
:
<div>
<div style={{display: "flex", }}>
<div style={{flex: 2, }}>
{typeButtonGroup}
<ButtonGroup style={{marginLeft: 25, }}>
<Tooltip title="Reload the agent data" placement="top">
<span>
<Button
disabled={execution === null || Object.keys(execution).length === 0}
style={{}}
variant={"outlined"}
color="secondary"
onClick={() => {
GetExecution(execution?.execution_id, agentActionResult?.action?.id, execution?.authorization)
}}
>
<RefreshIcon />
</Button>
</span>
</Tooltip>
{execution?.status === "EXECUTING" ?
<Tooltip title="Stop the agent" placement="top">
<span>
<Button
disabled={execution === null || true}
style={{}}
variant={"outlined"}
color="secondary"
onClick={() => {
toast.warn("Not implemented. Should run abort.")
}}
>
<PauseIcon />
</Button>
</span>
</Tooltip>
: null}
</ButtonGroup>
</div>
<div style={{flex: 1, }}>
<AvatarGroup>
{chosenApps?.map((app, index) => {
return(
<Tooltip title={`Constraint: ${app?.name}`} key={index} placement="top">
<Avatar
key={index}
alt={app?.name}
src={app?.large_image}
onClick={() => {
window.open(`/apps/${app.id}`, '_blank', 'noopener,noreferrer');
}}
style={{
cursor: "pointer",
width: 30,
height: 30,
}}
/>
</Tooltip>
)
})}
</AvatarGroup>
</div>
</div>
{buttonState === "timeline" ?
<TimelineRender agent_data={data} />
:
null
}
</div>
{chosenApps.map((app, index) => {
const chosenName = (app?.name?.charAt(0).toUpperCase() + app?.name?.slice(1))?.replaceAll("_", " ").replaceAll("-", " ")
const chosenImagePath = app?.image
const chosenImage = <img src={chosenImagePath} style={{width: 24, height: 24, borderRadius: 20, marginRight: 1, }} />
return (
<Chip icon={chosenImage} label={chosenName} variant="outlined"
style={chipStyle}
onDelete={() => {
const newChosenApps = chosenApps.filter((a, i) => i !== index)
setChosenApps(newChosenApps)
}}
/>
)
})}
</div>
</Box>
:
<div>
<ButtonGroup style={{marginTop: 50, }}>
<Button
variant={buttonState === "default" ? "contained" : "outlined"}
color="secondary"
onClick={() => {
setButtonState("default");
}}
>
Default
</Button>
<Button
variant={buttonState === "timeline" ? "contained" : "outlined"}
color="secondary"
onClick={() => {
setButtonState("timeline");
}}
>
Timeline
</Button>
</ButtonGroup>
<Tooltip title="Reload the agent data" placement="top">
<span>
<Button
disabled={execution === null || Object.keys(execution).length === 0}
style={{marginLeft: 25, }}
variant={"outlined"}
color="secondary"
onClick={() => {
GetExecution(execution.execution_id, agentActionResult.action.id, execution.authorization)
}}
>
<RefreshIcon />
</Button>
</span>
</Tooltip>
{buttonState === "timeline" ?
<TimelineRender agent_data={data} />
:
null
}
</div>
}
</div>
</div>
)
}
+94 -49
View File
@@ -463,9 +463,9 @@ const AngularWorkflow = (defaultprops) => {
var to_be_copied = "";
const [firstrequest, setFirstrequest] = React.useState(true);
const cystyle = useMemo(() => defaultCytoscapeStyle(theme), [themeMode]);
// const cystyle = useMemo(() => defaultCytoscapeStyle, [themeMode]);
const [apps, setApps] = React.useState([]);
const cystyle = useMemo(() => defaultCytoscapeStyle(theme, apps), [themeMode, apps]);
const [cy, setCy] = React.useState();
const useStyles = makeStyles({
@@ -593,7 +593,6 @@ const AngularWorkflow = (defaultprops) => {
const [showDropdown, setShowDropdown] = React.useState(false);
const [triggerActionList, setTriggerActionList] = React.useState([]);
const [apps, setApps] = React.useState([]);
const [filteredApps, setFilteredApps] = React.useState([]);
const [prioritizedApps, setPrioritizedApps] = React.useState([])
@@ -2560,7 +2559,7 @@ const AngularWorkflow = (defaultprops) => {
// Center and zoom to the node with smooth animation if requested
if (config.center) {
cy.animate({
cy.stop().animate({
center: {
eles: cyNode
},
@@ -3324,7 +3323,7 @@ const AngularWorkflow = (defaultprops) => {
}
const animationDuration = 150
foundnode.animate(
foundnode.stop().animate(
{
style: parsedStyle,
},
@@ -3445,6 +3444,18 @@ const AngularWorkflow = (defaultprops) => {
return
}
for (var i = 0; i < workflowExecutions.length; i++) {
const exec = workflowExecutions[i]
if (exec.execution_id === curAction.source_execution) {
if (exec?.execution_argument === undefined || exec?.execution_argument === null || exec?.execution_argument.length === 0) {
break
}
setExecutionText(exec.execution_argument)
break
}
}
setExecutionRunning(true)
setExecutionRequestStarted(true)
var headers = {
@@ -3830,7 +3841,7 @@ const AngularWorkflow = (defaultprops) => {
} else {
console.log("Closing auth modal? FAIL")
toast("Failed to find new authentication. See details in Oauth2 popup window where auth was attempted.");
toast.error("Failed to find new authentication. See details in Oauth2 popup window where auth was attempted.");
shouldClose = false
}
} else {
@@ -4184,7 +4195,7 @@ const AngularWorkflow = (defaultprops) => {
}
const animationDuration = 150
node.animate(
node.stop().animate(
{
style: parsedStyle,
},
@@ -4201,7 +4212,7 @@ const AngularWorkflow = (defaultprops) => {
"border-color": "#81c784",
}
node.animate(
node.stop().animate(
{
style: parsedStyle,
},
@@ -4250,7 +4261,7 @@ const AngularWorkflow = (defaultprops) => {
}
const animationDuration = 150
node.animate(
node.stop().animate(
{
style: parsedStyle,
},
@@ -4267,7 +4278,7 @@ const AngularWorkflow = (defaultprops) => {
"border-color": "#81c784",
}
node.animate(
node.stop().animate(
{
style: parsedStyle,
},
@@ -4298,7 +4309,7 @@ const AngularWorkflow = (defaultprops) => {
}
const animationDuration = 150
node.animate(
node.stop().animate(
{
style: parsedStyle,
},
@@ -4314,7 +4325,7 @@ const AngularWorkflow = (defaultprops) => {
"line-gradient-stop-colors": ["grey", "grey"],
}
node.animate(
node.stop().animate(
{
style: parsedStyle,
},
@@ -4346,7 +4357,7 @@ const AngularWorkflow = (defaultprops) => {
}
const animationDuration = 150
node.animate(
node.stop().animate(
{
style: parsedStyle,
},
@@ -4363,7 +4374,7 @@ const AngularWorkflow = (defaultprops) => {
"border-color": color,
}
node.animate(
node.stop().animate(
{
style: parsedStyle,
},
@@ -4394,7 +4405,7 @@ const AngularWorkflow = (defaultprops) => {
}
const animationDuration = 150
node.animate(
node.stop().animate(
{
style: parsedStyle,
},
@@ -4450,7 +4461,7 @@ const AngularWorkflow = (defaultprops) => {
}
const animationDuration = 150
node.animate(
node.stop().animate(
{
style: parsedStyle,
},
@@ -4484,7 +4495,7 @@ const AngularWorkflow = (defaultprops) => {
}
const animationDuration = 150
node.animate(
node.stop().animate(
{
style: parsedStyle,
},
@@ -8627,7 +8638,7 @@ const AngularWorkflow = (defaultprops) => {
};
}
event.target.animate(
event.target.stop().animate(
{
style: parsedStyle,
},
@@ -9423,7 +9434,7 @@ const AngularWorkflow = (defaultprops) => {
}
if (event.target !== undefined && event.target !== null) {
event.target.animate(
event.target.stop().animate(
{
style: parsedStyle,
},
@@ -9741,7 +9752,7 @@ const AngularWorkflow = (defaultprops) => {
}
const animationDuration = 150
foundnode.animate(
foundnode.stop().animate(
{
style: parsedStyle,
},
@@ -9910,7 +9921,7 @@ const AngularWorkflow = (defaultprops) => {
}
const animationDuration = 150
foundnode.animate(
foundnode.stop().animate(
{
style: parsedStyle,
},
@@ -12487,7 +12498,7 @@ const AngularWorkflow = (defaultprops) => {
//}
safeRefine(event.currentTarget.value)
}}
limit={5}
limit={15}
/>
{/*isSearchStalled ? 'My search is stalled' : ''*/}
</form>
@@ -14360,8 +14371,7 @@ const AngularWorkflow = (defaultprops) => {
padding: 30,
pointerEvents: "auto",
color: theme.palette.text.primary,
minWidth: isMobile ? "90%" : 650,
border: theme.palette.defaultBorder,
minWidth: isMobile ? "90%" : 750,
borderRadius: theme.palette.borderRadius,
backgroundColor: "black",
@@ -14474,18 +14484,24 @@ const AngularWorkflow = (defaultprops) => {
<Button
variant="outlined"
color="primary"
style={{position: "sticky", top: 35, maxHeight: 40, maxWidth: 150, marginRight: 5, }}
style={{position: "sticky", top: 50, maxHeight: 40, maxWidth: 150, marginRight: 5, }}
onClick={() => {
setExecutionText(data)
executeWorkflow(data, workflow.start, lastSaved);
executeWorkflow(data, workflow.start, lastSaved)
setExecutionArgumentModalOpen(false)
}}
>Select</Button>
<ReactJson
src={validate.result}
theme={theme.palette.jsonTheme}
style={theme.palette.reactJsonStyle}
theme={"summerfruit"}
style={{
padding: 5,
width: "98%",
borderRadius: 5,
border: "1px solid rgba(255,255,255,0.7)",
overflowX: "auto",
}}
shouldCollapse={(jsonField) => {
return collapseField(jsonField)
}}
@@ -14499,10 +14515,11 @@ const AngularWorkflow = (defaultprops) => {
}
return (
<Paper style={{ padding: 10, marginTop: 10, backgroundColor: theme.palette.platformColor, maxHeight: 100, overflow: "auto", cursor: "pointer", position: "relative", }}
<Paper
style={{ padding: 10, marginTop: 10, backgroundColor: theme.palette.platformColor, maxHeight: 150, overflow: "auto", position: "relative", border: `2px solid rgba(255,255,255,0.3)`, }}
onClick={() => {
setExecutionText(data)
executeWorkflow(data, workflow.start, lastSaved);
//executeWorkflow(data, workflow.start, lastSaved);
setExecutionArgumentModalOpen(false)
}}
@@ -14759,6 +14776,7 @@ const AngularWorkflow = (defaultprops) => {
color: theme.palette.textColor,
backgroundColor: 'inherit',
zIndex: 10000,
fontSize: 12,
}}
>
<b>PS: Conditions can't be used for loops [ .# ]. Use the filters list action.{" "}</b>
@@ -15110,7 +15128,9 @@ const AngularWorkflow = (defaultprops) => {
key={condition.condition.id}
square
style={paperVariableStyle}
onClick={() => { }}
onClick={() => {
setLastSaved(false)
}}
>
<div
style={{
@@ -15339,6 +15359,7 @@ const AngularWorkflow = (defaultprops) => {
return
}
setLastSaved(false)
setSourceValue({
name: "source",
value: "",
@@ -15384,17 +15405,15 @@ const AngularWorkflow = (defaultprops) => {
// Change Direction of the branch target/source
const foundBranch = cy.getElementById(selectedEdge.id)
if (foundBranch !== undefined && foundBranch !== null) {
console.log("BRANCH: ", foundBranch)
const source = foundBranch.data("source")
const target = foundBranch.data("target")
var branchdata = JSON.parse(JSON.stringify(foundBranch.data()))
console.log("BEFORE: ", branchdata)
console.log("Start node", workflow.start)
const startNode = workflow.start
if (source === startNode) {
toast("Can't point to Start Node")
} else {
setLastSaved(false)
const newid = uuidv4()
branchdata.source = target
branchdata.target = source
@@ -19298,7 +19317,7 @@ const AngularWorkflow = (defaultprops) => {
}, 2000);
}
toast.info("Successfully changed active organisation - refreshing!");
toast.success("Successfully changed active organisation - refreshing!");
} else {
if (responseJson.reason !== undefined && responseJson.reason !== null && responseJson.reason.length > 0) {
toast(responseJson.reason);
@@ -20649,7 +20668,7 @@ const AngularWorkflow = (defaultprops) => {
}
const buttonHeights = 45
const boxSize = buttonHeights
const boxSize = buttonHeights+2
const executionButton = executionRunning ? (
<Tooltip color="primary" title="Stop execution" placement="top">
<span>
@@ -20737,20 +20756,17 @@ const AngularWorkflow = (defaultprops) => {
>
<TextField
id="execution_argument_input_field"
style={{
...theme.palette.textFieldStyle,
height: buttonHeights + 2,
}}
variant="outlined"
InputProps={{
style: {
...theme.palette.innerTextfieldStyle,
height: buttonHeights + 2,
marginTop: -1,
border: "none",
height: buttonHeights+2,
marginTop: 0,
// Remove left side borderRadius
borderTopLeftRadius: 0,
borderBottomLeftRadius: 0,
borderTop: "0px solid",
}
}}
disabled={workflow.public}
@@ -21687,7 +21703,7 @@ const AngularWorkflow = (defaultprops) => {
return (
<div style={{ display: "flex" }}>
<div style={{ display: "flex", maxHeight: 500, overflow: "auto",}}>
<IconButton
style={{
marginTop: "auto",
@@ -21749,7 +21765,7 @@ const AngularWorkflow = (defaultprops) => {
onSelect={(select) => {
HandleJsonCopy(validate.result, select, "exec");
}}
name={false}
name={"$exec"}
/>
</div>
)
@@ -21787,7 +21803,26 @@ const AngularWorkflow = (defaultprops) => {
)
}
if (execution.execution_source === "authgroups") {
if (execution.execution_source?.startsWith("datastore")) {
const iconMargin = 7
return (
<div style={{
width: size,
height: size,
borderRadius: borderRadius,
backgroundColor: green,
}}>
<StorageIcon
style={{
width: size / 3 * 2,
height: size / 3 * 2,
marginLeft: iconMargin,
marginTop: iconMargin,
}}
/>
</div>
)
} else if (execution.execution_source === "authgroups") {
const iconMargin = 7
return (
<div style={{
@@ -22943,6 +22978,7 @@ const AngularWorkflow = (defaultprops) => {
</Typography>
</div>
) : null}
{executionData.execution_source !== undefined &&
executionData.execution_source !== null &&
executionData.execution_source.length > 0 &&
@@ -22954,8 +22990,17 @@ const AngularWorkflow = (defaultprops) => {
</Typography>
<Typography variant="body1" color="textSecondary">
{executionData.execution_source === "authgroups" || (executionData.authgroup !== undefined && executionData.authgroup !== null && executionData.authgroup.length > 0) ?
{executionData?.execution_source?.startsWith("datastore") ?
<a
rel="noopener noreferrer"
href={`/admin?tab=datastore${executionData.execution_source.split("|").length > 2 ? "&category="+executionData.execution_source.split("|")[1]+"&key="+executionData.execution_source.split("|")[2] : ""}`}
target="_blank"
style={{ textDecoration: "none", color: theme.palette.linkColor }}
>
Datastore Automation
</a>
:
executionData.execution_source === "authgroups" || (executionData.authgroup !== undefined && executionData.authgroup !== null && executionData.authgroup.length > 0) ?
<a
rel="noopener noreferrer"
href={`/admin?tab=app_auth`}
@@ -23614,7 +23659,7 @@ const AngularWorkflow = (defaultprops) => {
HandleJsonCopy(showResult, select, data.action.label);
console.log("SELECTED!: ", select);
}}
name={"Results for " + data.action.label}
name={`$${data?.action?.label?.toLowerCase()}`}
/>
</span>
+1 -1
View File
@@ -563,7 +563,7 @@ const AppCreator = (defaultprops) => {
};
const isCloud = (window.location.host === "localhost:3002" || window.location.host === "shuffler.io") ? true : (process.env.IS_SSR === "true");
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
useEffect(() => {
if (window.location.pathname.includes("apps/edit")) {
+1 -1
View File
@@ -247,7 +247,7 @@ const buttonBackground = "linear-gradient(to right, #f86a3e, #f34079)";
read_time: 1,
})
const isCloud = (window.location.host === "localhost:3002" || window.location.host === "shuffler.io") ? true : (process.env.IS_SSR === "true");
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
// FIXME: This is used, as useEffect() creates an issue with apps not loading at all
+50 -17
View File
@@ -62,7 +62,6 @@ const dividerColor = "rgb(225, 228, 232)";
const hrefStyle = {
color: "rgba(255, 255, 255, 0.8)",
textDecoration: "none",
marginRight: window.location.pathname.includes("/articles/") ? "0.8em" : undefined,
};
@@ -387,6 +386,7 @@ export const CodeHandler = (props) => {
minWidth: "50%",
maxWidth: "100%",
backgroundColor: theme.palette.inputColor,
whiteSpace: "pre-wrap",
overflowY: "auto",
// Have it inline
borderRadius: theme.palette?.borderRadius,
@@ -1020,7 +1020,7 @@ const Docs = (defaultprops) => {
}
const fetchDocList = (resetCache = false) => {
var url = `${globalUrl}/api/v1/docs`
var url = `${globalUrl}/api/v1/docs?resetCache=${resetCache}`
if (location.pathname.includes("/legal")) {
url = `${globalUrl}/api/v1/docs?folder=legal&resetCache=${resetCache}`
} else if (location.pathname.includes("/articles")) {
@@ -1047,16 +1047,16 @@ const Docs = (defaultprops) => {
.catch((error) => { });
};
const fetchDoc = (docId) => {
const fetchDoc = (docId, resetCache = false) => {
if (docId === undefined) {
return
}
var url = `${globalUrl}/api/v1/docs/${docId}`
var url = `${globalUrl}/api/v1/docs/${docId}?resetCache=${resetCache}`
if (location.pathname.includes("/legal")) {
url = `${globalUrl}/api/v1/docs/${docId}?folder=legal`
url = `${globalUrl}/api/v1/docs/${docId}?folder=legal&resetCache=${resetCache}`
} else if (location.pathname.includes("/articles")) {
url = `${globalUrl}/api/v1/docs/${docId}?folder=articles`
url = `${globalUrl}/api/v1/docs/${docId}?folder=articles&resetCache=${resetCache}`
}
fetch(url, {
@@ -1082,7 +1082,7 @@ const Docs = (defaultprops) => {
autoClose: 2000,
});
setTimeout(() => {
navigate(`/articles/2.0_release`)
navigate(`/articles`)
}, 2000)
return
}
@@ -1130,7 +1130,8 @@ const Docs = (defaultprops) => {
const handleResetCache = () => {
fetchDocList(true);
toast("Cache has been reset");
fetchDoc(props.match.params.key, true);
toast("Cache and list will be reset in a few seconds");
}
if (firstrequest) {
@@ -1344,6 +1345,21 @@ const Docs = (defaultprops) => {
tr: TableRowRenderer,
th: TableHeaderCellRenderer,
td: TableCellRenderer,
ul: ({ children }) => (
<Box component="ul" sx={{ my: 0.5, fontFamily: '"Segoe UI", Inter, sans-serif', fontSize: "17.6px" }}>
{children}
</Box>
),
ol: ({ children }) => (
<Box component="ol" sx={{ my: 0.5, fontFamily: '"Segoe UI", Inter, sans-serif', fontSize: "17.6px" }}>
{children}
</Box>
),
li: ({ children }) => (
<Typography component="li" sx={{ my: 0.5, color: "inherit", fontFamily: '"Segoe UI", Inter, sans-serif', fontSize: "17.6px" }}>
{children}
</Typography>
),
}
@@ -1355,7 +1371,6 @@ const Docs = (defaultprops) => {
const activeListItemStyle = {
backgroundColor: "rgba(248, 106, 62, 0.08)", // Slight orange tint
marginRight: window.location.pathname.includes("/articles/") ? "0.8em" : undefined,
borderLeft: "3px solid #f86a3e",
paddingLeft: "13px", // Compensate for the border
};
@@ -1415,6 +1430,7 @@ const Docs = (defaultprops) => {
<List style={{
listStyle: "none",
paddingLeft: "0",
paddingRight: isArticlePage ? 15 : undefined,
paddingTop: !isArticlePage ? "60px" : undefined,
paddingBottom: !isArticlePage ? "30px" : undefined,
display: isArticlePage ? sidebarOpen ? "block" : "none" : undefined,
@@ -1524,15 +1540,32 @@ const Docs = (defaultprops) => {
}
</div>
<div style={IndexBar}>
{userdata?.support && isArticlePage && (
<Button
variant="contained"
color="secondary"
onClick={handleResetCache}
style={{marginBottom: "15px"}}
{userdata?.support && (
<Tooltip
title="Wait about 5 minutes after updating the file on GitHub, then click to reset the cache. (Support only) "
placement="top"
componentsProps={{
tooltip: {
sx: {
backgroundColor: "rgba(33, 33, 33, 1)",
color: "rgba(241, 241, 241, 1)",
fontSize: 14,
border: "1px solid rgba(73, 73, 73, 1)",
fontFamily: theme?.typography?.fontFamily,
}
},
}}
arrow
>
Reset Cache
</Button>
<Button
variant="contained"
color="secondary"
onClick={handleResetCache}
style={{marginBottom: "15px"}}
>
Reset Cache
</Button>
</Tooltip>
)}
{tocLines.length > 0 ?
(
+8 -6
View File
@@ -111,8 +111,8 @@ const NewDashboard = (props) => {
const STATIC_TIME_PERCENT = 'TBD'
const STATIC_MONEY_PERCENT = 'TBD'
const kpis = [
{ value: timeFmt.display, title: timeFmt.title, label: 'Time saved', icon: <TrendingUpIcon sx={{ color: '#5cc879', fontSize: 34 }} />, percentage: STATIC_TIME_PERCENT, color: '#5cc879', disabled: true},
{ value: formatCurrencyCompact(totals.moneySavedDollars), label: 'Money saved', icon: <TrendingUpIcon sx={{ color: '#5cc879', fontSize: 34 }} />, percentage: STATIC_MONEY_PERCENT, color: '#5cc879', disabled: true, },
//{ value: timeFmt.display, title: timeFmt.title, label: 'Time saved', icon: <TrendingUpIcon sx={{ color: '#5cc879', fontSize: 34 }} />, percentage: STATIC_TIME_PERCENT, color: '#5cc879', disabled: true},
//{ value: formatCurrencyCompact(totals.moneySavedDollars), label: 'Money saved', icon: <TrendingUpIcon sx={{ color: '#5cc879', fontSize: 34 }} />, percentage: STATIC_MONEY_PERCENT, color: '#5cc879', disabled: true, },
{ value: String(unreadCount), label: 'Total errors', icon: <ErrorOutlineIcon sx={{ color: '#f87171', fontSize: 34, opacity: 0.9 }} />, percentage: "", color: '#f87171' },
{ value: String(readCount), label: 'Errors resolved', icon: <TaskAltIcon sx={{ color: '#5cc879', fontSize: 34, opacity: 0.9 }} />, percentage: "", color: '#5cc879' },
];
@@ -133,12 +133,12 @@ const NewDashboard = (props) => {
useEffect(() => {
const anyLoading =
loadingSfw ||
loadingRot ||
//loadingSfw ||
//loadingRot ||
loadingNoti ||
loadingSelectedOrgStats ||
loadingSelectedOrgStats //||
!selectedOrganization ||
!selectedOrgForStats;
//!selectedOrgForStats;
setShowOverlay(anyLoading);
}, [
loadingSfw,
@@ -399,6 +399,7 @@ const NewDashboard = (props) => {
headerSubtitle="Complete these steps to start seeing insights."
/>
)}
{showOverlay && (
<div style={{ position: 'absolute', inset: 0, background: 'rgba(17,17,17,0.6)', backdropFilter: 'blur(2px)', zIndex: 10, display: 'flex', alignItems: 'center', justifyContent: 'center', borderRadius: 12 }}>
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 12 }}>
@@ -407,6 +408,7 @@ const NewDashboard = (props) => {
</div>
</div>
)}
{/* Header / Greeting */}
<Box style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', margin: '8px 0 16px 0' }}>
<Typography variant="h5">{`${getGreeting()}, ${displayName ?? 'User'}!`}</Typography>
+1 -1
View File
@@ -25,7 +25,7 @@ const SetAuthentication = (props) => {
const [loadFail, setLoadFail] = useState("");
const [appAuthentication, setAppAuthentication] = React.useState([]);
const isCloud = (window.location.host === "localhost:3002" || window.location.host === "shuffler.io") ? true : (process.env.IS_SSR === "true");
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
//const alert = useAlert();
const parseIncomingOpenapiData = (data) => {
+1 -1
View File
@@ -47,7 +47,7 @@ const Welcome = (props) => {
}
}, [activeStep])
const isCloud = (window.location.host === "localhost:3002" || window.location.host === "shuffler.io") ? true : (process.env.IS_SSR === "true");
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
const [steps, setSteps] = useState([
"Help us get to know you",
"Find your Apps",
+3 -3
View File
@@ -343,7 +343,7 @@ export const GetIconInfo = (action) => {
"protect",
],
},
{ key: "intel", values: ["intel", "feed", "threat intel", "threat intelligence", "ti", "t.i.", "t.i", "ti.", "rule", "technique", "tactic", "techniques", "tactics", "ioc", "indicator", "hash", "ip", "url", "domain", ] },
{ key: "intel", values: ["intel", "feed", "threat intel", "threat intelligence", "ti", "t.i.", "t.i", "ti.", "rule", "technique", "tactic", "techniques", "tactics", "ioc", "indicator", "hash", "ip", "url", "domain", "sha", "md5", ] },
];
var selectedKey = ""
@@ -3346,7 +3346,7 @@ const Workflows2 = (props) => {
}
style={{
textDecoration: "none",
color: "inherit",
color: theme.palette.text.primary,
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
@@ -5149,7 +5149,7 @@ const Workflows2 = (props) => {
{backgroundWorkflows.length > 0 &&
<Tab
label={`Background Processes`}
label={`Security Bundle`}
value={4}
style={{
...tabStyle,