Tons of minor fixes from cloud sync

This commit is contained in:
Frikky
2025-07-11 16:04:14 +02:00
parent 4f48d71252
commit 7e9cdce4b6
29 changed files with 1788 additions and 1015 deletions
+4
View File
@@ -87,6 +87,10 @@ const Admin2 = (props) => {
leads.push("distribution partner");
}
if (responseJson.lead_info.channel_partner) {
leads.push("channel partner");
}
if (responseJson.lead_info.service_partner) {
leads.push("service partner");
}
+209 -43
View File
@@ -1,11 +1,14 @@
import React, { useState, useEffect, useContext, memo } from "react";
import { Context } from "../context/ContextApi.jsx";
import { useNavigate, Link, useLocation } from "react-router-dom";
import { getTheme } from "../theme.jsx";
import { toast } from "react-toastify"
import ReactJson from "react-json-view-ssr";
import { v4 as uuidv4} from "uuid";
import { validateJson, collapseField, handleReactJsonClipboard, HandleJsonCopy } from "../views/Workflows.jsx";
import {
Box,
Button,
ButtonGroup,
Typography,
@@ -13,6 +16,7 @@ import {
CircularProgress,
Tooltip,
IconButton,
TextField,
} from '@mui/material'
import {
@@ -21,6 +25,8 @@ import {
RestartAlt as RestartAltIcon,
ExpandMore as ExpandMoreIcon,
ExpandLess as ExpandLessIcon,
Send as SendIcon,
Error as ErrorIcon,
} from '@mui/icons-material'
import {
@@ -39,9 +45,12 @@ const AgentUI = (props) => {
const [originalStartTime, setOriginalStartTime] = useState(0)
const [latestEndTime, setLatestEndTime] = useState(0)
const [showAgentStarter, setShowAgentStarter] = useState(false)
const [actionInput, setActionInput] = useState("")
const {themeMode} = useContext(Context)
const theme = getTheme(themeMode)
const navigate = useNavigate();
const agentWrapperStyle = {
width: 1000,
@@ -64,6 +73,10 @@ const AgentUI = (props) => {
return
}
if (node_id === undefined || node_id === null || node_id === "") {
return
}
var found = false
for (var key in execution_data.results) {
const item = execution_data.results[key]
@@ -86,6 +99,16 @@ const AgentUI = (props) => {
if (found === false) {
toast.warn("Failed to find the relevant AI Agent result")
if (execution_data?.results?.length === 1) {
setAgentActionResult(execution_data.results[0])
const validatedData = validateJson(execution_data.results[0].result)
if (validatedData.valid) {
setData(validatedData.result)
} else {
toast.warn("Action output result is not valid JSON!")
}
}
}
}
@@ -95,10 +118,10 @@ const AgentUI = (props) => {
return
}
if (node_id === undefined || node_id === null) {
toast.error("No node ID provided. Please provide node_id in the URL.")
return
}
//if (node_id === undefined || node_id === null || node_id === "") {
// toast.error("No node ID provided. Please provide node_id in the URL.")
// return
//}
if (authorization === undefined || authorization === null) {
toast.error("No authorization provided. Please provide authorization in the URL.")
@@ -166,6 +189,7 @@ const AgentUI = (props) => {
const url = `${globalUrl}/api/v1/apps/agent/run?rerun=true&decision_id=${decision?.run_details?.id}`
var body = agentActionResult.action
console.log("BODY: ", body)
body.source_execution = execution.execution_id
body.source_workflow = execution.workflow.id
@@ -202,10 +226,11 @@ const AgentUI = (props) => {
const executionId = params.get("execution_id")
const nodeId = params.get("node_id")
const authorization = params.get("authorization")
if (executionId !== undefined && executionId !== null && nodeId !== undefined && nodeId !== null && authorization !== undefined && authorization !== null) {
if (executionId !== undefined && executionId !== null && authorization !== undefined && authorization !== null) {
GetExecution(executionId, nodeId, authorization)
} else {
toast.warn("No execution ID or node ID provided. Please provide execution_id and node_id in the URL.")
setShowAgentStarter(true)
//toast.warn("No execution ID or node ID provided. Please provide execution_id and node_id in the URL.")
}
}, [])
@@ -221,6 +246,11 @@ const AgentUI = (props) => {
<Tooltip title="Finished" placement="top">
<CheckCircleIcon style={{color: green, marginRight: 10, }} />
</Tooltip>
:
item.status === "ABORTED" || item.status === "FAILURE" ?
<Tooltip title={`${item.status}: Check the raw data`} placement="top">
<ErrorIcon style={{color: red, marginRight: 10, }} />
</Tooltip>
:
<Tooltip title={`Not started yet: ${item.status}`} placement="top">
<HourglassDisabledIcon style={{marginRight: 10, }} />
@@ -246,11 +276,13 @@ const AgentUI = (props) => {
const validate = validateJson(item.details)
const itemStartTime = item.start_time
var itemEndTime = item.end_time
if (itemStartTime !== undefined && (itemStartTime < originalStartTime || originalStartTime === 0)) {
setOriginalStartTime(itemStartTime)
if (itemStartTime !== undefined && itemStartTime !== originalStartTime && (itemStartTime < originalStartTime || originalStartTime === 0)) {
console.log("Rerender 1")
//setOriginalStartTime(itemStartTime)
}
if (itemEndTime !== undefined && itemEndTime > latestEndTime) {
console.log("Rerender 2")
setLatestEndTime(itemEndTime)
}
@@ -280,23 +312,47 @@ const AgentUI = (props) => {
borderTop: "1px solid " + theme.palette.surfaceColor,
borderRadius: theme.palette.borderRadius,
}}
onMouseEnter={() => {
if (!hovered) {
console.log("HOVER")
setHovered(true)
}
}}
onMouseLeave={() => {
if (hovered) {
setHovered(false)
}
}}
>
<div
style={{
display: "flex",
backgroundColor: hovered ? theme.palette.surfaceColor : "inherit",
}}
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
onClick={(e) => {
if (item.details === undefined || item.details === null || item.details === "") {
toast("No details to open")
if (item?.category === "agent" && item?.type === "agent") {
// Show all the data
if (openIndexes.includes(index)) {
console.log("Rerender 3")
setOpenIndexes(openIndexes.filter((i) => i !== index))
} else {
console.log("Rerender 4")
setOpenIndexes([...openIndexes, index])
}
} else {
toast.warn("No details to open")
}
return
}
if (openIndexes.includes(index)) {
console.log("Rerender 5")
setOpenIndexes(openIndexes.filter((i) => i !== index))
} else {
console.log("Rerender 6")
setOpenIndexes([...openIndexes, index])
}
}}
@@ -421,18 +477,31 @@ const AgentUI = (props) => {
const TimelineRender = (props) => {
const { agent_data } = props;
const actionResult = execution?.results?.length > 0 ? execution.results[0] : execution
var timelineItems = [
{
"label": "AI Agent 2",
"type": "agent",
"category": "agent",
"details": actionResult?.result,
"status": agent_data.status,
"start_time": agent_data.started_at,
"end_time": agent_data.completed_at,
"status": agent_data?.status,
"start_time": agent_data?.started_at,
"end_time": agent_data?.completed_at,
},
]
// Autofixer for result lol
if ((agent_data?.decisions === undefined || agent_data?.decisions === null)) {
const verifiedInput = validateJson(actionResult?.result)
if (verifiedInput.valid === true && verifiedInput.result?.decisions !== undefined && verifiedInput.result?.decisions !== null) {
agent_data.decisions = verifiedInput.result?.decisions
setAgentActionResult(actionResult)
}
}
var sortedTimelineItems = []
for (var key in agent_data?.decisions) {
const item = agent_data.decisions[key]
@@ -501,39 +570,136 @@ const AgentUI = (props) => {
)
}
const submitInput = (inputText) => {
toast.info("Submitting AI Agent input: " + inputText);
//setShowAgentStarter(false);
//GetExecution(execution?.execution_id, execution?.node_id, execution?.authorization);
if (inputText === undefined || inputText === null || inputText === "") {
toast.error("Please provide a valid input for the AI Agent.")
return
}
// 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()
const data = {
"id": uuid,
"name":"agent",
//"app_name":"Shuffle AI",
"app_name":"AI Agent", // Failover for rerun
"app_id":"shuffle_agent",
"app_version":"1.0.0",
"environment":"cloud",
"parameters":[
{
"name":"app_name",
"value":"openai"
},
{
"name":"input",
"value": inputText
},
{
"name":"action",
"value":"list_tickets"
}
]}
const url = `${globalUrl}/api/v1/apps/agent_starter/run`
fetch(url, {
method: "POST",
body: JSON.stringify(data),
credentials: "include",
})
.then((response) => {
return response.json()
})
.then((responseJson) => {
toast.success("Got response!")
console.log("Agent run response: ", responseJson)
if (responseJson.success === true && responseJson.authorization !== undefined && responseJson.execution_id !== undefined) {
navigate("?execution_id=" + responseJson.execution_id + "&authorization=" + responseJson.authorization)
setShowAgentStarter(false)
GetExecution(responseJson.execution_id, "", responseJson.authorization)
}
})
.catch((error) => {
toast.error("Error: " + error)
})
}
return (
<div style={agentWrapperStyle}>
{/*
<Typography variant="h4">
Agent Input: {data.input}
</Typography>
*/}
<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>
{showAgentStarter ?
<Box component="form" style={{textAlign: "center", }} onSubmit={(e) => {
e.preventDefault();
submitInput(actionInput);
}}>
<img src="/images/logos/agent.svg" style={{
width: 200,
height: 200,
}} />
<div />
{buttonState === "timeline" ?
<TimelineRender agent_data={data} />
:
null
<Typography variant="h5" style={{marginTop: 30, }}>
Shuffle AI Agents
</Typography>
<TextField
label="Agent Input"
variant="outlined"
style={{width: 300, marginRight: 20, marginTop: 30, }}
defaultValue={execution?.execution_id || ""}
onChange={(e) => {
setActionInput(e.target.value)
}}
InputProps={{
endAdornment: (
<Tooltip title="This is the input for the AI Agent. It can be any valid JSON.">
<IconButton type="submit">
<SendIcon
color="primary"
/>
</IconButton>
</Tooltip>
),
}}
/>
</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>
{buttonState === "timeline" ?
<TimelineRender agent_data={data} />
:
null
}
</div>
}
</div>
)
+49 -17
View File
@@ -401,7 +401,6 @@ export function SetJsonDotnotation(jsonInput, inputKey) {
//export const green = "#86c142";
export const green = "#02CB70"
export const yellow = "#FECC00";
//export const red = "#ff3632";
export const red = "#F53434";
export const grey = "#b0b0b0";
@@ -729,6 +728,8 @@ const AngularWorkflow = (defaultprops) => {
"List tickets",
"Send Email",
"Get specific ticket",
"Update ticket",
"Add ticket comment",
],
"multiselect": true,
},
@@ -2258,7 +2259,7 @@ const AngularWorkflow = (defaultprops) => {
currentnode.removeClass("shuffle-hover-highlight");
currentnode.removeClass("awaiting-data-highlight");
currentnode.addClass("success-highlight");
incomingEdges.addClass("success-highlight");
outgoingEdges.addClass("success-highlight");
if (visited !== undefined && visited !== null && !visited.includes(label)) {
@@ -5368,7 +5369,8 @@ const AngularWorkflow = (defaultprops) => {
if (!branchFound) {
var relevantNodes = []
const minDistance = 185
//const minDistance = 185
const minDistance = 85
const draggedNode = event.target
const allnodes = cy.nodes().jsons()
for (var nodekey in allnodes) {
@@ -5398,7 +5400,7 @@ const AngularWorkflow = (defaultprops) => {
if (decoratorNodeIds.includes(node.data.id)) {
// Drag a little farther to remove it
if (distance > minDistance + 75) {
if (distance > minDistance + 125) {
// Remove the branch? Why?
const edgeToRemove = cy.getElementById(branches[branchkey].data.id)
if (edgeToRemove !== null && edgeToRemove !== undefined) {
@@ -18187,8 +18189,8 @@ const AngularWorkflow = (defaultprops) => {
</div>
</div>
const defaultEnvironment = environments && environments?.find(
(env) => env?.default && env?.Name?.toLowerCase() !== "cloud"
const defaultEnvironment = environments.find(
(env) => env.default && env.Name.toLowerCase() !== "cloud"
);
if (selectedTrigger.trigger_type === "PIPELINE" && selectedTrigger.environment === "onprem" && defaultEnvironment !== undefined) {
@@ -20997,7 +20999,10 @@ const AngularWorkflow = (defaultprops) => {
</div>
*/}
{userdata.support === true || (userdata.avatar !== undefined && (userdata.avatar === creatorProfile.github_avatar || allowList.includes(userdata.public_username))) ?
{userdata.support ||
(userdata.avatar !== undefined && (userdata.avatar === creatorProfile.github_avatar || allowList.includes(userdata.public_username))) ||
(workflow?.owner?.length > 0 && workflow.owner === userdata?.active_org?.id && userdata?.active_org.role === "admin") ||
(workflow?.owner?.length > 0 && workflow.owner === userdata?.id)?
<div style={{ marginTop: 50, }}>
<Typography variant="body2" color="textSecondary">
You can see these buttons because you may have the correct access rights as a creator to help modify this workflow.
@@ -22738,7 +22743,7 @@ const AngularWorkflow = (defaultprops) => {
style={{ zIndex: 50000 }}
>
<ArrowLeftIcon style={{
color: relevant_errors.length > 0 ? yellow : theme.palette.textColor,
color: relevant_errors.length > 0 ? red : theme.palette.textColor,
}} />
</Tooltip>
</IconButton>
@@ -22783,6 +22788,26 @@ const AngularWorkflow = (defaultprops) => {
</span>
: null}
{data?.action?.name === "run_schemaless" || data?.action?.name === "run_singul" || data?.action?.name === "singul" && data?.action?.parameters?.length > 4 ?
<div
style={{flex: 10, float: "right", textAlign: "right", }}
>
<Tooltip title={"Explore the raw debug-output"}>
<a
rel="noopener noreferrer"
href={data?.action?.parameters?.find((param) => param?.name === "X-Debug-Url")?.value || ""}
target="_blank"
style={{
textDecoration: "none",
color: "rgba(255,255,255,0.4)",
}}
>
<OpenInNewIcon />
</a>
</Tooltip>
</div>
: null}
{data.action.app_name === "shuffle-subflow" &&
validate.result.success !== undefined &&
validate.result.success === true ? (
@@ -22944,7 +22969,7 @@ const AngularWorkflow = (defaultprops) => {
>
<b>Action Logs</b>
</Typography>
<Typography variant="body2" style={{ whiteSpace: 'pre-line', }}>
<Typography variant="body2" color="textSecondary" style={{ whiteSpace: 'pre-line', }}>
More log details for this action are not available without <a style={{ color: theme.palette.linkColor, }} href="/admin?tab=locations" target="_blank" rel="noopener noreferrer">an onprem environment</a> with the <a style={{ color: theme.palette.linkColor, }} href="/docs/configuration#scaling-shuffle" target="_blank" rel="noopener noreferrer">SHUFFLE_LOGS_DISABLED</a> environment variable set to false: SHUFFLE_LOGS_DISABLED=false. Logs are enabled by default, except in scale mode.
</Typography>
</div>
@@ -23002,7 +23027,9 @@ const AngularWorkflow = (defaultprops) => {
variant="body1"
style={{}}
>
<b>{data.name}</b>: {showVariable ? data.value : null}
<b>{data.name}</b>: <span style={{color: "rgba(255,255,255,0.5)" }}>
{showVariable ? data.value : null}
</span>
</Typography>
}
{open ?
@@ -23024,8 +23051,8 @@ const AngularWorkflow = (defaultprops) => {
<Typography
variant="body2"
style={{
marginTop: 5,
whiteSpace: 'pre-line',
color: showlink ? "#FF8544" : theme.palette.text.primary,
cursor: showlink ? "pointer" : "default",
}}
onClick={(e) => {
@@ -23035,7 +23062,7 @@ const AngularWorkflow = (defaultprops) => {
window.open(data.value, "_blank")
}
}}
color={showlink ? "inherit" : "textSecondary"}
color={showlink ? "#ff8544" : "textSecondary"}
>
{data.value}
</Typography>
@@ -23199,8 +23226,9 @@ const AngularWorkflow = (defaultprops) => {
sx: {
pointerEvents: "auto",
color: theme.palette.text.primary,
minWidth: isMobile ? "90%" : "750px",
maxHeight: "550px",
minWidth: isMobile ? "90%" : 900,
minHeight: 500,
maxHeight: 650,
overflowY: "auto",
overflowX: "hidden",
border: theme.palette.defaultBorder,
@@ -23418,7 +23446,9 @@ const AngularWorkflow = (defaultprops) => {
>
<b>{selectedResult.action.label.replaceAll("_", " ")}</b>
</div>
<div style={{ fontSize: 14, color: theme.palette.textColor, }}>{selectedResult.action.name}</div>
<Typography variant="body2" color="textSecondary" style={{ }}>
{selectedResult.action.name}
</Typography>
</div>
</div>
@@ -25588,12 +25618,14 @@ const AngularWorkflow = (defaultprops) => {
const actionIndex = workflow?.actions.findIndex(action => action.id === actionId);
if (actionIndex >= 0) {
// Find the parameter with matching name
console.log("fieldName", fieldName)
const paramIndex = workflow.actions[actionIndex].parameters.findIndex(param => param.name === fieldName);
if (paramIndex >= 0) {
// Update the parameter value
workflow.actions[actionIndex].parameters[paramIndex].value = newData;
if(selectedAction !== undefined && selectedAction !== null && selectedAction.id === actionId) {
selectedAction.parameters[paramIndex].value = newData;
setSelectedAction(selectedAction)
}
// Update workflow state to trigger re-render
setWorkflow({...workflow});
setLastSaved(false);
+226 -6
View File
@@ -233,6 +233,7 @@ const buttonBackground = "linear-gradient(to right, #f86a3e, #f34079)";
const [secondaryApp, setSecondaryApp] = useState({});
const [firstRequest, setFirstRequest] = useState(true);
const [publishModalOpen, setPublishModalOpen] = React.useState(false);
const [showDistributionPopup, setShowDistributionPopup] = React.useState(false);
const [categories, setCategories] = useState(appCategories)
const [newWorkflowCategories, setNewWorkflowCategories] = React.useState([]);
@@ -743,7 +744,7 @@ const buttonBackground = "linear-gradient(to right, #f86a3e, #f34079)";
}
};
const activateApp = (action) => {
const activateApp = (action, org_id, multiple_request = false) => {
if (serverside === true) {
return
}
@@ -753,11 +754,14 @@ const buttonBackground = "linear-gradient(to right, #f86a3e, #f34079)";
if (action !== undefined && action !== null) {
url = `${globalUrl}/api/v1/apps/${appId}/${action}`
}
fetch(url, {
fetch(url, {
method: "GET",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
"Accept": "application/json",
"Org-Id": org_id !== undefined && org_id !== null && org_id?.length > 0 ? org_id : userdata.active_org.id
},
credentials: "include",
})
@@ -784,7 +788,7 @@ const buttonBackground = "linear-gradient(to right, #f86a3e, #f34079)";
}
}
} else {
if (checkLogin !== undefined && checkLogin !== null) {
if ((checkLogin !== undefined && checkLogin !== null) && !multiple_request) {
checkLogin()
}
@@ -795,6 +799,9 @@ const buttonBackground = "linear-gradient(to right, #f86a3e, #f34079)";
toast("App activated for your organization!")
}
} else {
if (responseJson.success && !multiple_request &&(responseJson.reason !== undefined || responseJson.reason !== null)) {
toast.success(`${responseJson.reason}`);
}
}
}
})
@@ -3909,6 +3916,212 @@ const buttonBackground = "linear-gradient(to right, #f86a3e, #f34079)";
</Dialog>
) : null;
const handleActivateApp = (id, action) => {
if (action === "activate_all") {
const childOrgs = userdata.orgs.filter(
(data) => data.creator_org === userdata.active_org.id
);
const orgIds = childOrgs.map((data) => data.id);
// run app activation requset for each org
orgIds.forEach((orgId) => {
activateApp("activate", orgId, true)
});
setTimeout(() => {
toast.success("App activated for all sub-orgs");
}, 5000);
} else if (action === "deactivate_all") {
const childOrgs = userdata.orgs.filter(
(data) => data.creator_org === userdata.active_org.id
);
const orgIds = childOrgs.map((data) => data.id);
// run app deactivation request for each org
orgIds.forEach((orgId) => {
activateApp("deactivate", orgId, true)
});
setTimeout(() => {
toast.success("App deactivated for all sub-orgs");
}, 5000);
} else if (action === "activate_single") {
if (id === null) {
toast.error("Please select a sub-org to activate the app for.");
return;
}
activateApp("activate", id);
} else if (action === "deactivate_single") {
if (id === null) {
toast.error("Please select a sub-org to deactivate the app for.");
return;
}
activateApp("deactivate", id);
}
};
const appDistributinModal = showDistributionPopup ? (
<Dialog
open={showDistributionPopup}
onClose={() => setShowDistributionPopup(false)}
PaperProps={{
sx: {
borderRadius: theme?.palette?.DialogStyle?.borderRadius || 3,
border: theme?.palette?.DialogStyle?.border,
fontFamily: theme?.typography?.fontFamily,
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
zIndex: 1000,
minWidth: "600px",
minHeight: "320px",
overflow: "auto",
'& .MuiDialogContent-root': {
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
},
'& .MuiDialogTitle-root': {
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
p: 2,
},
'& .MuiDialogActions-root': {
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
},
},
}}
>
<DialogTitle
sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
pr: 1,
pb: 1,
}}
>
<Typography variant="h6" fontWeight={600} color="text.primary">
Select sub-org to distribute App
</Typography>
<IconButton
onClick={() => setShowDistributionPopup(false)}
sx={{
color: theme.palette.text.primary,
}}
>
<CloseIcon />
</IconButton>
</DialogTitle>
<DialogContent
sx={{
color: "rgba(255,255,255,0.85)",
px: 2,
pt: 1,
pb: 2,
display: "flex",
flexDirection: "column",
gap: 1,
}}
>
<MenuItem
value="none"
onClick={() => handleActivateApp(null, "deactivate_all")}
sx={{
borderRadius: 1,
px: 2,
'&:hover': {
backgroundColor: 'rgba(255,255,255,0.08)',
},
}}
>
Deactivate for all suborgs
</MenuItem>
<MenuItem
value="all"
onClick={() => handleActivateApp(null, "activate_all")}
sx={{
borderRadius: 1,
px: 2,
'&:hover': {
backgroundColor: 'rgba(255,255,255,0.08)',
},
}}
>
Activate for all suborgs
</MenuItem>
{userdata.orgs.map((data, index) => {
if (data.creator_org !== userdata.active_org.id) return null;
const imageSize = 28;
const imageStyle = {
width: imageSize,
height: imageSize,
borderRadius: '50%',
objectFit: 'cover',
marginRight: 12,
};
const imageSrc = data.image || theme.palette.defaultImage;
return (
<MenuItem
key={index}
value={data.id}
sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
borderRadius: 1,
px: 2,
py: 1,
'&:hover': {
backgroundColor: 'rgba(255,255,255,0.06)',
},
}}
>
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<img alt={data.name} src={imageSrc} style={imageStyle} />
<Typography variant="body1" color="text.primary">
{data.name}
</Typography>
</Box>
<Box sx={{ display: 'flex', gap: 1 }}>
<Button
variant="outlined"
color="secondary"
size="small"
onClick={() => handleActivateApp(data.id, "activate_single")}
sx={{
textTransform: 'none',
borderRadius: '6px',
fontWeight: 500,
}}
>
Activate
</Button>
<Button
variant="outlined"
color="secondary"
size="small"
onClick={() => handleActivateApp(data.id, "deactivate_single")}
sx={{
textTransform: 'none',
borderRadius: '6px',
fontWeight: 500,
}}
>
Deactivate
</Button>
</Box>
</MenuItem>
);
})}
</DialogContent>
</Dialog>
) : null;
const landingpageDataBrowser = (
<div
style={{
@@ -3918,6 +4131,7 @@ const buttonBackground = "linear-gradient(to right, #f86a3e, #f34079)";
}}
>
{publishModal}
{appDistributinModal}
<div style={{ display: "flex", position: "relative" }}>
{isMobile ? null : (
<Breadcrumbs
@@ -4066,7 +4280,7 @@ const buttonBackground = "linear-gradient(to right, #f86a3e, #f34079)";
</Button>
}
{isMobile || app?.reference_org === userdata?.active_org?.id ? null : (
{isMobile || app?.reference_org === userdata?.active_org?.id || (app?.suborg_distribution?.includes(userdata?.active_org?.id)) ? null : (
<Button
variant={userdata.active_apps !== undefined && userdata.active_apps !== null && userdata.active_apps.includes(appId) ? "outlined": "contained"}
component="label"
@@ -4168,7 +4382,8 @@ const buttonBackground = "linear-gradient(to right, #f86a3e, #f34079)";
Try the API
</Button>
</a>
<Select
{app?.reference_org === userdata?.active_org?.id ? (
<Select
value={sharingConfiguration}
disabled={!isCloud}
onChange={(event) => {
@@ -4223,6 +4438,11 @@ const buttonBackground = "linear-gradient(to right, #f86a3e, #f34079)";
);
})}
</Select>
): null}
{userdata && (userdata?.active_org?.creator_org?.length > 0 || userdata?.active_org?.child_orgs?.length === 0) ? null : (
<Button variant="outlined" color="secondary" onClick={()=> {setShowDistributionPopup(true)}} >Distribute App</Button>
)}
</div>
) : (
<a
+10 -16
View File
@@ -315,7 +315,7 @@ const RadialChart = ({keys, setSelectedCategory}) => {
// What data do we fill in here? Idk
const Dashboard = (props) => {
const { globalUrl, isLoggedIn } = props;
const { globalUrl, userdata, isLoggedIn } = props;
//const alert = useAlert();
const [bigChartData, setBgChartData] = useState("data1");
const [dayAmount, setDayAmount] = useState(7);
@@ -323,10 +323,10 @@ const Dashboard = (props) => {
const [stats, setStats] = useState({});
const [changeme, setChangeme] = useState("");
const [statsRan, setStatsRan] = useState(false);
const [keys, setKeys] = useState([])
const [treeKeys, setTreeKeys] = useState([])
const [keys, setKeys] = useState([])
const [treeKeys, setTreeKeys] = useState([])
const [selectedUsecaseCategory, setSelectedUsecaseCategory] = useState("");
const [selectedUsecaseCategory, setSelectedUsecaseCategory] = useState("")
const [selectedUsecases, setSelectedUsecases] = useState([]);
const [usecases, setUsecases] = useState([]);
const [workflows, setWorkflows] = useState([]);
@@ -750,8 +750,6 @@ const Dashboard = (props) => {
const newname = data.key !== undefined ? data.key.replaceAll("_", " ") : ""
console.log("KEYDATA: ", data)
const loadNewStats = (newkey) => {
const resp = LoadStats(globalUrl, newkey)
if (resp !== undefined) {
@@ -807,7 +805,7 @@ const Dashboard = (props) => {
backgroundColor: theme.palette.inputColor,
color: "white",
height: 40,
maxWidth: 150,
maxWidth: 200,
borderRadius: theme.palette?.borderRadius,
}}
>
@@ -829,10 +827,12 @@ const Dashboard = (props) => {
</Select>
}
</div>
<DashboardBarchart
timelineData={data}
height={50}
/>
</Paper>
</Draggable>
)
@@ -848,14 +848,6 @@ const Dashboard = (props) => {
: null}
</div>
{/*widgetData === undefined || widgetData === null || widgetData === [] || widgetData.length === 0 ? null :
<Draggable>
<Paper style={{height: 350, width: 500, padding: "15px 15px 15px 15px", }}>
<LineChartWrapper keys={widgetData[0]} height={280} width={470} />
</Paper>
</Draggable>
*/}
{newWidgetData === undefined || newWidgetData === null || newWidgetData === [] ? null :
newWidgetData.map((data, index) => {
@@ -872,7 +864,9 @@ const Dashboard = (props) => {
);
const dataWrapper = (
<div style={{ maxWidth: 1366, margin: "auto", paddingTop: 10, }}>{data}</div>
<div style={{ maxWidth: 1366, margin: "auto", paddingTop: 10, }}>
{data}
</div>
);
return dataWrapper;
+1 -1
View File
@@ -558,7 +558,7 @@ export const handleReactJsonClipboard = (copy) => {
document.execCommand("copy");
console.log("COPYING!");
toast("Copied value to clipboard, NOT json path.")
toast.success("Copied Value, NOT json path.")
} else {
console.log("Failed to copy from " + elementName + ": ", copyText);
}