Tons of minor fixes from cloud sync
This commit is contained in:
@@ -92,9 +92,14 @@ const AdminNavBar = (props) => {
|
|||||||
const HandleVisibleTabs = () => {
|
const HandleVisibleTabs = () => {
|
||||||
if (userdata?.id?.length > 0) {
|
if (userdata?.id?.length > 0) {
|
||||||
if (userdata?.active_org?.role === "admin" || userdata?.support) {
|
if (userdata?.active_org?.role === "admin" || userdata?.support) {
|
||||||
setVisibleItems(items);
|
if (isChildOrg) {
|
||||||
|
const filteredItems = items.filter(item => item.text !== "Partner");
|
||||||
|
setVisibleItems(filteredItems);
|
||||||
|
}else {
|
||||||
|
setVisibleItems(items);
|
||||||
|
}
|
||||||
}else {
|
}else {
|
||||||
const filteredItems = items.filter(item => item.text !== "Users" && item.text !== "Files" && item.text !== "Datastore" && item.text !== "Triggers" && item.text !== "Locations");
|
const filteredItems = items.filter(item => item.text !== "Users" && item.text !== "Files" && item.text !== "Datastore" && item.text !== "Triggers" && item.text !== "Locations" && item.text !== "Partner");
|
||||||
setVisibleItems(filteredItems);
|
setVisibleItems(filteredItems);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -105,12 +110,12 @@ const AdminNavBar = (props) => {
|
|||||||
// Filter out Users and Tenants tabs
|
// Filter out Users and Tenants tabs
|
||||||
if (userdata?.active_org?.role === "admin" || userdata?.support) {
|
if (userdata?.active_org?.role === "admin" || userdata?.support) {
|
||||||
const filteredItems = items.filter(item =>
|
const filteredItems = items.filter(item =>
|
||||||
item.text !== "Users" && item.text !== "Tenants"
|
item.text !== "Users" && item.text !== "Tenants" && item.text !== "Partner"
|
||||||
);
|
);
|
||||||
setVisibleItems(filteredItems);
|
setVisibleItems(filteredItems);
|
||||||
}else {
|
}else {
|
||||||
const filteredItems = items.filter(item =>
|
const filteredItems = items.filter(item =>
|
||||||
item.text !== "Users" && item.text !== "Tenants" && item.text !== "Files" && item.text !== "Datastore" && item.text !== "Triggers" && item.text !== "Locations"
|
item.text !== "Users" && item.text !== "Partner" && item.text !== "Tenants" && item.text !== "Files" && item.text !== "Datastore" && item.text !== "Triggers" && item.text !== "Locations"
|
||||||
);
|
);
|
||||||
setVisibleItems(filteredItems);
|
setVisibleItems(filteredItems);
|
||||||
}
|
}
|
||||||
@@ -191,7 +196,7 @@ const AdminNavBar = (props) => {
|
|||||||
|
|
||||||
const params = new URLSearchParams(location.search);
|
const params = new URLSearchParams(location.search);
|
||||||
const tab = params?.get('tab')?.toLowerCase();
|
const tab = params?.get('tab')?.toLowerCase();
|
||||||
if (tab === "users" || tab === "tenants") {
|
if (tab === "users" || tab === "tenants" || tab === "partner") {
|
||||||
toast.info("You are not allowed to access this tab. Please contact your admin for more information. Redirecting to Organization Configuration tab.");
|
toast.info("You are not allowed to access this tab. Please contact your admin for more information. Redirecting to Organization Configuration tab.");
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setSelectedItem("Organization");
|
setSelectedItem("Organization");
|
||||||
@@ -200,6 +205,7 @@ const AdminNavBar = (props) => {
|
|||||||
}
|
}
|
||||||
, 3000);
|
, 3000);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (userdata && isOrgLoaded && isUserDataLoaded && userdata?.active_org?.role !== "admin" && !userdata?.support) {
|
} else if (userdata && isOrgLoaded && isUserDataLoaded && userdata?.active_org?.role !== "admin" && !userdata?.support) {
|
||||||
const queryParams = new URLSearchParams(location.search);
|
const queryParams = new URLSearchParams(location.search);
|
||||||
const tabName = queryParams?.get('admin_tab')?.toLowerCase();
|
const tabName = queryParams?.get('admin_tab')?.toLowerCase();
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
// import Switch from '@mui/material/Switch';
|
// import Switch from '@mui/material/Switch';
|
||||||
// import { Typography, Button } from '@mui/material';
|
// import { Typography, Button } from '@mui/material';
|
||||||
// import { useNavigate, Link, useParams } from "react-router-dom";
|
// import { useNavigate, Link, useParams } from "react-router-dom";
|
||||||
// import { Bar } from 'react-chartjs-2';
|
|
||||||
// import Grid from '@mui/material/Grid';
|
// import Grid from '@mui/material/Grid';
|
||||||
// import SearchIcon from '@mui/icons-material/Search';
|
// import SearchIcon from '@mui/icons-material/Search';
|
||||||
// import NewReleasesIcon from '@mui/icons-material/NewReleases';
|
// import NewReleasesIcon from '@mui/icons-material/NewReleases';
|
||||||
|
|||||||
@@ -94,9 +94,9 @@ const Billing = memo((props) => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (userdata.app_execution_limit !== undefined && userdata.app_execution_usage !== undefined) {
|
if (userdata.app_execution_limit !== undefined && userdata.app_execution_usage !== undefined) {
|
||||||
const percentage = (userdata.app_execution_usage / userdata.app_execution_limit) * 100;
|
const percentage = ((userdata.app_execution_usage + userdata.app_executions_suborgs) / userdata.app_execution_limit) * 100;
|
||||||
setCurrentAppRunsInPercentage(Math.round(percentage));
|
setCurrentAppRunsInPercentage(Math.round(percentage));
|
||||||
setCurrentAppRunsInNumber(userdata.app_execution_limit - userdata.app_execution_usage);
|
setCurrentAppRunsInNumber(userdata.app_execution_limit - userdata.app_execution_usage - userdata.app_executions_suborgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (userdata?.id?.length > 0 && isLoggedIn === false){
|
if (userdata?.id?.length > 0 && isLoggedIn === false){
|
||||||
@@ -1860,19 +1860,6 @@ const Billing = memo((props) => {
|
|||||||
|
|
||||||
const updateAlertThreshold = (index, field, value) => {
|
const updateAlertThreshold = (index, field, value) => {
|
||||||
|
|
||||||
if (field === 'percentage') {
|
|
||||||
if (value > 100 || value < 0) {
|
|
||||||
value = 0
|
|
||||||
toast("The percentage value should be between 0 and 100")
|
|
||||||
}
|
|
||||||
} else if (field === 'count') {
|
|
||||||
if (value < 0 || value >= userdata.app_execution_limit) {
|
|
||||||
value = 0
|
|
||||||
toast("The count value should be greater than 0 and less than the total app execution limit")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const totalValue = userdata.app_execution_limit;
|
const totalValue = userdata.app_execution_limit;
|
||||||
const newAlertThresholds = alertThresholds.map((threshold, i) => {
|
const newAlertThresholds = alertThresholds.map((threshold, i) => {
|
||||||
if (i === index) {
|
if (i === index) {
|
||||||
@@ -2423,9 +2410,21 @@ const Billing = memo((props) => {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Typography style={{marginTop: 10, fontSize: 16,}} color="textSecondary">
|
<Typography style={{marginTop: 10, fontSize: 16,}} color="textSecondary">
|
||||||
You have used <strong>{currentAppRunsInPercentage}%</strong> of total app execution limit or <strong>{userdata.app_execution_usage}</strong> app runs out of <strong>{userdata.app_execution_limit}</strong> app runs.
|
You have used <strong>{currentAppRunsInPercentage}%</strong> of total app execution limit or <strong>{userdata.app_execution_usage + userdata.app_executions_suborgs}</strong> app runs out of <strong>{userdata.app_execution_limit}</strong> app runs.
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
|
{userdata?.active_org?.creator_org?.length > 0 ? null :
|
||||||
|
(
|
||||||
|
<>
|
||||||
|
<Typography color="textSecondary" style={{ marginTop: 20, fontSize: 16 }}>
|
||||||
|
Parent Organization App Executions: <strong>{userdata.app_execution_usage}</strong>
|
||||||
|
</Typography>
|
||||||
|
<Typography color="textSecondary" style={{ fontSize: 16 }}>
|
||||||
|
Sub-Organization App Executions: <strong>{userdata.app_executions_suborgs || "N/A"}</strong>
|
||||||
|
</Typography>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<Typography style={{ marginTop: 20, fontSize: 18 }}>
|
<Typography style={{ marginTop: 20, fontSize: 18 }}>
|
||||||
Set email alert thresholds for app runs
|
Set email alert thresholds for app runs
|
||||||
@@ -2442,8 +2441,8 @@ const Billing = memo((props) => {
|
|||||||
: " " + 0 + " "}
|
: " " + 0 + " "}
|
||||||
app runs.
|
app runs.
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography color="textSecondary" style={{ fontSize: 16 }}>
|
<Typography color="textSecondary" style={{ fontSize: 16, marginTop: 10 }}>
|
||||||
<span style={{fontWeight: 'bold'}}>Please note</span>: Once your app runs reach the set alert threshold, all admins in the organization will receive an email notification.
|
<span style={{fontWeight: 'bold'}}>Please note</span>: Once your app runs reach the set alert threshold, all admins in the organization will receive an email notification. For Parent organizations, the alert will be sent base on the total app runs from both parent and sub-organizations. For Sub-organizations, the alert will be sent based on the app runs of the sub-organization only.
|
||||||
</Typography>
|
</Typography>
|
||||||
<div style={{ marginTop: 15 }}>
|
<div style={{ marginTop: 15 }}>
|
||||||
{alertThresholds.map((threshold, index) => (
|
{alertThresholds.map((threshold, index) => (
|
||||||
@@ -2706,6 +2705,7 @@ const BillingStatsChildOrg = memo(({ userdata, globalUrl, selectedOrganization,
|
|||||||
const { themeMode, brandColor, supportEmail } = useContext(Context);
|
const { themeMode, brandColor, supportEmail } = useContext(Context);
|
||||||
const theme = getTheme(themeMode, brandColor);
|
const theme = getTheme(themeMode, brandColor);
|
||||||
|
|
||||||
|
|
||||||
// Handle page change
|
// Handle page change
|
||||||
const handleChangePage = (event, newPage) => {
|
const handleChangePage = (event, newPage) => {
|
||||||
setPage(newPage);
|
setPage(newPage);
|
||||||
@@ -2830,6 +2830,7 @@ const BillingStatsChildOrg = memo(({ userdata, globalUrl, selectedOrganization,
|
|||||||
usage: stat?.monthly_app_executions || "N/A",
|
usage: stat?.monthly_app_executions || "N/A",
|
||||||
workflows_usage: stat?.total_workflow_executions || "N/A",
|
workflows_usage: stat?.total_workflow_executions || "N/A",
|
||||||
workflow_usage_limit: subOrg?.sync_features?.workflow_executions?.limit || "N/A",
|
workflow_usage_limit: subOrg?.sync_features?.workflow_executions?.limit || "N/A",
|
||||||
|
app_runs_hard_limit: subOrg?.Billing?.app_runs_hard_limit || 0,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -2890,6 +2891,33 @@ const BillingStatsChildOrg = memo(({ userdata, globalUrl, selectedOrganization,
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
field: "app_runs_hard_limit", headerName: "App Executions Hard Limit", width: 200, renderCell: (params) => {
|
||||||
|
console.log("params.row: ", params.row)
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Typography style={{ fontSize: 16 }}>
|
||||||
|
{params.row.app_runs_hard_limit}
|
||||||
|
</Typography>
|
||||||
|
<IconButton
|
||||||
|
style={{ color: theme.palette.primary.main }}
|
||||||
|
onClick={() => {
|
||||||
|
setOpen(true)
|
||||||
|
setEditingOrgId(params.row.orgId)
|
||||||
|
setEditing("app_executions_hard_limit")
|
||||||
|
if (params.row.app_runs_hard_limit === "N/A") {
|
||||||
|
setLimit("")
|
||||||
|
} else {
|
||||||
|
setLimit(params.row.app_runs_hard_limit)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Edit/>
|
||||||
|
</IconButton>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
setSubOrgStatsColumns(columns)
|
setSubOrgStatsColumns(columns)
|
||||||
@@ -2913,7 +2941,7 @@ const BillingStatsChildOrg = memo(({ userdata, globalUrl, selectedOrganization,
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selectedOrganization.sync_features.app_executions.limit <= 10000) {
|
if (selectedOrganization.sync_features.app_executions.limit <= 10000 && editing === "app_executions") {
|
||||||
toast.error("Insufficient app execution limit to increase child org limit")
|
toast.error("Insufficient app execution limit to increase child org limit")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -2939,7 +2967,10 @@ const BillingStatsChildOrg = memo(({ userdata, globalUrl, selectedOrganization,
|
|||||||
|
|
||||||
const org = subOrgs[orgIndex]
|
const org = subOrgs[orgIndex]
|
||||||
|
|
||||||
org.sync_features[editing].limit = limit
|
if (editing !== "app_executions_hard_limit") {
|
||||||
|
org.sync_features[editing].limit = limit
|
||||||
|
}
|
||||||
|
|
||||||
org.sync_features.editing = true
|
org.sync_features.editing = true
|
||||||
const sync_features = org.sync_features
|
const sync_features = org.sync_features
|
||||||
const data = {
|
const data = {
|
||||||
@@ -2947,6 +2978,13 @@ const BillingStatsChildOrg = memo(({ userdata, globalUrl, selectedOrganization,
|
|||||||
sync_features: sync_features,
|
sync_features: sync_features,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (editing === "app_executions_hard_limit") {
|
||||||
|
data.editing = "app_runs_hard_limit";
|
||||||
|
data.billing = {
|
||||||
|
app_runs_hard_limit: limit || 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const url = `${globalUrl}/api/v1/orgs/${orgId}`;
|
const url = `${globalUrl}/api/v1/orgs/${orgId}`;
|
||||||
fetch(url, {
|
fetch(url, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
@@ -2974,6 +3012,12 @@ const BillingStatsChildOrg = memo(({ userdata, globalUrl, selectedOrganization,
|
|||||||
newRows[orgIndex].workflow_usage_limit = limit;
|
newRows[orgIndex].workflow_usage_limit = limit;
|
||||||
return newRows;
|
return newRows;
|
||||||
});
|
});
|
||||||
|
}else if (editing === "app_executions_hard_limit") {
|
||||||
|
setSubOrgStatsRows((prevRows) => {
|
||||||
|
const newRows = [...prevRows];
|
||||||
|
newRows[orgIndex].app_runs_hard_limit = limit;
|
||||||
|
return newRows;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -3087,10 +3131,21 @@ const IncreaseLimitPopUp = memo(({ open, onClose, limit, setLimit, HandleEditLim
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<DialogTitle style={{ fontSize: 24, fontWeight: "bold" }}>
|
{editing === "app_executions_hard_limit" ? (
|
||||||
|
<DialogTitle style={{ fontSize: 24, fontWeight: "bold" }}>
|
||||||
|
Add {editing.replaceAll("_", " ").replace(/\b\w/g, c => c.toUpperCase())}
|
||||||
|
</DialogTitle>
|
||||||
|
) : (
|
||||||
|
<DialogTitle style={{ fontSize: 24, fontWeight: "bold" }}>
|
||||||
Increase {editing.replaceAll("_", " ").replace(/\b\w/g, c => c.toUpperCase())} Limit
|
Increase {editing.replaceAll("_", " ").replace(/\b\w/g, c => c.toUpperCase())} Limit
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
|
)}
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
|
{ editing === "app_executions_hard_limit" ? (
|
||||||
|
<Typography style={{ marginRight: 20, marginBottom: 20, fontSize: 16, color: theme.palette.text.secondary }}>
|
||||||
|
Please note that once you set a hard limit for app runs workflows will not be able to run if the limit is reached. You will be notified by email when you reach the limit.
|
||||||
|
</Typography>
|
||||||
|
) : null}
|
||||||
<TextField
|
<TextField
|
||||||
value={currentLimit}
|
value={currentLimit}
|
||||||
onChange={(e) => setCurrentLimit(e.target.value)}
|
onChange={(e) => setCurrentLimit(e.target.value)}
|
||||||
|
|||||||
@@ -31,57 +31,10 @@ import {
|
|||||||
Box,
|
Box,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
|
|
||||||
import {
|
|
||||||
BarChart,
|
|
||||||
BarSeries,
|
|
||||||
Bar,
|
|
||||||
BarLabel,
|
|
||||||
|
|
||||||
GridlineSeries,
|
|
||||||
Gridline,
|
|
||||||
TooltipArea,
|
|
||||||
ChartTooltip,
|
|
||||||
TooltipTemplate,
|
|
||||||
} from 'reaviz';
|
|
||||||
|
|
||||||
import { typecost, typecost_single, } from "../views/HandlePaymentNew.jsx";
|
import { typecost, typecost_single, } from "../views/HandlePaymentNew.jsx";
|
||||||
|
import LineChartWrapper from '../components/LineChartWrapper.jsx';
|
||||||
import { Context } from '../context/ContextApi.jsx';
|
import { Context } from '../context/ContextApi.jsx';
|
||||||
|
|
||||||
const LineChartWrapper = ({keys, inputname, height, width}) => {
|
|
||||||
const [hovered, setHovered] = useState("");
|
|
||||||
const inputdata = keys.data === undefined ? keys : keys.data
|
|
||||||
const {themeMode} = useContext(Context)
|
|
||||||
const theme = getTheme(themeMode)
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div style={{color: "white", border: "1px solid rgba(255,255,255,0.3)", borderRadius: theme.palette?.borderRadius, padding: 30, marginTop: 15, backgroundColor: theme.palette.platformColor, overflow: "hidden", }}>
|
|
||||||
<Typography variant="h6" style={{marginBotton: 30, }}>
|
|
||||||
{inputname}
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<BarChart
|
|
||||||
style={{marginTop: 100, }}
|
|
||||||
width={"100%"}
|
|
||||||
height={height}
|
|
||||||
data={inputdata}
|
|
||||||
|
|
||||||
series={
|
|
||||||
<BarSeries
|
|
||||||
bar={
|
|
||||||
<Bar />
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
gridlines={
|
|
||||||
<GridlineSeries line={<Gridline direction="all" />} />
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const AppStats = (defaultprops) => {
|
const AppStats = (defaultprops) => {
|
||||||
const {
|
const {
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ import { getTheme } from "../theme.jsx";
|
|||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
import ReactJson from "react-json-view-ssr";
|
import ReactJson from "react-json-view-ssr";
|
||||||
import { GetIconInfo } from "../views/Workflows2.jsx";
|
import { GetIconInfo, } from "../views/Workflows2.jsx";
|
||||||
|
import { validateJson, handleReactJsonClipboard, } from "../views/Workflows.jsx";
|
||||||
import { red } from "../views/AngularWorkflow.jsx";
|
import { red } from "../views/AngularWorkflow.jsx";
|
||||||
import CollectIngestModal from "../components/CollectIngestModal.jsx";
|
import CollectIngestModal from "../components/CollectIngestModal.jsx";
|
||||||
import {
|
import {
|
||||||
@@ -76,8 +77,8 @@ import {
|
|||||||
SmartToy as SmartToyIcon,
|
SmartToy as SmartToyIcon,
|
||||||
Settings as SettingsIcon,
|
Settings as SettingsIcon,
|
||||||
FilterAlt as FilterAltIcon,
|
FilterAlt as FilterAltIcon,
|
||||||
|
CompareArrows as CompareArrowsIcon,
|
||||||
} from "@mui/icons-material";
|
} from "@mui/icons-material";
|
||||||
import { validateJson, } from "../views/Workflows.jsx";
|
|
||||||
import { Context } from "../context/ContextApi.jsx";
|
import { Context } from "../context/ContextApi.jsx";
|
||||||
|
|
||||||
const scrollStyle1 = {
|
const scrollStyle1 = {
|
||||||
@@ -130,7 +131,6 @@ const CacheView = memo((props) => {
|
|||||||
})
|
})
|
||||||
const [_, setUpdate] = useState(Math.random())
|
const [_, setUpdate] = useState(Math.random())
|
||||||
const [selectedRows, setSelectedRows] = useState([]);
|
const [selectedRows, setSelectedRows] = useState([]);
|
||||||
|
|
||||||
// Direct category migration from ../components/Files.jsx
|
// Direct category migration from ../components/Files.jsx
|
||||||
const [selectAllChecked, setSelectAllChecked] = React.useState(false)
|
const [selectAllChecked, setSelectAllChecked] = React.useState(false)
|
||||||
const [renderTextBox, setRenderTextBox] = React.useState(false);
|
const [renderTextBox, setRenderTextBox] = React.useState(false);
|
||||||
@@ -139,12 +139,14 @@ const CacheView = memo((props) => {
|
|||||||
const [selectedFileId, setSelectedFileId] = React.useState("");
|
const [selectedFileId, setSelectedFileId] = React.useState("");
|
||||||
const [updateToThisCategory, setUpdateToThisCategory] = useState("")
|
const [updateToThisCategory, setUpdateToThisCategory] = useState("")
|
||||||
const [workflows, setWorkflows] = useState([]);
|
const [workflows, setWorkflows] = useState([]);
|
||||||
|
const [apps, setApps] = useState([]);
|
||||||
|
|
||||||
const [selectedFiles, setSelectedFiles] = useState([]);
|
const [selectedFiles, setSelectedFiles] = useState([]);
|
||||||
const [showAutomationMenu, setShowAutomationMenu] = useState(false);
|
const [showAutomationMenu, setShowAutomationMenu] = useState(false);
|
||||||
const [showSettingsMenu, setShowSettingsMenu] = useState(false);
|
const [showSettingsMenu, setShowSettingsMenu] = useState(false);
|
||||||
const [showCollectIngestMenu, setShowCollectIngestMenu] = useState(false);
|
const [showCollectIngestMenu, setShowCollectIngestMenu] = useState(false);
|
||||||
|
|
||||||
|
var to_be_copied = "";
|
||||||
const defaultAutomation = [
|
const defaultAutomation = [
|
||||||
{
|
{
|
||||||
"name": "Run workflow",
|
"name": "Run workflow",
|
||||||
@@ -156,6 +158,42 @@ const CacheView = memo((props) => {
|
|||||||
"icon": <AirIcon />,
|
"icon": <AirIcon />,
|
||||||
"enabled": false,
|
"enabled": false,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "Correlate Categories",
|
||||||
|
"description": "",
|
||||||
|
"type": "singul",
|
||||||
|
"options": [{
|
||||||
|
"key": "datastore_categories",
|
||||||
|
"value": "",
|
||||||
|
}],
|
||||||
|
"icon": <CompareArrowsIcon />,
|
||||||
|
"enabled": false,
|
||||||
|
"disabled": false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Run AI Agent",
|
||||||
|
"description": "",
|
||||||
|
"options": [{
|
||||||
|
"key": "",
|
||||||
|
"value": "",
|
||||||
|
}],
|
||||||
|
"icon": <SmartToyIcon />,
|
||||||
|
"enabled": false,
|
||||||
|
"disabled": true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Send webhook",
|
||||||
|
"description": "Sends the updated value to a specified webhook URL.",
|
||||||
|
"options": [{
|
||||||
|
"key": "webhook_url",
|
||||||
|
"value": "",
|
||||||
|
}],
|
||||||
|
"icon": <WebhookIcon />,
|
||||||
|
"enabled": false,
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{
|
{
|
||||||
"name": "Send message",
|
"name": "Send message",
|
||||||
"description": "",
|
"description": "",
|
||||||
@@ -180,27 +218,6 @@ const CacheView = memo((props) => {
|
|||||||
"enabled": false,
|
"enabled": false,
|
||||||
"disabled": true,
|
"disabled": true,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "Run AI Agent",
|
|
||||||
"description": "",
|
|
||||||
"options": [{
|
|
||||||
"key": "",
|
|
||||||
"value": "",
|
|
||||||
}],
|
|
||||||
"icon": <SmartToyIcon />,
|
|
||||||
"enabled": false,
|
|
||||||
"disabled": true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Send webhook",
|
|
||||||
"description": "Sends the updated value to a specified webhook URL.",
|
|
||||||
"options": [{
|
|
||||||
"key": "webhook_url",
|
|
||||||
"value": "",
|
|
||||||
}],
|
|
||||||
"icon": <WebhookIcon />,
|
|
||||||
"enabled": false,
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
|
|
||||||
const [categoryAutomations, setCategoryAutomations] = useState(defaultAutomation)
|
const [categoryAutomations, setCategoryAutomations] = useState(defaultAutomation)
|
||||||
@@ -210,6 +227,35 @@ const CacheView = memo((props) => {
|
|||||||
const theme = getTheme(themeMode, brandColor);
|
const theme = getTheme(themeMode, brandColor);
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
|
|
||||||
|
const getApps = () => {
|
||||||
|
const url = `${globalUrl}/api/v1/apps`
|
||||||
|
fetch(url, {
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
Accept: "application/json",
|
||||||
|
},
|
||||||
|
credentials: "include",
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
if (response.status !== 200) {
|
||||||
|
console.log("Status not 200 for workflows :O!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then((responseJson) => {
|
||||||
|
if (responseJson?.success === false) {
|
||||||
|
toast.warn("Failed to load apps. Please try again or contact support@shuffler if this persists.")
|
||||||
|
} else {
|
||||||
|
setApps(responseJson)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
toast(error.toString());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const getWorkflows = () => {
|
const getWorkflows = () => {
|
||||||
const url = `${globalUrl}/api/v1/workflows`
|
const url = `${globalUrl}/api/v1/workflows`
|
||||||
@@ -251,6 +297,7 @@ const CacheView = memo((props) => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getWorkflows()
|
getWorkflows()
|
||||||
|
getApps()
|
||||||
listOrgCache(orgId, selectedCategory, 0, pageSize, page)
|
listOrgCache(orgId, selectedCategory, 0, pageSize, page)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
@@ -322,7 +369,7 @@ const CacheView = memo((props) => {
|
|||||||
.then((responseJson) => {
|
.then((responseJson) => {
|
||||||
setCachedLoaded(true);
|
setCachedLoaded(true);
|
||||||
if (responseJson?.success === true) {
|
if (responseJson?.success === true) {
|
||||||
setListCache(responseJson.keys)
|
setListCache(responseJson.keys);
|
||||||
|
|
||||||
if (responseJson.total_amount !== undefined && responseJson.total_amount !== null && responseJson.total_amount > 0) {
|
if (responseJson.total_amount !== undefined && responseJson.total_amount !== null && responseJson.total_amount > 0) {
|
||||||
setTotalAmount(responseJson.total_amount)
|
setTotalAmount(responseJson.total_amount)
|
||||||
@@ -563,34 +610,6 @@ const CacheView = memo((props) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleReactJsonClipboard = (copy) => {
|
|
||||||
const elementName = "copy_element_shuffle";
|
|
||||||
let copyText = document.getElementById(elementName);
|
|
||||||
|
|
||||||
if (copyText) {
|
|
||||||
if (copy.namespace && copy.name && copy.src) {
|
|
||||||
copy = copy.src;
|
|
||||||
}
|
|
||||||
|
|
||||||
const clipboard = navigator.clipboard;
|
|
||||||
if (!clipboard) {
|
|
||||||
toast("Can only copy over HTTPS (port 3443)");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let stringified = JSON.stringify(copy);
|
|
||||||
if (stringified.startsWith('"') && stringified.endsWith('"')) {
|
|
||||||
stringified = stringified.slice(1, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
navigator.clipboard.writeText(stringified);
|
|
||||||
toast("Copied value to clipboard, NOT json path.");
|
|
||||||
} else {
|
|
||||||
console.log("Failed to copy from " + elementName + ": ", copyText);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
const timestamp = (timestamp) => {
|
const timestamp = (timestamp) => {
|
||||||
if (timestamp === undefined || timestamp === null || timestamp === "") {
|
if (timestamp === undefined || timestamp === null || timestamp === "") {
|
||||||
return null
|
return null
|
||||||
@@ -1096,7 +1115,153 @@ const CacheView = memo((props) => {
|
|||||||
|
|
||||||
{showOptions && (
|
{showOptions && (
|
||||||
updatedAutomation.options.map((option, optionIndex) => {
|
updatedAutomation.options.map((option, optionIndex) => {
|
||||||
if (option?.key === "workflow_id") {
|
if (option?.key === "datastore_categories") {
|
||||||
|
if (datastoreCategories === undefined || datastoreCategories === null || datastoreCategories.length <= 1) {
|
||||||
|
return (
|
||||||
|
<Typography key={optionIndex} style={{ color: theme.palette.text.secondary, marginTop: 10 }}>
|
||||||
|
No categories available. Please add categories in the settings.
|
||||||
|
</Typography>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Autocomplete
|
||||||
|
key={optionIndex}
|
||||||
|
|
||||||
|
multiple
|
||||||
|
label="Choose Datastore Categories"
|
||||||
|
id="datastore_category_search"
|
||||||
|
autoHighlight
|
||||||
|
freeSolo
|
||||||
|
value={datastoreCategories?.filter(c => option?.value.includes(c)) || []}
|
||||||
|
classes={{ inputRoot: classes.inputRoot }}
|
||||||
|
ListboxProps={{
|
||||||
|
style: {
|
||||||
|
backgroundColor: theme.palette.surfaceColor,
|
||||||
|
color: theme.palette.text.primary,
|
||||||
|
borderRadius: theme.palette.borderRadius,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
onChange={(event, newValue) => {
|
||||||
|
console.log("New Value: ", newValue)
|
||||||
|
|
||||||
|
option.value = ""
|
||||||
|
for (var i = 0; i < newValue.length; i++) {
|
||||||
|
option.value += newValue[i] + ","
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newValue.length > 0) {
|
||||||
|
updatedAutomation.enabled = true
|
||||||
|
} else {
|
||||||
|
updatedAutomation.enabled = false
|
||||||
|
}
|
||||||
|
|
||||||
|
updatedAutomation.options[optionIndex] = option
|
||||||
|
setUpdatedAutomation(updatedAutomation)
|
||||||
|
setUpdated(true)
|
||||||
|
|
||||||
|
setUpdate(Math.random()) // Force re-render
|
||||||
|
}}
|
||||||
|
|
||||||
|
getOptionLabel={(option) => {
|
||||||
|
if (option === undefined || option === null) {
|
||||||
|
return "No Categories Selected";
|
||||||
|
}
|
||||||
|
|
||||||
|
return option
|
||||||
|
}}
|
||||||
|
options={datastoreCategories}
|
||||||
|
fullWidth
|
||||||
|
style={{
|
||||||
|
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
|
||||||
|
borderRadius: theme.palette.textFieldStyle.borderRadius,
|
||||||
|
color: theme.palette.textFieldStyle.color,
|
||||||
|
height: 35,
|
||||||
|
marginBottom: 40,
|
||||||
|
}}
|
||||||
|
renderOption={(props, data, state) => {
|
||||||
|
const fixedname = data?.charAt(0)?.toUpperCase() + data?.slice(1)?.replaceAll("_", " ")
|
||||||
|
const iconDetails = GetIconInfo({
|
||||||
|
"app_name": fixedname,
|
||||||
|
"name": fixedname,
|
||||||
|
})
|
||||||
|
|
||||||
|
const keyfound = option?.value.includes(data)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tooltip arrow placement="left" title={
|
||||||
|
<span style={{}}>
|
||||||
|
{data.image !== undefined && data.image !== null && data.image.length > 0 ?
|
||||||
|
<img src={data.image} alt={data.name} style={{ backgroundColor: theme.palette.surfaceColor, maxHeight: 200, minHeigth: 200, borderRadius: theme.palette?.borderRadius, }} />
|
||||||
|
: null}
|
||||||
|
<Typography>
|
||||||
|
Choose {data}
|
||||||
|
</Typography>
|
||||||
|
</span>
|
||||||
|
} >
|
||||||
|
<MenuItem
|
||||||
|
{...props}
|
||||||
|
style={{
|
||||||
|
backgroundColor: theme.palette.surfaceColor,
|
||||||
|
}}
|
||||||
|
value={data}
|
||||||
|
>
|
||||||
|
<Typography style={{
|
||||||
|
display: "flex",
|
||||||
|
marginTop: 5,
|
||||||
|
color: keyfound ? red : theme.palette.text.primary,
|
||||||
|
}}>
|
||||||
|
<div style={{marginRight: 10, }}>
|
||||||
|
{iconDetails?.originalIcon && (
|
||||||
|
iconDetails?.originalIcon
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{fixedname}
|
||||||
|
</Typography>
|
||||||
|
</MenuItem>
|
||||||
|
</Tooltip>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
renderInput={(params) => {
|
||||||
|
return (
|
||||||
|
<TextField
|
||||||
|
{...params}
|
||||||
|
style={{
|
||||||
|
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
|
||||||
|
color: theme.palette.textFieldStyle.color,
|
||||||
|
borderRadius: theme.palette.textFieldStyle.borderRadius,
|
||||||
|
height: 35,
|
||||||
|
fontSize: 16,
|
||||||
|
marginTop: "16px"
|
||||||
|
}}
|
||||||
|
InputProps={{
|
||||||
|
...params.InputProps,
|
||||||
|
style: {
|
||||||
|
height: 35,
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
padding: "0px 8px",
|
||||||
|
fontSize: 16,
|
||||||
|
borderRadius: 4,
|
||||||
|
},
|
||||||
|
inputProps: {
|
||||||
|
...params.inputProps,
|
||||||
|
style: {
|
||||||
|
height: "100%",
|
||||||
|
boxSizing: "border-box",
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
variant="outlined"
|
||||||
|
placeholder="Select Categories to Correlate"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
} else if (option?.key === "workflow_id") {
|
||||||
return (
|
return (
|
||||||
<Autocomplete
|
<Autocomplete
|
||||||
key={optionIndex}
|
key={optionIndex}
|
||||||
@@ -1181,8 +1346,7 @@ const CacheView = memo((props) => {
|
|||||||
{...props}
|
{...props}
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: theme.palette.surfaceColor,
|
backgroundColor: theme.palette.surfaceColor,
|
||||||
color: data.id === option?.value ? "red" : theme.palette.text.primary,
|
color: data.id === option?.value ? red : theme.palette.text.primary,
|
||||||
borderBottom: data.id === "parent" ? "2px solid rgba(255,255,255,0.5)" : null
|
|
||||||
}}
|
}}
|
||||||
value={data}
|
value={data}
|
||||||
>
|
>
|
||||||
@@ -1327,16 +1491,11 @@ const CacheView = memo((props) => {
|
|||||||
}}
|
}}
|
||||||
collapsed={true}
|
collapsed={true}
|
||||||
enableClipboard={(copy) => {
|
enableClipboard={(copy) => {
|
||||||
// handleReactJsonClipboard(copy);
|
handleReactJsonClipboard(copy)
|
||||||
}}
|
}}
|
||||||
collapseStringsAfterLength={theme.palette.jsonCollapseStringsAfterLength}
|
collapseStringsAfterLength={theme.palette.jsonCollapseStringsAfterLength}
|
||||||
iconStyle={theme.palette.jsonIconStyle}
|
iconStyle={theme.palette.jsonIconStyle}
|
||||||
displayDataTypes={false}
|
displayDataTypes={false}
|
||||||
onSelect={(select) => {
|
|
||||||
|
|
||||||
// HandleJsonCopy(showResult, select, data.action.label);
|
|
||||||
console.log("SELECTED!: ", select);
|
|
||||||
}}
|
|
||||||
name={null}
|
name={null}
|
||||||
/>
|
/>
|
||||||
:
|
:
|
||||||
@@ -1419,7 +1578,9 @@ const CacheView = memo((props) => {
|
|||||||
<IconButton
|
<IconButton
|
||||||
style={{ padding: "6px" }}
|
style={{ padding: "6px" }}
|
||||||
disabled={data.org_id !== selectedOrganization.id ? true : false}
|
disabled={data.org_id !== selectedOrganization.id ? true : false}
|
||||||
onClick={() => {
|
onClick={(e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
// Try to make the value JSON indented
|
// Try to make the value JSON indented
|
||||||
const valid = validateJson(data.value)
|
const valid = validateJson(data.value)
|
||||||
var newvalue = data.value
|
var newvalue = data.value
|
||||||
@@ -1469,7 +1630,9 @@ const CacheView = memo((props) => {
|
|||||||
<IconButton
|
<IconButton
|
||||||
style={{ padding: "6px" }}
|
style={{ padding: "6px" }}
|
||||||
disabled={data.public_authorization === undefined || data.public_authorization === null || data.public_authorization === "" || data.org_id !== selectedOrganization.id ? true : false}
|
disabled={data.public_authorization === undefined || data.public_authorization === null || data.public_authorization === "" || data.org_id !== selectedOrganization.id ? true : false}
|
||||||
onClick={() => {
|
onClick={(e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
window.open(`${globalUrl}/api/v1/orgs/${orgId}/cache/${data.key}?type=text&authorization=${data.public_authorization}`, "_blank");
|
window.open(`${globalUrl}/api/v1/orgs/${orgId}/cache/${data.key}?type=text&authorization=${data.public_authorization}`, "_blank");
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -1486,7 +1649,9 @@ const CacheView = memo((props) => {
|
|||||||
<IconButton
|
<IconButton
|
||||||
style={{ padding: "6px" }}
|
style={{ padding: "6px" }}
|
||||||
disabled={selectedOrganization?.id === undefined ? false : data.org_id !== selectedOrganization.id ? true : false}
|
disabled={selectedOrganization?.id === undefined ? false : data.org_id !== selectedOrganization.id ? true : false}
|
||||||
onClick={() => {
|
onClick={(e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
deleteEntry(orgId, data.key, data.category)
|
deleteEntry(orgId, data.key, data.category)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -1609,6 +1774,8 @@ const CacheView = memo((props) => {
|
|||||||
|
|
||||||
workflows={workflows}
|
workflows={workflows}
|
||||||
getWorkflows={getWorkflows}
|
getWorkflows={getWorkflows}
|
||||||
|
|
||||||
|
apps={apps}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{cacheDistributionModal}
|
{cacheDistributionModal}
|
||||||
@@ -2067,12 +2234,14 @@ const CacheView = memo((props) => {
|
|||||||
<DataGrid
|
<DataGrid
|
||||||
rows={listCache}
|
rows={listCache}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
|
|
||||||
checkboxSelection
|
checkboxSelection
|
||||||
disableRowSelectionOnClick
|
disableRowSelectionOnClick
|
||||||
rowSelectionModel={selectedRows}
|
rowSelectionModel={selectedRows}
|
||||||
|
onSelectionModelChange={(newSelection) => {
|
||||||
|
setSelectedRows(newSelection);
|
||||||
|
}}
|
||||||
onRowSelectionModelChange={(newSelection) => {
|
onRowSelectionModelChange={(newSelection) => {
|
||||||
setSelectedRows(newSelection)
|
setSelectedRows(newSelection);
|
||||||
}}
|
}}
|
||||||
keepNonExistentRowsSelected={false}
|
keepNonExistentRowsSelected={false}
|
||||||
getRowId={(row) => row.key}
|
getRowId={(row) => row.key}
|
||||||
@@ -2245,6 +2414,11 @@ const CacheView = memo((props) => {
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<TextField
|
||||||
|
id="copy_element_shuffle"
|
||||||
|
value={to_be_copied}
|
||||||
|
style={{ display: "none" }}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -10,10 +10,14 @@ import {
|
|||||||
DialogTitle,
|
DialogTitle,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
Typography,
|
Typography,
|
||||||
|
IconButton,
|
||||||
Paper,
|
Paper,
|
||||||
LinearProgress,
|
LinearProgress,
|
||||||
Grid,
|
Grid,
|
||||||
Button,
|
Button,
|
||||||
|
Tooltip,
|
||||||
|
Autocomplete,
|
||||||
|
TextField,
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -22,7 +26,7 @@ import {
|
|||||||
} from '@mui/icons-material';
|
} from '@mui/icons-material';
|
||||||
|
|
||||||
const CollectIngestModal = (props) => {
|
const CollectIngestModal = (props) => {
|
||||||
const { globalUrl, open, setOpen } = props;
|
const { globalUrl, open, setOpen, workflows, getWorkflows, apps, } = props;
|
||||||
|
|
||||||
const { themeMode, brandColor } = useContext(Context);
|
const { themeMode, brandColor } = useContext(Context);
|
||||||
const theme = getTheme(themeMode, brandColor);
|
const theme = getTheme(themeMode, brandColor);
|
||||||
@@ -37,12 +41,17 @@ const CollectIngestModal = (props) => {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
const startIngestion = (appname, index) => {
|
const startIngestion = (bundleName, appnames, category, index) => {
|
||||||
console.log("APPNAME:", appname, "INDEX:", index)
|
|
||||||
|
|
||||||
const body = {
|
var body = {
|
||||||
"app_name": appname,
|
"label": bundleName,
|
||||||
"label": appname,
|
"app_name": appnames,
|
||||||
|
|
||||||
|
"category": "",
|
||||||
|
}
|
||||||
|
|
||||||
|
if (category !== undefined && category !== null) {
|
||||||
|
body.category = category
|
||||||
}
|
}
|
||||||
|
|
||||||
const url = `${globalUrl}/api/v2/workflows/generate`
|
const url = `${globalUrl}/api/v2/workflows/generate`
|
||||||
@@ -61,21 +70,27 @@ const CollectIngestModal = (props) => {
|
|||||||
return response.json();
|
return response.json();
|
||||||
})
|
})
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
|
|
||||||
|
if (getWorkflows !== undefined) {
|
||||||
|
getWorkflows()
|
||||||
|
}
|
||||||
|
|
||||||
console.log("Ingestion started successfully:", data);
|
console.log("Ingestion started successfully:", data);
|
||||||
toast.success(`Ingestion for ${appname} started successfully!`);
|
toast.success(`Ingestion for ${bundleName} started successfully!`);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.error("Error starting ingestion:", error);
|
console.error("Error starting ingestion:", error);
|
||||||
toast.error(`Failed to start ingestion for ${appname}. Please try again.`);
|
toast.error(`Failed to start ingestion for ${bundleName}. Please try again or contact support@shuffler.io if this persists.`);
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const IngestItem = (props) => {
|
const IngestItem = (props) => {
|
||||||
const { type, index } = props
|
const { type, appCategory, index } = props
|
||||||
|
|
||||||
const [hovering, setHovering] = useState(false);
|
const [hovering, setHovering] = useState(false);
|
||||||
const [isFinished, setIsFinished] = useState(false);
|
const [selectedApps, setSelectedApps] = useState([]);
|
||||||
|
//const [isFinished, setIsFinished] = useState(false);
|
||||||
|
|
||||||
const appname = type
|
const appname = type
|
||||||
const ingestedAmount = 20
|
const ingestedAmount = 20
|
||||||
@@ -85,24 +100,93 @@ const CollectIngestModal = (props) => {
|
|||||||
"name": appname,
|
"name": appname,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
var foundMatchingWorkflow = null
|
||||||
|
if (workflows !== undefined && workflows !== null && workflows.length > 0) {
|
||||||
|
const parsedName = type.toLowerCase().replaceAll(" ", "_");
|
||||||
|
const foundWorkflow = workflows.find((workflow) => {
|
||||||
|
return workflow?.name?.toLowerCase().replaceAll(" ", "_") === parsedName
|
||||||
|
})
|
||||||
|
|
||||||
|
if (foundWorkflow !== undefined && foundWorkflow !== null) {
|
||||||
|
foundMatchingWorkflow = foundWorkflow
|
||||||
|
|
||||||
|
|
||||||
|
// Find relevant apps and maps them
|
||||||
|
if (apps.length > 0 && selectedApps.length === 0 && foundWorkflow?.actions !== undefined && foundWorkflow?.actions !== null && foundWorkflow?.actions.length > 0) {
|
||||||
|
var newSelectedApps = []
|
||||||
|
for (var actionkey in foundWorkflow.actions) {
|
||||||
|
const action = foundWorkflow.actions[actionkey]
|
||||||
|
|
||||||
|
if (action?.app_name !== "Singul" && action?.app_id !== "integration") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var paramkey in action.parameters) {
|
||||||
|
const param = action.parameters[paramkey]
|
||||||
|
if (!((param.name === "app_name" || param.name === "appName") && param.value !== undefined && param.value !== null && param.value.length > 0)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the app in available apps
|
||||||
|
const parsedname = param.value.replaceAll(" ", "_").toLowerCase()
|
||||||
|
for (var appkey in apps) {
|
||||||
|
const appname = apps[appkey].name.replaceAll(" ", "_").toLowerCase()
|
||||||
|
if (appname === parsedname) {
|
||||||
|
newSelectedApps.push(apps[appkey])
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newSelectedApps.length > 0) {
|
||||||
|
setSelectedApps(newSelectedApps)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var matchingapps = []
|
||||||
|
if (appCategory !== undefined && appCategory !== null && appCategory.length > 0 && apps !== undefined && apps !== null && apps.length > 0) {
|
||||||
|
for (var appkey in apps) {
|
||||||
|
const app = apps[appkey]
|
||||||
|
|
||||||
|
for (var categorykey in app.categories) {
|
||||||
|
const category = app.categories[categorykey]
|
||||||
|
if (category.toLowerCase() === appCategory.toLowerCase()) {
|
||||||
|
matchingapps.push(app)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
//<Grid item xs={hovering ? 12 : 5.9}
|
//<Grid item xs={hovering ? 12 : 5.9}
|
||||||
<Grid item xs={12}
|
<Grid item xs={12}
|
||||||
style={{
|
style={{
|
||||||
minHeight: hovering ? 225 : 145,
|
minHeight: hovering ? 250 : 140,
|
||||||
maxHeight: hovering ? 225 : 145,
|
maxHeight: hovering ? "auto" : 140,
|
||||||
cursor: "pointer",
|
cursor: "pointer",
|
||||||
position: "relative",
|
position: "relative",
|
||||||
transition: "all 0.3s ease-in-out",
|
transition: "all 0.3s ease-in-out",
|
||||||
|
|
||||||
borderRadius: theme.palette.borderRadius,
|
borderRadius: theme.palette.borderRadius,
|
||||||
border: hovering ? `2px solid ${theme.palette.primary.main}` : isFinished ? `2px solid ${theme.palette.success.main}` : `2px solid ${theme.palette.secondary.main}`,
|
border: hovering ? `2px solid ${theme.palette.primary.main}` : foundMatchingWorkflow !== null ? `2px solid ${theme.palette.success.main}` : `2px solid ${theme.palette.secondary.main}`,
|
||||||
textAlign: "center",
|
textAlign: "center",
|
||||||
marginBottom: 5,
|
marginBottom: 5,
|
||||||
|
|
||||||
overflow: "hidden",
|
overflow: "hidden",
|
||||||
}}
|
}}
|
||||||
onMouseEnter={() => setHovering(true)}
|
onMouseEnter={() => {
|
||||||
|
|
||||||
|
//if (foundMatchingWorkflow !== null) {
|
||||||
|
//} else {
|
||||||
|
setHovering(true)
|
||||||
|
//}
|
||||||
|
}}
|
||||||
onMouseLeave={() => setHovering(false)}
|
onMouseLeave={() => setHovering(false)}
|
||||||
>
|
>
|
||||||
<div style={{marginTop: 35, marginBottom: 35, }}>
|
<div style={{marginTop: 35, marginBottom: 35, }}>
|
||||||
@@ -116,18 +200,92 @@ const CollectIngestModal = (props) => {
|
|||||||
</Typography>
|
</Typography>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button variant="contained" onClick={() => {
|
<div style={{display: "flex", width: 400, margin: "auto", }}>
|
||||||
toast.info("Starting ingest for relevant apps")
|
{matchingapps.length > 0 ?
|
||||||
startIngestion(appname, index)
|
<Autocomplete
|
||||||
}}>
|
style={{flex: 1, }}
|
||||||
Start Ingestion
|
multiple
|
||||||
</Button>
|
filterSelectedOptions
|
||||||
|
options={matchingapps}
|
||||||
|
|
||||||
|
value={selectedApps}
|
||||||
|
onChange={(event, value) => {
|
||||||
|
setSelectedApps(value)
|
||||||
|
}}
|
||||||
|
|
||||||
|
getOptionLabel={(option) => {
|
||||||
|
const parsedname = option.name.replaceAll("_", " ")
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<img src={option?.large_image} alt={option.name} style={{ width: 24, height: 24, marginRight: 10, borderRadius: 5, }} />
|
||||||
|
<Typography variant="body1" style={{ display: "inline-block", verticalAlign: "middle", marginTop: -12, }}>
|
||||||
|
{parsedname}
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
renderInput={(params) => {
|
||||||
|
return (
|
||||||
|
<TextField
|
||||||
|
{...params}
|
||||||
|
variant="outlined"
|
||||||
|
label="Select apps"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
: null}
|
||||||
|
|
||||||
|
|
||||||
|
<Button
|
||||||
|
style={{flex: 1, }}
|
||||||
|
variant={foundMatchingWorkflow !== null ? "outlined" : "contained"}
|
||||||
|
onClick={() => {
|
||||||
|
|
||||||
|
//if (foundMatchingWorkflow !== null) {
|
||||||
|
// toast.error("Deletion not implemented for this POC. Please delete the workflow.")
|
||||||
|
//}
|
||||||
|
|
||||||
|
//else {
|
||||||
|
toast.info("Starting ingest for relevant apps")
|
||||||
|
var newapps = ""
|
||||||
|
for (var key in selectedApps) {
|
||||||
|
const app = selectedApps[key]
|
||||||
|
|
||||||
|
if (newapps.length > 0) {
|
||||||
|
newapps += ","
|
||||||
|
}
|
||||||
|
|
||||||
|
newapps += app.name
|
||||||
|
}
|
||||||
|
|
||||||
|
startIngestion(appname, newapps, appCategory, index)
|
||||||
|
//}
|
||||||
|
}}>
|
||||||
|
{foundMatchingWorkflow !== null ?
|
||||||
|
"Re-Create Ingestion"
|
||||||
|
:
|
||||||
|
"Start Ingestion"
|
||||||
|
}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{foundMatchingWorkflow !== null ?
|
||||||
|
<a href={`/workflows/${foundMatchingWorkflow.id}`} target="_blank" rel="noopener noreferrer">
|
||||||
|
<Tooltip title="View Workflow" placement="right">
|
||||||
|
<IconButton style={{position: "absolute", top: 10, right: 10, marginLeft: 15, }}>
|
||||||
|
<RocketIcon style={{ }} />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
</a>
|
||||||
|
: null}
|
||||||
{hovering ?
|
{hovering ?
|
||||||
<div>
|
<div>
|
||||||
</div>
|
</div>
|
||||||
: null}
|
: null}
|
||||||
|
|
||||||
{isFinished ?
|
{foundMatchingWorkflow !== null ?
|
||||||
<div>
|
<div>
|
||||||
<Typography variant="body1" style={{
|
<Typography variant="body1" style={{
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
@@ -137,6 +295,8 @@ const CollectIngestModal = (props) => {
|
|||||||
}}>
|
}}>
|
||||||
{ingestedAmount} / X
|
{ingestedAmount} / X
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
|
{/*
|
||||||
<LinearProgress
|
<LinearProgress
|
||||||
style={{
|
style={{
|
||||||
width: "100%",
|
width: "100%",
|
||||||
@@ -146,6 +306,7 @@ const CollectIngestModal = (props) => {
|
|||||||
variant="determinate"
|
variant="determinate"
|
||||||
fullWidth value={{ingestedAmount}}
|
fullWidth value={{ingestedAmount}}
|
||||||
/>
|
/>
|
||||||
|
*/}
|
||||||
</div>
|
</div>
|
||||||
: null}
|
: null}
|
||||||
</Grid>
|
</Grid>
|
||||||
@@ -189,13 +350,13 @@ const CollectIngestModal = (props) => {
|
|||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<Grid container>
|
<Grid container>
|
||||||
<IngestItem type="Ingest Tickets" index={1} />
|
<IngestItem type="Ingest Tickets" appCategory={"cases"} index={1} />
|
||||||
<IngestItem type="Enable Threat feeds" index={2} />
|
<IngestItem type="Enable Threat feeds" index={2} />
|
||||||
<IngestItem type="Track Assets" index={2} />
|
|
||||||
<IngestItem type="Enable Search" index={2} />
|
<IngestItem type="Enable Search" index={2} />
|
||||||
<IngestItem type="Enable Mitre Att&ck techniques" index={2} />
|
<IngestItem type="Enable Mitre Att&ck techniques" index={2} />
|
||||||
<IngestItem type="Enable Detection Rules" index={2} />
|
<IngestItem type="Enable Detection Rules" index={2} />
|
||||||
<IngestItem type="Ingest Logs" index={2} />
|
<IngestItem type="Ingest Logs" index={2} />
|
||||||
|
<IngestItem type="Track Assets" appCategory={"assets"} index={2} />
|
||||||
</Grid>
|
</Grid>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Bar } from 'react-chartjs-2';
|
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
|
|
||||||
|
import LineChartWrapper from '../components/LineChartWrapper.jsx';
|
||||||
|
|
||||||
export const LoadStats = (globalUrl, cachekey) => {
|
export const LoadStats = (globalUrl, cachekey) => {
|
||||||
if (globalUrl === undefined) {
|
if (globalUrl === undefined) {
|
||||||
console.log("Error: Global URL is undefined")
|
console.log("Error: Global URL is undefined")
|
||||||
@@ -64,84 +65,13 @@ export const LoadStats = (globalUrl, cachekey) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This wrapper is a waste lol
|
||||||
const DashboardBarchart = (props) => {
|
const DashboardBarchart = (props) => {
|
||||||
// this is clearly unfinished and not worth my time.
|
const { timelineData, title, height, } = props;
|
||||||
// refer to health page to see how i made it work there w/o using chartjs
|
|
||||||
|
|
||||||
// const { timelineData, title, height, } = props;
|
return (
|
||||||
// var inputHeight = 15
|
<LineChartWrapper keys={timelineData} height={150} width={"100%"} border={false} />
|
||||||
// if (height !== undefined && height !== null) {
|
)
|
||||||
// inputHeight = height
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const barOptions = {
|
|
||||||
// plugins: {
|
|
||||||
// tooltip: {
|
|
||||||
// enabled: true, // Ensure tooltips are enabled
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// tooltips: {
|
|
||||||
// mode: 'index',
|
|
||||||
// intersect: false,
|
|
||||||
// },
|
|
||||||
// legend: {
|
|
||||||
// display: false
|
|
||||||
// },
|
|
||||||
// layout: {
|
|
||||||
// padding: {
|
|
||||||
// top: 0, // Adjust the top padding as needed
|
|
||||||
// bottom: -10, // Adjust the bottom padding as needed
|
|
||||||
// left: 0, // Adjust the left padding as needed
|
|
||||||
// right: 0, // Adjust the right padding as needed
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// scales: {
|
|
||||||
// y: {
|
|
||||||
// beginAtZero: false,
|
|
||||||
// },
|
|
||||||
// yAxes: [{
|
|
||||||
// ticks: {
|
|
||||||
// display: false
|
|
||||||
// },
|
|
||||||
// beginAtZero: false,
|
|
||||||
// }],
|
|
||||||
// xAxes: [{
|
|
||||||
// ticks: {
|
|
||||||
// display: false
|
|
||||||
// },
|
|
||||||
// beginAtZero: false,
|
|
||||||
// }]
|
|
||||||
// },
|
|
||||||
// tooltips: {
|
|
||||||
// callbacks: {
|
|
||||||
// label: function (tooltipItem, data) {
|
|
||||||
// const label = data.labels[tooltipItem.index]
|
|
||||||
// return label.split('\n')[0]
|
|
||||||
// },
|
|
||||||
// afterLabel: function (tooltipItem, data) {
|
|
||||||
// const amount = tooltipItem.value === undefined || tooltipItem.value === null ? 0 : tooltipItem.value
|
|
||||||
// return `Amount: ${amount}`
|
|
||||||
// },
|
|
||||||
// title: function () {
|
|
||||||
// return title === undefined ? '' : title
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return (
|
|
||||||
// <Bar
|
|
||||||
// data={timelineData}
|
|
||||||
// options={barOptions}
|
|
||||||
// height={inputHeight}
|
|
||||||
// getElementAtEvent={(elements) => {
|
|
||||||
// if (elements && elements.length > 0) {
|
|
||||||
// //toast("Click event")
|
|
||||||
// console.log("Clicked: ", elements)
|
|
||||||
// }
|
|
||||||
// }}
|
|
||||||
// />
|
|
||||||
// )
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default DashboardBarchart;
|
export default DashboardBarchart;
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ const EditOrgTab = (props) => {
|
|||||||
handleGetOrg,
|
handleGetOrg,
|
||||||
selectedStatus, setSelectedStatus,
|
selectedStatus, setSelectedStatus,
|
||||||
handleEditOrg,
|
handleEditOrg,
|
||||||
|
handleStatusChange
|
||||||
} = props;
|
} = props;
|
||||||
const [organizationFeatures, setOrganizationFeatures] = React.useState({});
|
const [organizationFeatures, setOrganizationFeatures] = React.useState({});
|
||||||
const [users, setUsers] = React.useState([]);
|
const [users, setUsers] = React.useState([]);
|
||||||
@@ -39,43 +40,6 @@ const EditOrgTab = (props) => {
|
|||||||
const { themeMode, brandColor } = useContext(Context);
|
const { themeMode, brandColor } = useContext(Context);
|
||||||
const theme = getTheme(themeMode, brandColor);
|
const theme = getTheme(themeMode, brandColor);
|
||||||
|
|
||||||
const handleStatusChange = (event) => {
|
|
||||||
const { value } = event.target;
|
|
||||||
setSelectedStatus(value);
|
|
||||||
|
|
||||||
handleEditOrg(
|
|
||||||
selectedOrganization?.name,
|
|
||||||
selectedOrganization?.description,
|
|
||||||
selectedOrganization.id,
|
|
||||||
selectedOrganization.image,
|
|
||||||
{
|
|
||||||
app_download_repo: selectedOrganization?.defaults?.app_download_repo,
|
|
||||||
app_download_branch: selectedOrganization?.defaults?.app_download_branch,
|
|
||||||
workflow_download_repo: selectedOrganization?.defaults?.workflow_download_repo,
|
|
||||||
workflow_download_branch: selectedOrganization?.defaults?.workflow_download_branch,
|
|
||||||
notification_workflow: selectedOrganization?.defaults?.notification_workflow,
|
|
||||||
documentation_reference: selectedOrganization?.defaults?.documentation_reference,
|
|
||||||
workflow_upload_repo: selectedOrganization?.defaults?.workflow_upload_repo,
|
|
||||||
workflow_upload_branch: selectedOrganization?.defaults?.workflow_upload_branch,
|
|
||||||
workflow_upload_username: selectedOrganization?.defaults?.workflow_upload_username,
|
|
||||||
workflow_upload_token: selectedOrganization?.defaults?.workflow_upload_token,
|
|
||||||
newsletter: selectedOrganization?.defaults?.newsletter,
|
|
||||||
weekly_recommendations: selectedOrganization?.defaults?.weekly_recommendations,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
sso_entrypoint: selectedOrganization?.sso_config?.sso_entrypoint,
|
|
||||||
sso_certificate: selectedOrganization?.sso_config?.sso_certificate,
|
|
||||||
client_id: selectedOrganization?.sso_config?.client_id,
|
|
||||||
client_secret: selectedOrganization?.sso_config?.client_secret,
|
|
||||||
openid_authorization: selectedOrganization?.sso_config?.openid_authorization,
|
|
||||||
openid_token: selectedOrganization?.sso_config?.openid_token,
|
|
||||||
SSORequired: selectedOrganization?.sso_config?.SSORequired,
|
|
||||||
auto_provision: selectedOrganization?.sso_config?.auto_provision,
|
|
||||||
},
|
|
||||||
value.length === 0 ? ["none"] : value,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
const getUsers = () => {
|
const getUsers = () => {
|
||||||
fetch(globalUrl + "/api/v1/getusers", {
|
fetch(globalUrl + "/api/v1/getusers", {
|
||||||
@@ -443,6 +407,7 @@ If you're interested, please let me know a time that works for you, or set up a
|
|||||||
isEditOrgTab={true}
|
isEditOrgTab={true}
|
||||||
handleGetOrg={handleGetOrg}
|
handleGetOrg={handleGetOrg}
|
||||||
serverside={serverside}
|
serverside={serverside}
|
||||||
|
handleStatusChange={handleStatusChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div >
|
</div >
|
||||||
|
|||||||
+514
-527
File diff suppressed because it is too large
Load Diff
@@ -49,7 +49,7 @@ const menuData = {
|
|||||||
{
|
{
|
||||||
title: "Shuffle",
|
title: "Shuffle",
|
||||||
description:
|
description:
|
||||||
"The most versatile automation engine with focus on security.",
|
"The most versatile automation engine. Focused on cybersecurity.",
|
||||||
icon: "/images/icons/shuffleLogo.svg",
|
icon: "/images/icons/shuffleLogo.svg",
|
||||||
path: "/docs/about",
|
path: "/docs/about",
|
||||||
gaData: {
|
gaData: {
|
||||||
@@ -61,7 +61,7 @@ const menuData = {
|
|||||||
{
|
{
|
||||||
title: "Singul",
|
title: "Singul",
|
||||||
description:
|
description:
|
||||||
"Connect your favorite services with a singul line of code.",
|
"Connect to your favorite services with a singul line of code.",
|
||||||
icon: "/images/logos/singul.svg",
|
icon: "/images/logos/singul.svg",
|
||||||
path: "https://singul.io",
|
path: "https://singul.io",
|
||||||
gaData: {
|
gaData: {
|
||||||
@@ -70,6 +70,7 @@ const menuData = {
|
|||||||
label: "singul_click"
|
label: "singul_click"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
/*
|
||||||
{
|
{
|
||||||
title: "API Explorer",
|
title: "API Explorer",
|
||||||
description:
|
description:
|
||||||
@@ -82,6 +83,7 @@ const menuData = {
|
|||||||
label: "api_explorer_click"
|
label: "api_explorer_click"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
*/
|
||||||
],
|
],
|
||||||
Services: [
|
Services: [
|
||||||
{
|
{
|
||||||
@@ -1245,23 +1247,6 @@ const Navbar = (props) => {
|
|||||||
>
|
>
|
||||||
{menuItem.title}
|
{menuItem.title}
|
||||||
</Typography>
|
</Typography>
|
||||||
{menuItem.title === "Singul" && (
|
|
||||||
<Typography
|
|
||||||
sx={{
|
|
||||||
fontSize: '10px',
|
|
||||||
color: '#FF8544',
|
|
||||||
border: '1px solid #FF8544',
|
|
||||||
borderRadius: '4px',
|
|
||||||
padding: '2px 6px',
|
|
||||||
lineHeight: 1,
|
|
||||||
fontWeight: 500,
|
|
||||||
textTransform: 'uppercase',
|
|
||||||
letterSpacing: '0.5px',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Beta: Coming Soon
|
|
||||||
</Typography>
|
|
||||||
)}
|
|
||||||
</Box>
|
</Box>
|
||||||
<Typography
|
<Typography
|
||||||
variant="body2"
|
variant="body2"
|
||||||
|
|||||||
@@ -56,7 +56,8 @@ const OrgHeaderexpandedNew = (props) => {
|
|||||||
adminTab,
|
adminTab,
|
||||||
selectedStatus,
|
selectedStatus,
|
||||||
setSelectedStatus,
|
setSelectedStatus,
|
||||||
isEditOrgTab
|
isEditOrgTab,
|
||||||
|
handleStatusChange
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
@@ -68,7 +69,7 @@ const OrgHeaderexpandedNew = (props) => {
|
|||||||
style: {
|
style: {
|
||||||
maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
|
maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
|
||||||
width: 300,
|
width: 300,
|
||||||
borderRadius: 20,
|
borderRadius: 4,
|
||||||
overflowY: "scroll",
|
overflowY: "scroll",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -93,40 +94,6 @@ const OrgHeaderexpandedNew = (props) => {
|
|||||||
const { themeMode, supportEmail, brandColor } = useContext(Context);
|
const { themeMode, supportEmail, brandColor } = useContext(Context);
|
||||||
const theme = getTheme(themeMode, brandColor);
|
const theme = getTheme(themeMode, brandColor);
|
||||||
|
|
||||||
const handleStatusChange = (event) => {
|
|
||||||
const { value } = event.target;
|
|
||||||
handleEditOrg(
|
|
||||||
orgName,
|
|
||||||
orgDescription,
|
|
||||||
selectedOrganization.id,
|
|
||||||
selectedOrganization?.image,
|
|
||||||
{
|
|
||||||
app_download_repo: selectedOrganization?.defaults?.app_download_repo,
|
|
||||||
app_download_branch: selectedOrganization?.defaults?.app_download_branch,
|
|
||||||
workflow_download_repo: selectedOrganization?.defaults?.workflow_download_repo,
|
|
||||||
workflow_download_branch: selectedOrganization?.defaults?.workflow_download_branch,
|
|
||||||
notification_workflow: selectedOrganization?.defaults?.notification_workflow,
|
|
||||||
documentation_reference: selectedOrganization?.defaults?.documentation_reference,
|
|
||||||
workflow_upload_repo: selectedOrganization?.defaults?.workflow_upload_repo,
|
|
||||||
workflow_upload_branch: selectedOrganization?.defaults?.workflow_upload_branch,
|
|
||||||
workflow_upload_username: selectedOrganization?.defaults?.workflow_upload_username,
|
|
||||||
workflow_upload_token: selectedOrganization?.defaults?.workflow_upload_token,
|
|
||||||
newsletter: selectedOrganization?.defaults?.newsletter,
|
|
||||||
weekly_recommendations: selectedOrganization?.defaults?.weekly_recommendations,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
sso_entrypoint: selectedOrganization?.sso_config?.sso_entrypoint,
|
|
||||||
sso_certificate: selectedOrganization?.sso_config?.sso_certificate,
|
|
||||||
client_id: selectedOrganization?.sso_config?.client_id,
|
|
||||||
client_secret: selectedOrganization?.sso_config?.client_secret,
|
|
||||||
openid_authorization: selectedOrganization?.sso_config?.openid_authorization,
|
|
||||||
openid_token: selectedOrganization?.sso_config?.openid_token,
|
|
||||||
SSORequired: selectedOrganization?.sso_config?.SSORequired,
|
|
||||||
auto_provision: selectedOrganization?.sso_config?.auto_provision,
|
|
||||||
},
|
|
||||||
value.length === 0 ? ["none"] : value,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
const [appDownloadBranch, setAppDownloadBranch] = React.useState(
|
const [appDownloadBranch, setAppDownloadBranch] = React.useState(
|
||||||
selectedOrganization.defaults === undefined
|
selectedOrganization.defaults === undefined
|
||||||
? defaultBranch
|
? defaultBranch
|
||||||
@@ -544,7 +511,7 @@ const OrgHeaderexpandedNew = (props) => {
|
|||||||
renderValue={(selected) => selected.join(', ')}
|
renderValue={(selected) => selected.join(', ')}
|
||||||
MenuProps={MenuProps}
|
MenuProps={MenuProps}
|
||||||
>
|
>
|
||||||
{["contacted", "lead", "demo done", "pov", "customer", "open source", "student", "internal", "creator", "tech partner", "integration partner", "distribution partner", "service partner", "old customer", "old lead"].map((name) => (
|
{["contacted", "lead", "demo done", "pov", "customer", "open source", "student", "internal", "creator", "tech partner", "integration partner", "distribution partner", "channel partner", "service partner", "old customer", "old lead"].map((name) => (
|
||||||
<MenuItem key={name} value={name}>
|
<MenuItem key={name} value={name}>
|
||||||
<Checkbox checked={selectedStatus.indexOf(name) > -1} />
|
<Checkbox checked={selectedStatus.indexOf(name) > -1} />
|
||||||
<ListItemText primary={name} />
|
<ListItemText primary={name} />
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ const OrganizationTab = (props) => {
|
|||||||
const [billingInfo, setBillingInfo] = useState({});
|
const [billingInfo, setBillingInfo] = useState({});
|
||||||
const [orgRequest, setOrgRequest] = React.useState(true);
|
const [orgRequest, setOrgRequest] = React.useState(true);
|
||||||
const [curIndex, setCurIndex] = React.useState(0);
|
const [curIndex, setCurIndex] = React.useState(0);
|
||||||
const items = ['Org Configuration', "SSO", "Notifications", 'Billing & Stats', 'Branding'];
|
const items = ['Org Configuration', "SSO", "Notifications", 'Billing & Stats'];
|
||||||
const [visibleTabs, setVisibleTabs] = useState(items);
|
const [visibleTabs, setVisibleTabs] = useState(items);
|
||||||
const [unreadNotifications, setUnreadNotifications] = React.useState(
|
const [unreadNotifications, setUnreadNotifications] = React.useState(
|
||||||
notifications?.filter((notification) => notification.read === false)?.length
|
notifications?.filter((notification) => notification.read === false)?.length
|
||||||
@@ -157,16 +157,16 @@ const OrganizationTab = (props) => {
|
|||||||
isLoaded={isLoaded}
|
isLoaded={isLoaded}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
case 'branding':
|
// case 'branding':
|
||||||
return <Branding
|
// return <Branding
|
||||||
isCloud={isCloud}
|
// isCloud={isCloud}
|
||||||
userdata={userdata}
|
// userdata={userdata}
|
||||||
globalUrl={globalUrl}
|
// globalUrl={globalUrl}
|
||||||
handleGetOrg={handleGetOrg}
|
// handleGetOrg={handleGetOrg}
|
||||||
selectedOrganization={selectedOrganization}
|
// selectedOrganization={selectedOrganization}
|
||||||
clickedFromOrgTab={true}
|
// clickedFromOrgTab={true}
|
||||||
setSelectedOrganization={setSelectedOrganization}
|
// setSelectedOrganization={setSelectedOrganization}
|
||||||
/>;
|
// />;
|
||||||
// case 'analytics':
|
// case 'analytics':
|
||||||
// return <AnalyticsTab isCloud={isCloud} userdata={userdata} globalUrl={globalUrl} />;
|
// return <AnalyticsTab isCloud={isCloud} userdata={userdata} globalUrl={globalUrl} />;
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -904,13 +904,12 @@ const ParsedAction = (props) => {
|
|||||||
|
|
||||||
if (paramvalue.includes("$")) {
|
if (paramvalue.includes("$")) {
|
||||||
let actions = workflow.actions?.map((action) => {
|
let actions = workflow.actions?.map((action) => {
|
||||||
return "$" + action.label?.toLowerCase();
|
return "$" + action.label?.toLowerCase().replaceAll(" ", "_");
|
||||||
})
|
})
|
||||||
|
|
||||||
if (newActionList?.length > 0) {
|
if (newActionList?.length > 0) {
|
||||||
let appParentActions = parentActionList?.map(action => "$" + action.name.toLowerCase());
|
let appParentActions = parentActionList?.map(action => "$" + action.name.toLowerCase().replaceAll(" ", "_"));
|
||||||
let notPresentAction = actions?.filter((action) => !appParentActions?.includes(action))
|
let notPresentAction = actions?.filter((action) => !appParentActions?.includes(action))
|
||||||
|
|
||||||
// Extract all variable references from paramvalue
|
// Extract all variable references from paramvalue
|
||||||
// Examples of what it matches:
|
// Examples of what it matches:
|
||||||
// - Simple variables: $test, $myVar, $x
|
// - Simple variables: $test, $myVar, $x
|
||||||
|
|||||||
@@ -358,6 +358,21 @@ const PartnerDetails = (props) => {
|
|||||||
animation="wave"
|
animation="wave"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div style={{ width: "100%", maxWidth: 500, marginRight: 10, marginTop: 10 }}>
|
||||||
|
<Typography variant="text" style={{ color: theme?.palette?.text?.primary }}>
|
||||||
|
Contact Email
|
||||||
|
</Typography>
|
||||||
|
<Skeleton
|
||||||
|
variant="rounded"
|
||||||
|
height={35}
|
||||||
|
width="100%"
|
||||||
|
style={{
|
||||||
|
marginTop: 5,
|
||||||
|
borderRadius: 4
|
||||||
|
}}
|
||||||
|
animation="wave"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -824,7 +839,7 @@ const PartnerDetails = (props) => {
|
|||||||
value={partnerData?.article_url}
|
value={partnerData?.article_url}
|
||||||
onBlur={() => {}}
|
onBlur={() => {}}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
if (e.target.value.length > 100) {
|
if (e.target.value.length > 1000) {
|
||||||
toast("Choose a shorter article URL.");
|
toast("Choose a shorter article URL.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -851,7 +866,65 @@ const PartnerDetails = (props) => {
|
|||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
style={{ width: "100%", maxWidth: 500, marginRight: 10, marginTop: 20 }}
|
||||||
|
>
|
||||||
|
<Typography
|
||||||
|
variant="text"
|
||||||
|
style={{ color: theme.palette.text.primary }}
|
||||||
|
>
|
||||||
|
Contact Email
|
||||||
|
</Typography>
|
||||||
|
<TextField
|
||||||
|
required
|
||||||
|
disabled={isDisabled}
|
||||||
|
style={{
|
||||||
|
flex: "1",
|
||||||
|
display: "flex",
|
||||||
|
height: 35,
|
||||||
|
width: "100%",
|
||||||
|
maxWidth: 500,
|
||||||
|
marginTop: "5px",
|
||||||
|
marginRight: "15px",
|
||||||
|
color: theme.palette.textFieldStyle.color,
|
||||||
|
backgroundColor: isEditOrgTab
|
||||||
|
? theme.palette.textFieldStyle.backgroundColor
|
||||||
|
: theme.palette.inputColor,
|
||||||
|
cursor: isDisabled ? "not-allowed" : "pointer",
|
||||||
|
}}
|
||||||
|
fullWidth={true}
|
||||||
|
placeholder="https://www.example.com"
|
||||||
|
type="name"
|
||||||
|
id="standard-required"
|
||||||
|
margin="normal"
|
||||||
|
variant="outlined"
|
||||||
|
value={partnerData?.contact_email}
|
||||||
|
onBlur={() => {}}
|
||||||
|
onChange={(e) => {
|
||||||
|
setPartnerData({
|
||||||
|
...partnerData,
|
||||||
|
contact_email: e.target.value,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
color="primary"
|
||||||
|
InputProps={{
|
||||||
|
style: {
|
||||||
|
color: theme.palette.textFieldStyle.color,
|
||||||
|
height: "35px",
|
||||||
|
fontSize: "1em",
|
||||||
|
borderRadius: 4,
|
||||||
|
backgroundColor:
|
||||||
|
theme.palette.textFieldStyle.backgroundColor,
|
||||||
|
},
|
||||||
|
classes: {
|
||||||
|
notchedOutline: isEditOrgTab
|
||||||
|
? null
|
||||||
|
: classes.notchedOutline,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -54,7 +54,8 @@ const PartnerSettings = (props) => {
|
|||||||
"tech_partner": "#ff8544",
|
"tech_partner": "#ff8544",
|
||||||
"distribution_partner": "#2BC07E",
|
"distribution_partner": "#2BC07E",
|
||||||
"service_partner": "#a99cf9",
|
"service_partner": "#a99cf9",
|
||||||
"integration_partner": "#fb47a0"
|
"integration_partner": "#fb47a0",
|
||||||
|
"channel_partner": "#4caf50",
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSendUpdateRequest = () => {
|
const handleSendUpdateRequest = () => {
|
||||||
@@ -85,6 +86,7 @@ const PartnerSettings = (props) => {
|
|||||||
{ field: partnerData?.landscape_image_url, name: "Landscape Image" },
|
{ field: partnerData?.landscape_image_url, name: "Landscape Image" },
|
||||||
{ field: partnerData?.website_url, name: "Website URL" },
|
{ field: partnerData?.website_url, name: "Website URL" },
|
||||||
{ field: partnerData?.article_url, name: "Article URL" },
|
{ field: partnerData?.article_url, name: "Article URL" },
|
||||||
|
{ field: partnerData?.contact_email, name: "Contact Email" },
|
||||||
{ field: partnerData?.country, name: "Country" },
|
{ field: partnerData?.country, name: "Country" },
|
||||||
{ field: partnerData?.region, name: "Region" },
|
{ field: partnerData?.region, name: "Region" },
|
||||||
];
|
];
|
||||||
@@ -129,6 +131,7 @@ const PartnerSettings = (props) => {
|
|||||||
description: partnerData.description?.trim(),
|
description: partnerData.description?.trim(),
|
||||||
website_url: partnerData.website_url?.trim(),
|
website_url: partnerData.website_url?.trim(),
|
||||||
article_url: partnerData.article_url?.trim(),
|
article_url: partnerData.article_url?.trim(),
|
||||||
|
contact_email: partnerData.contact_email?.trim(),
|
||||||
partner_type: partnerTypes,
|
partner_type: partnerTypes,
|
||||||
expertise: partnerData?.expertise || [],
|
expertise: partnerData?.expertise || [],
|
||||||
services: partnerData?.services || [],
|
services: partnerData?.services || [],
|
||||||
@@ -177,6 +180,7 @@ const PartnerSettings = (props) => {
|
|||||||
description: partnerData.description?.trim(),
|
description: partnerData.description?.trim(),
|
||||||
website_url: partnerData.website_url?.trim(),
|
website_url: partnerData.website_url?.trim(),
|
||||||
article_url: partnerData.article_url?.trim(),
|
article_url: partnerData.article_url?.trim(),
|
||||||
|
contact_email: partnerData.contact_email?.trim(),
|
||||||
partner_type: partnerTypes,
|
partner_type: partnerTypes,
|
||||||
usecases: partnerData?.usecases || [],
|
usecases: partnerData?.usecases || [],
|
||||||
expertise: partnerData?.expertise || [],
|
expertise: partnerData?.expertise || [],
|
||||||
|
|||||||
@@ -195,14 +195,17 @@ const PartnerTab = (props) => {
|
|||||||
// Enable the tab by default
|
// Enable the tab by default
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isSupportOnlyTab = (tabName) => {
|
||||||
|
return tabName === "Apps" || tabName === "Articles" || tabName === "AI Agents";
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ height: "100%", width: "100%", color: theme.palette.platformColor, backgroundColor: theme.palette.platformColor, borderTopRightRadius: '8px', borderBottomRightRadius: 8, borderLeft: theme.palette.defaultBorder, boxSizing: 'border-box' }}>
|
<div style={{ height: "100%", width: "100%", color: theme.palette.platformColor, backgroundColor: theme.palette.platformColor, borderTopRightRadius: '8px', borderBottomRightRadius: 8, borderLeft: theme.palette.defaultBorder, boxSizing: 'border-box' }}>
|
||||||
<div style={{ display: 'flex', justifyContent: 'space-around', width: "100%", borderBottom: theme.palette.defaultBorder ,boxSizing: 'border-box' }}>
|
<div style={{ display: 'flex', justifyContent: 'space-around', width: "100%", borderBottom: theme.palette.defaultBorder ,boxSizing: 'border-box' }}>
|
||||||
{tabsOnPartnerTab?.map((tabName, index) => (
|
{tabsOnPartnerTab?.map((tabName, index) => (
|
||||||
<div style={{ pointerEvents: 'auto', width: '100%',}}>
|
<div key={tabName} style={{ pointerEvents: 'auto', width: '100%', position: 'relative'}}>
|
||||||
<Button
|
<Button
|
||||||
key={tabName}
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setCurIndex(index);
|
setCurIndex(index);
|
||||||
handleTabClick(index === 0 ? "partner_settings" : tabName.toLowerCase().replace(/[\s&]+/g, ''));
|
handleTabClick(index === 0 ? "partner_settings" : tabName.toLowerCase().replace(/[\s&]+/g, ''));
|
||||||
@@ -232,6 +235,26 @@ const PartnerTab = (props) => {
|
|||||||
>
|
>
|
||||||
{tabName}
|
{tabName}
|
||||||
</Button>
|
</Button>
|
||||||
|
{isSupportOnlyTab(tabName) && userdata?.support && (
|
||||||
|
<div style={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: '8px',
|
||||||
|
right: '8px',
|
||||||
|
backgroundColor: '#4D4D4D',
|
||||||
|
color: 'white',
|
||||||
|
borderRadius: '50%',
|
||||||
|
width: '20px',
|
||||||
|
height: '20px',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
fontSize: '12px',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
zIndex: 1
|
||||||
|
}}>
|
||||||
|
S
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -29,9 +29,9 @@ import CloseIcon from "@mui/icons-material/Close";
|
|||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
import MoreVertIcon from "@mui/icons-material/MoreVert";
|
import MoreVertIcon from "@mui/icons-material/MoreVert";
|
||||||
import EditIcon from "@mui/icons-material/Edit";
|
import EditIcon from "@mui/icons-material/Edit";
|
||||||
import StarIcon from "@mui/icons-material/Star";
|
|
||||||
import DeleteIcon from "@mui/icons-material/Delete";
|
import DeleteIcon from "@mui/icons-material/Delete";
|
||||||
import AddCircleOutlineIcon from "@mui/icons-material/AddCircleOutline";
|
import AddCircleOutlineIcon from "@mui/icons-material/AddCircleOutline";
|
||||||
|
import OpenInNewIcon from '@mui/icons-material/OpenInNew';
|
||||||
import DeleteOutlineIcon from "@mui/icons-material/DeleteOutline";
|
import DeleteOutlineIcon from "@mui/icons-material/DeleteOutline";
|
||||||
import KeyboardArrowUpIcon from "@mui/icons-material/KeyboardArrowUp";
|
import KeyboardArrowUpIcon from "@mui/icons-material/KeyboardArrowUp";
|
||||||
import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown";
|
import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown";
|
||||||
@@ -1083,16 +1083,40 @@ const PartnersUsecasesTab = ({ isCloud, globalUrl, userdata, partnerData, setPar
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Typography
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
color: theme.palette.accentColor,
|
display: "flex",
|
||||||
|
flexDirection: "row",
|
||||||
|
gap: 1,
|
||||||
mb: 2,
|
mb: 2,
|
||||||
fontSize: "16px",
|
alignItems: "center",
|
||||||
fontWeight: 600,
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Public Workflow
|
<Typography
|
||||||
</Typography>
|
sx={{
|
||||||
|
color: theme.palette.accentColor,
|
||||||
|
fontSize: "16px",
|
||||||
|
fontWeight: 600,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Public Workflow
|
||||||
|
</Typography>
|
||||||
|
<IconButton
|
||||||
|
size="small"
|
||||||
|
sx={{
|
||||||
|
color: theme.palette.primary.main,
|
||||||
|
"&:hover": {
|
||||||
|
backgroundColor: theme.palette.action.hover,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
onClick={() => {
|
||||||
|
// Open workflow in new tab - adjust URL as needed
|
||||||
|
window.open(`${window.location.origin}/workflows/${formData.mainContent.publicWorkflowId}`, "_blank");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<OpenInNewIcon fontSize="small" />
|
||||||
|
</IconButton>
|
||||||
|
</Box>
|
||||||
<Select
|
<Select
|
||||||
displayEmpty
|
displayEmpty
|
||||||
value={
|
value={
|
||||||
|
|||||||
@@ -176,11 +176,12 @@ const Priorities = memo((props) => {
|
|||||||
credentials: "include",
|
credentials: "include",
|
||||||
}).then((response) => {
|
}).then((response) => {
|
||||||
if (response.status !== 200) {
|
if (response.status !== 200) {
|
||||||
toast(`Failed getting config for ${item.id}: `, response.reason);
|
//toast.error(`Failed getting config for ${item.id}: `, response.reason)
|
||||||
console.log("Status not 200 for app config :O!");
|
console.log("Status not 200 for app config :O!");
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
return response.json();
|
|
||||||
|
return response.json()
|
||||||
}).then((responseJson) => {
|
}).then((responseJson) => {
|
||||||
if (!responseJson.success) {
|
if (!responseJson.success) {
|
||||||
console.log("Could not get app config")
|
console.log("Could not get app config")
|
||||||
@@ -209,7 +210,7 @@ const Priorities = memo((props) => {
|
|||||||
|
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
console.log("Error getting app config: " + error);
|
console.log("Error getting app config: " + error);
|
||||||
toast("Error getting app config: " + error);
|
//toast.error("Error getting app config: " + error);
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -79,9 +79,14 @@ const RuntimeDebugger = (props) => {
|
|||||||
const [searchLoading, setSearchLoading] = useState(false)
|
const [searchLoading, setSearchLoading] = useState(false)
|
||||||
const [rowCursor, setCursor] = useState("")
|
const [rowCursor, setCursor] = useState("")
|
||||||
const [rowsPerPage, setRowsPerPage] = useState(10)
|
const [rowsPerPage, setRowsPerPage] = useState(10)
|
||||||
|
const [maxExecutionCount, setMaxExecutionCount] = useState(50)
|
||||||
const [resultRows, setResultRows] = useState([])
|
const [resultRows, setResultRows] = useState([])
|
||||||
const [selectedWorkflowExecutions, setSelectedWorkflowExecutions] = useState([])
|
const [selectedWorkflowExecutions, setSelectedWorkflowExecutions] = useState([])
|
||||||
const [suborgWorkflowRuns, setSuborgWorkflowRuns] = useState(false)
|
const [suborgWorkflowRuns, setSuborgWorkflowRuns] = useState(false)
|
||||||
|
const [paginationModel, setPaginationModel] = useState({
|
||||||
|
page: 0,
|
||||||
|
pageSize: 10,
|
||||||
|
})
|
||||||
const [openWorkflowMenu, setOpenWorkflowMenu] = useState(false)
|
const [openWorkflowMenu, setOpenWorkflowMenu] = useState(false)
|
||||||
const [workflows, setWorkflows] = useState([
|
const [workflows, setWorkflows] = useState([
|
||||||
{"id": "", "name": "All Workflows",}
|
{"id": "", "name": "All Workflows",}
|
||||||
@@ -665,6 +670,13 @@ const RuntimeDebugger = (props) => {
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setPaginationModel(prev => ({
|
||||||
|
...prev,
|
||||||
|
pageSize: rowsPerPage
|
||||||
|
}))
|
||||||
|
}, [rowsPerPage])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Check if the user is currently focusing a texxtfield or not
|
// Check if the user is currently focusing a texxtfield or not
|
||||||
// If they are, don't submit the search
|
// If they are, don't submit the search
|
||||||
@@ -672,7 +684,7 @@ const RuntimeDebugger = (props) => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
submitSearch(workflowId, status, startTime, endTime, rowCursor, rowsPerPage, suborgWorkflowRuns)
|
submitSearch(workflowId, status, startTime, endTime, rowCursor, maxExecutionCount, suborgWorkflowRuns)
|
||||||
}, [workflowId, status, startTime, endTime])
|
}, [workflowId, status, startTime, endTime])
|
||||||
|
|
||||||
const textfieldStyle = {
|
const textfieldStyle = {
|
||||||
@@ -723,7 +735,7 @@ const RuntimeDebugger = (props) => {
|
|||||||
setWorkflowId(e.target.value.id)
|
setWorkflowId(e.target.value.id)
|
||||||
setSuborgWorkflowRuns(false)
|
setSuborgWorkflowRuns(false)
|
||||||
|
|
||||||
submitSearch(e.target.value.id, status, startTime, endTime, rowCursor, rowsPerPage, false)
|
submitSearch(e.target.value.id, status, startTime, endTime, rowCursor, maxExecutionCount, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
const executeWorkflow = (execution) => {
|
const executeWorkflow = (execution) => {
|
||||||
@@ -819,9 +831,12 @@ const RuntimeDebugger = (props) => {
|
|||||||
<div style={{display: "flex", paddingTop: 50, }}>
|
<div style={{display: "flex", paddingTop: 50, }}>
|
||||||
<div style={{display: 'flex', flexDirection: 'column'}}>
|
<div style={{display: 'flex', flexDirection: 'column'}}>
|
||||||
<div style={{display: "flex", width: "100%", }}>
|
<div style={{display: "flex", width: "100%", }}>
|
||||||
<Typography variant="h3" style={{flex: 3, whiteSpace: "nowrap" }}>Workflow Run Debugger {totalCount !== 0 ? ` (~${totalCount})` : ""}</Typography>
|
<Typography variant="h3" style={{flex: 3, whiteSpace: "nowrap" }}>
|
||||||
|
Workflow Run Debugger {totalCount !== 0 ? ` (~${totalCount})` : ""}
|
||||||
|
</Typography>
|
||||||
|
|
||||||
{selectedWorkflowExecutions.length > 0 ?
|
{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"
|
||||||
@@ -864,7 +879,7 @@ const RuntimeDebugger = (props) => {
|
|||||||
} else {
|
} else {
|
||||||
toast("Aborted "+aborted+" workflows.")
|
toast("Aborted "+aborted+" workflows.")
|
||||||
// Research
|
// Research
|
||||||
submitSearch(workflowId, status, startTime, endTime, rowCursor, rowsPerPage, suborgWorkflowRuns)
|
submitSearch(workflowId, status, startTime, endTime, rowCursor, maxExecutionCount, suborgWorkflowRuns)
|
||||||
|
|
||||||
setSelectedWorkflowExecutions([])
|
setSelectedWorkflowExecutions([])
|
||||||
}
|
}
|
||||||
@@ -902,7 +917,7 @@ const RuntimeDebugger = (props) => {
|
|||||||
marginTop: 20,
|
marginTop: 20,
|
||||||
marginLeft: 10,
|
marginLeft: 10,
|
||||||
marginRight: 12,
|
marginRight: 12,
|
||||||
width: 693,
|
width: 643,
|
||||||
height: 51,
|
height: 51,
|
||||||
borderRadius: 4,
|
borderRadius: 4,
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
@@ -913,7 +928,7 @@ const RuntimeDebugger = (props) => {
|
|||||||
color: theme.palette.textColor,
|
color: theme.palette.textColor,
|
||||||
fontSize: "1em",
|
fontSize: "1em",
|
||||||
height: 51,
|
height: 51,
|
||||||
width: 693,
|
width: 643,
|
||||||
borderRadius: 4,
|
borderRadius: 4,
|
||||||
},
|
},
|
||||||
startAdornment: (
|
startAdornment: (
|
||||||
@@ -942,6 +957,34 @@ const RuntimeDebugger = (props) => {
|
|||||||
placeholder="Filter by Workflow Name, Status, Execution Argument, Results"
|
placeholder="Filter by Workflow Name, Status, Execution Argument, Results"
|
||||||
id="shuffle_search_field"
|
id="shuffle_search_field"
|
||||||
/>
|
/>
|
||||||
|
<Tooltip title="Set the maximum number of workflow executions to retrieve in search results" placement="top">
|
||||||
|
<FormControl style={{ minWidth: 120, marginTop: 20 }}>
|
||||||
|
<InputLabel id="max-execution-count-label" style={{ fontSize: '0.875rem' }}>Max Results</InputLabel>
|
||||||
|
<Select
|
||||||
|
labelId="max-execution-count-label"
|
||||||
|
id="max-execution-count-select"
|
||||||
|
value={maxExecutionCount}
|
||||||
|
label="Max Results"
|
||||||
|
size="small"
|
||||||
|
onChange={(e) => {
|
||||||
|
setMaxExecutionCount(e.target.value)
|
||||||
|
submitSearch(workflowId, status, startTime, endTime, rowCursor, e.target.value, suborgWorkflowRuns)
|
||||||
|
}}
|
||||||
|
style={{
|
||||||
|
height: 50,
|
||||||
|
backgroundColor: theme.palette.backgroundColor,
|
||||||
|
color: theme.palette.textColor,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<MenuItem value={10}>10</MenuItem>
|
||||||
|
<MenuItem value={25}>25</MenuItem>
|
||||||
|
<MenuItem value={50}>50</MenuItem>
|
||||||
|
<MenuItem value={100}>100</MenuItem>
|
||||||
|
<MenuItem value={200}>200</MenuItem>
|
||||||
|
<MenuItem value={500}>500</MenuItem>
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
{userdata?.active_org?.creator_org?.length === 0 ? (
|
{userdata?.active_org?.creator_org?.length === 0 ? (
|
||||||
<div style={{display: "flex", margin: 'auto',marginTop: 20,justifyContent: 'center', alignItems: 'center', }}>
|
<div style={{display: "flex", margin: 'auto',marginTop: 20,justifyContent: 'center', alignItems: 'center', }}>
|
||||||
@@ -956,7 +999,8 @@ const RuntimeDebugger = (props) => {
|
|||||||
setStartTime("")
|
setStartTime("")
|
||||||
setEndTime("")
|
setEndTime("")
|
||||||
setSearchQuery("")
|
setSearchQuery("")
|
||||||
submitSearch("", "", "", "", rowCursor, rowsPerPage, !suborgWorkflowRuns)}
|
setMaxExecutionCount(50)
|
||||||
|
submitSearch("", "", "", "", rowCursor, 50, !suborgWorkflowRuns)}
|
||||||
}
|
}
|
||||||
color="secondary"
|
color="secondary"
|
||||||
/>
|
/>
|
||||||
@@ -967,7 +1011,7 @@ const RuntimeDebugger = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<form onSubmit={(e) => {
|
<form onSubmit={(e) => {
|
||||||
submitSearch(workflowId, status, startTime, endTime, rowCursor, rowsPerPage, suborgWorkflowRuns)
|
submitSearch(workflowId, status, startTime, endTime, rowCursor, maxExecutionCount, suborgWorkflowRuns)
|
||||||
}} style={{display: "flex", justifyContent: "center", alignItems: "center", }}>
|
}} 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>
|
||||||
@@ -1158,7 +1202,8 @@ const RuntimeDebugger = (props) => {
|
|||||||
setEndTime("")
|
setEndTime("")
|
||||||
setSearchQuery("")
|
setSearchQuery("")
|
||||||
setSuborgWorkflowRuns(false)
|
setSuborgWorkflowRuns(false)
|
||||||
submitSearch("", "", "", "", rowCursor, rowsPerPage, false)
|
setMaxExecutionCount(50)
|
||||||
|
submitSearch("", "", "", "", rowCursor, 50, false)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<FilterAltOffIcon />
|
<FilterAltOffIcon />
|
||||||
@@ -1169,7 +1214,7 @@ const RuntimeDebugger = (props) => {
|
|||||||
variant="outlined"
|
variant="outlined"
|
||||||
color="primary"
|
color="primary"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
submitSearch(workflowId, status, startTime, endTime, rowCursor, rowsPerPage, suborgWorkflowRuns)
|
submitSearch(workflowId, status, startTime, endTime, rowCursor, maxExecutionCount, suborgWorkflowRuns)
|
||||||
}}
|
}}
|
||||||
disabled={searchLoading}
|
disabled={searchLoading}
|
||||||
style={{height: 50, minWidth: 100, marginTop: 15, }}
|
style={{height: 50, minWidth: 100, marginTop: 15, }}
|
||||||
@@ -1181,20 +1226,18 @@ const RuntimeDebugger = (props) => {
|
|||||||
<DataGrid
|
<DataGrid
|
||||||
rows={filteredRows}
|
rows={filteredRows}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
pageSize={rowsPerPage}
|
paginationModel={paginationModel}
|
||||||
rowsPerPageOptions={[10, 20, 50, 100]}
|
pageSizeOptions={[10, 20, 50, 75, 100]}
|
||||||
checkboxSelection
|
checkboxSelection
|
||||||
disableSelectionOnClick
|
disableSelectionOnClick
|
||||||
onPageSizeChange={(newPageSize) => {
|
|
||||||
setRowsPerPage(newPageSize)
|
onPaginationModelChange={(newPaginationModel) => {
|
||||||
submitSearch(workflowId, status, startTime, endTime, rowCursor, newPageSize, suborgWorkflowRuns)
|
setPaginationModel(newPaginationModel)
|
||||||
|
setRowsPerPage(newPaginationModel.pageSize)
|
||||||
|
// No API call needed - this is just for client-side pagination
|
||||||
}}
|
}}
|
||||||
// event for when clicking next page
|
|
||||||
// Hide page changer
|
onRowSelectionModelChange={(newSelection) => {
|
||||||
onPageChange={(params) => {
|
|
||||||
console.log("params: ", params)
|
|
||||||
}}
|
|
||||||
onSelectionModelChange={(newSelection) => {
|
|
||||||
//console.log("newSelection: ", newSelection)
|
//console.log("newSelection: ", newSelection)
|
||||||
//setSelectedWorkflowExecutionsIndexes(newSelection)
|
//setSelectedWorkflowExecutionsIndexes(newSelection)
|
||||||
var found = []
|
var found = []
|
||||||
|
|||||||
@@ -2476,9 +2476,14 @@ const CodeEditor = (props) => {
|
|||||||
}}
|
}}
|
||||||
displayDataTypes={false}
|
displayDataTypes={false}
|
||||||
onSelect={(select) => {
|
onSelect={(select) => {
|
||||||
//HandleJsonCopy(executionResult.result, select, "exec");
|
var basename = "exec"
|
||||||
|
if (selectedAction !== undefined && selectedAction !== null && Object.keys(selectedAction).length !== 0) {
|
||||||
|
basename = selectedAction.label.toLowerCase().replaceAll(" ", "_")
|
||||||
|
}
|
||||||
|
|
||||||
|
HandleJsonCopy(executionResult.result, select, basename)
|
||||||
}}
|
}}
|
||||||
name={"Test result"}
|
name={"Run Output"}
|
||||||
/>
|
/>
|
||||||
:
|
:
|
||||||
<span style={{ maxHeight: 190, minHeight: 190, }}>
|
<span style={{ maxHeight: 190, minHeight: 190, }}>
|
||||||
|
|||||||
@@ -667,13 +667,12 @@ const TenantsTab = memo((props) => {
|
|||||||
const handleDeleteAccount = () => {
|
const handleDeleteAccount = () => {
|
||||||
const baseURL = globalUrl;
|
const baseURL = globalUrl;
|
||||||
|
|
||||||
const url = `${baseURL}/api/v1/orgs/${selectedOrganization?.id}`;
|
const url = `${baseURL}/api/v1/orgs/${selectedSuborg?.id}`;
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
suborg_id : selectedSuborg?.id,
|
|
||||||
password: password,
|
password: password,
|
||||||
}
|
};
|
||||||
|
|
||||||
fetch(url, {
|
fetch(url, {
|
||||||
mode: "cors",
|
mode: "cors",
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
|
|||||||
@@ -1465,7 +1465,11 @@ const UserManagmentTab = memo((props) => {
|
|||||||
<ListItem key={index} style={{ backgroundColor: bgColor, display: 'table-row', borderBottomLeftRadius: users?.length - 1 === index ? 8 : 0, borderBottomRightRadius: users?.length - 1 === index ? 8 : 0 }}>
|
<ListItem key={index} style={{ backgroundColor: bgColor, display: 'table-row', borderBottomLeftRadius: users?.length - 1 === index ? 8 : 0, borderBottomRightRadius: users?.length - 1 === index ? 8 : 0 }}>
|
||||||
{isCloud ? (
|
{isCloud ? (
|
||||||
<ListItemText
|
<ListItemText
|
||||||
primary={(<img src={`https://flagcdn.com/48x36/${userRegion.toLowerCase()}.png`} alt={data?.user_geo_info?.country?.iso_code} style={{ marginRight: 30, width: 25, height: 23, }} />)}
|
primary={(
|
||||||
|
userRegion ? (
|
||||||
|
<img src={`https://flagcdn.com/48x36/${userRegion.toLowerCase()}.png`} alt={data?.user_geo_info?.country?.iso_code} style={{ marginRight: 30, width: 25, height: 23, }} />
|
||||||
|
) : null
|
||||||
|
)}
|
||||||
style={{ display: 'table-cell', verticalAlign: 'middle', textAlign: 'center' }}
|
style={{ display: 'table-cell', verticalAlign: 'middle', textAlign: 'center' }}
|
||||||
/>) : null}
|
/>) : null}
|
||||||
<ListItemText
|
<ListItemText
|
||||||
|
|||||||
@@ -392,8 +392,8 @@ export default function defaultCytoscapeStyle(theme) {
|
|||||||
{
|
{
|
||||||
selector: ".success-highlight",
|
selector: ".success-highlight",
|
||||||
css: {
|
css: {
|
||||||
"background-color": "#41dcab",
|
"background-color": "#02CB70",
|
||||||
"border-color": "#41dcab",
|
"border-color": "#02CB70",
|
||||||
"border-width": "5px",
|
"border-width": "5px",
|
||||||
"transition-property": "background-color",
|
"transition-property": "background-color",
|
||||||
"transition-duration": "0.5s",
|
"transition-duration": "0.5s",
|
||||||
@@ -412,8 +412,8 @@ export default function defaultCytoscapeStyle(theme) {
|
|||||||
{
|
{
|
||||||
selector: ".failure-highlight",
|
selector: ".failure-highlight",
|
||||||
css: {
|
css: {
|
||||||
"background-color": "#8e3530",
|
"background-color": "#F53434",
|
||||||
"border-color": "#8e3530",
|
"border-color": "#F53434",
|
||||||
"border-width": "5px",
|
"border-width": "5px",
|
||||||
"transition-property": "background-color",
|
"transition-property": "background-color",
|
||||||
"transition-duration": "0.5s",
|
"transition-duration": "0.5s",
|
||||||
@@ -433,7 +433,7 @@ export default function defaultCytoscapeStyle(theme) {
|
|||||||
selector: ".executing-highlight",
|
selector: ".executing-highlight",
|
||||||
css: {
|
css: {
|
||||||
//"background-color": "#ffef47",
|
//"background-color": "#ffef47",
|
||||||
"border-color": "#ffef47",
|
"border-color": "#FECC00",
|
||||||
"border-width": "8px",
|
"border-width": "8px",
|
||||||
"transition-property": "border-width",
|
"transition-property": "border-width",
|
||||||
"transition-duration": "0.25s",
|
"transition-duration": "0.25s",
|
||||||
@@ -475,8 +475,8 @@ export default function defaultCytoscapeStyle(theme) {
|
|||||||
selector: "edge.executing-highlight",
|
selector: "edge.executing-highlight",
|
||||||
css: {
|
css: {
|
||||||
width: "5px",
|
width: "5px",
|
||||||
"target-arrow-color": "#ffef47",
|
"target-arrow-color": "#FECC00",
|
||||||
"line-color": "#ffef47",
|
"line-color": "#FECC00",
|
||||||
"transition-property": "line-color, width",
|
"transition-property": "line-color, width",
|
||||||
"transition-duration": "0.25s",
|
"transition-duration": "0.25s",
|
||||||
},
|
},
|
||||||
@@ -495,9 +495,9 @@ export default function defaultCytoscapeStyle(theme) {
|
|||||||
{
|
{
|
||||||
selector: "edge.success-highlight",
|
selector: "edge.success-highlight",
|
||||||
css: {
|
css: {
|
||||||
width: "3px",
|
width: "4px",
|
||||||
"target-arrow-color": "#41dcab",
|
"target-arrow-color": "#02CB70",
|
||||||
"line-color": "#41dcab",
|
"line-color": "#02CB70",
|
||||||
"transition-property": "line-color, width",
|
"transition-property": "line-color, width",
|
||||||
"transition-duration": "0.5s",
|
"transition-duration": "0.5s",
|
||||||
"line-fill": "linear-gradient",
|
"line-fill": "linear-gradient",
|
||||||
|
|||||||
@@ -87,6 +87,10 @@ const Admin2 = (props) => {
|
|||||||
leads.push("distribution partner");
|
leads.push("distribution partner");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (responseJson.lead_info.channel_partner) {
|
||||||
|
leads.push("channel partner");
|
||||||
|
}
|
||||||
|
|
||||||
if (responseJson.lead_info.service_partner) {
|
if (responseJson.lead_info.service_partner) {
|
||||||
leads.push("service partner");
|
leads.push("service partner");
|
||||||
}
|
}
|
||||||
|
|||||||
+209
-43
@@ -1,11 +1,14 @@
|
|||||||
import React, { useState, useEffect, useContext, memo } from "react";
|
import React, { useState, useEffect, useContext, memo } from "react";
|
||||||
import { Context } from "../context/ContextApi.jsx";
|
import { Context } from "../context/ContextApi.jsx";
|
||||||
|
import { useNavigate, Link, useLocation } from "react-router-dom";
|
||||||
import { getTheme } from "../theme.jsx";
|
import { getTheme } from "../theme.jsx";
|
||||||
import { toast } from "react-toastify"
|
import { toast } from "react-toastify"
|
||||||
import ReactJson from "react-json-view-ssr";
|
import ReactJson from "react-json-view-ssr";
|
||||||
|
import { v4 as uuidv4} from "uuid";
|
||||||
import { validateJson, collapseField, handleReactJsonClipboard, HandleJsonCopy } from "../views/Workflows.jsx";
|
import { validateJson, collapseField, handleReactJsonClipboard, HandleJsonCopy } from "../views/Workflows.jsx";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
Box,
|
||||||
Button,
|
Button,
|
||||||
ButtonGroup,
|
ButtonGroup,
|
||||||
Typography,
|
Typography,
|
||||||
@@ -13,6 +16,7 @@ import {
|
|||||||
CircularProgress,
|
CircularProgress,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
IconButton,
|
IconButton,
|
||||||
|
TextField,
|
||||||
} from '@mui/material'
|
} from '@mui/material'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -21,6 +25,8 @@ import {
|
|||||||
RestartAlt as RestartAltIcon,
|
RestartAlt as RestartAltIcon,
|
||||||
ExpandMore as ExpandMoreIcon,
|
ExpandMore as ExpandMoreIcon,
|
||||||
ExpandLess as ExpandLessIcon,
|
ExpandLess as ExpandLessIcon,
|
||||||
|
Send as SendIcon,
|
||||||
|
Error as ErrorIcon,
|
||||||
} from '@mui/icons-material'
|
} from '@mui/icons-material'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -39,9 +45,12 @@ const AgentUI = (props) => {
|
|||||||
|
|
||||||
const [originalStartTime, setOriginalStartTime] = useState(0)
|
const [originalStartTime, setOriginalStartTime] = useState(0)
|
||||||
const [latestEndTime, setLatestEndTime] = useState(0)
|
const [latestEndTime, setLatestEndTime] = useState(0)
|
||||||
|
const [showAgentStarter, setShowAgentStarter] = useState(false)
|
||||||
|
const [actionInput, setActionInput] = useState("")
|
||||||
|
|
||||||
const {themeMode} = useContext(Context)
|
const {themeMode} = useContext(Context)
|
||||||
const theme = getTheme(themeMode)
|
const theme = getTheme(themeMode)
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const agentWrapperStyle = {
|
const agentWrapperStyle = {
|
||||||
width: 1000,
|
width: 1000,
|
||||||
@@ -64,6 +73,10 @@ const AgentUI = (props) => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (node_id === undefined || node_id === null || node_id === "") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var found = false
|
var found = false
|
||||||
for (var key in execution_data.results) {
|
for (var key in execution_data.results) {
|
||||||
const item = execution_data.results[key]
|
const item = execution_data.results[key]
|
||||||
@@ -86,6 +99,16 @@ const AgentUI = (props) => {
|
|||||||
|
|
||||||
if (found === false) {
|
if (found === false) {
|
||||||
toast.warn("Failed to find the relevant AI Agent result")
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node_id === undefined || node_id === null) {
|
//if (node_id === undefined || node_id === null || node_id === "") {
|
||||||
toast.error("No node ID provided. Please provide node_id in the URL.")
|
// toast.error("No node ID provided. Please provide node_id in the URL.")
|
||||||
return
|
// return
|
||||||
}
|
//}
|
||||||
|
|
||||||
if (authorization === undefined || authorization === null) {
|
if (authorization === undefined || authorization === null) {
|
||||||
toast.error("No authorization provided. Please provide authorization in the URL.")
|
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}`
|
const url = `${globalUrl}/api/v1/apps/agent/run?rerun=true&decision_id=${decision?.run_details?.id}`
|
||||||
var body = agentActionResult.action
|
var body = agentActionResult.action
|
||||||
|
console.log("BODY: ", body)
|
||||||
body.source_execution = execution.execution_id
|
body.source_execution = execution.execution_id
|
||||||
body.source_workflow = execution.workflow.id
|
body.source_workflow = execution.workflow.id
|
||||||
|
|
||||||
@@ -202,10 +226,11 @@ const AgentUI = (props) => {
|
|||||||
const executionId = params.get("execution_id")
|
const executionId = params.get("execution_id")
|
||||||
const nodeId = params.get("node_id")
|
const nodeId = params.get("node_id")
|
||||||
const authorization = params.get("authorization")
|
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)
|
GetExecution(executionId, nodeId, authorization)
|
||||||
} else {
|
} 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">
|
<Tooltip title="Finished" placement="top">
|
||||||
<CheckCircleIcon style={{color: green, marginRight: 10, }} />
|
<CheckCircleIcon style={{color: green, marginRight: 10, }} />
|
||||||
</Tooltip>
|
</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">
|
<Tooltip title={`Not started yet: ${item.status}`} placement="top">
|
||||||
<HourglassDisabledIcon style={{marginRight: 10, }} />
|
<HourglassDisabledIcon style={{marginRight: 10, }} />
|
||||||
@@ -246,11 +276,13 @@ const AgentUI = (props) => {
|
|||||||
const validate = validateJson(item.details)
|
const validate = validateJson(item.details)
|
||||||
const itemStartTime = item.start_time
|
const itemStartTime = item.start_time
|
||||||
var itemEndTime = item.end_time
|
var itemEndTime = item.end_time
|
||||||
if (itemStartTime !== undefined && (itemStartTime < originalStartTime || originalStartTime === 0)) {
|
if (itemStartTime !== undefined && itemStartTime !== originalStartTime && (itemStartTime < originalStartTime || originalStartTime === 0)) {
|
||||||
setOriginalStartTime(itemStartTime)
|
console.log("Rerender 1")
|
||||||
|
//setOriginalStartTime(itemStartTime)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (itemEndTime !== undefined && itemEndTime > latestEndTime) {
|
if (itemEndTime !== undefined && itemEndTime > latestEndTime) {
|
||||||
|
console.log("Rerender 2")
|
||||||
setLatestEndTime(itemEndTime)
|
setLatestEndTime(itemEndTime)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -280,23 +312,47 @@ const AgentUI = (props) => {
|
|||||||
borderTop: "1px solid " + theme.palette.surfaceColor,
|
borderTop: "1px solid " + theme.palette.surfaceColor,
|
||||||
borderRadius: theme.palette.borderRadius,
|
borderRadius: theme.palette.borderRadius,
|
||||||
}}
|
}}
|
||||||
|
onMouseEnter={() => {
|
||||||
|
if (!hovered) {
|
||||||
|
console.log("HOVER")
|
||||||
|
setHovered(true)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onMouseLeave={() => {
|
||||||
|
if (hovered) {
|
||||||
|
setHovered(false)
|
||||||
|
}
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
backgroundColor: hovered ? theme.palette.surfaceColor : "inherit",
|
backgroundColor: hovered ? theme.palette.surfaceColor : "inherit",
|
||||||
}}
|
}}
|
||||||
onMouseEnter={() => setHovered(true)}
|
|
||||||
onMouseLeave={() => setHovered(false)}
|
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
if (item.details === undefined || item.details === null || item.details === "") {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (openIndexes.includes(index)) {
|
if (openIndexes.includes(index)) {
|
||||||
|
console.log("Rerender 5")
|
||||||
setOpenIndexes(openIndexes.filter((i) => i !== index))
|
setOpenIndexes(openIndexes.filter((i) => i !== index))
|
||||||
} else {
|
} else {
|
||||||
|
console.log("Rerender 6")
|
||||||
setOpenIndexes([...openIndexes, index])
|
setOpenIndexes([...openIndexes, index])
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
@@ -421,18 +477,31 @@ const AgentUI = (props) => {
|
|||||||
|
|
||||||
const TimelineRender = (props) => {
|
const TimelineRender = (props) => {
|
||||||
const { agent_data } = props;
|
const { agent_data } = props;
|
||||||
|
|
||||||
|
const actionResult = execution?.results?.length > 0 ? execution.results[0] : execution
|
||||||
var timelineItems = [
|
var timelineItems = [
|
||||||
{
|
{
|
||||||
"label": "AI Agent 2",
|
"label": "AI Agent 2",
|
||||||
"type": "agent",
|
"type": "agent",
|
||||||
"category": "agent",
|
"category": "agent",
|
||||||
|
"details": actionResult?.result,
|
||||||
|
|
||||||
"status": agent_data.status,
|
"status": agent_data?.status,
|
||||||
"start_time": agent_data.started_at,
|
"start_time": agent_data?.started_at,
|
||||||
"end_time": agent_data.completed_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 = []
|
var sortedTimelineItems = []
|
||||||
for (var key in agent_data?.decisions) {
|
for (var key in agent_data?.decisions) {
|
||||||
const item = agent_data.decisions[key]
|
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 (
|
return (
|
||||||
<div style={agentWrapperStyle}>
|
<div style={agentWrapperStyle}>
|
||||||
{/*
|
|
||||||
<Typography variant="h4">
|
|
||||||
Agent Input: {data.input}
|
|
||||||
</Typography>
|
|
||||||
*/}
|
|
||||||
|
|
||||||
<ButtonGroup style={{marginTop: 50, }}>
|
{showAgentStarter ?
|
||||||
<Button
|
<Box component="form" style={{textAlign: "center", }} onSubmit={(e) => {
|
||||||
variant={buttonState === "default" ? "contained" : "outlined"}
|
e.preventDefault();
|
||||||
color="secondary"
|
submitInput(actionInput);
|
||||||
onClick={() => {
|
}}>
|
||||||
setButtonState("default");
|
<img src="/images/logos/agent.svg" style={{
|
||||||
}}
|
width: 200,
|
||||||
>
|
height: 200,
|
||||||
Default
|
}} />
|
||||||
</Button>
|
<div />
|
||||||
<Button
|
|
||||||
variant={buttonState === "timeline" ? "contained" : "outlined"}
|
|
||||||
color="secondary"
|
|
||||||
onClick={() => {
|
|
||||||
setButtonState("timeline");
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Timeline
|
|
||||||
</Button>
|
|
||||||
</ButtonGroup>
|
|
||||||
|
|
||||||
{buttonState === "timeline" ?
|
<Typography variant="h5" style={{marginTop: 30, }}>
|
||||||
<TimelineRender agent_data={data} />
|
Shuffle AI Agents
|
||||||
:
|
</Typography>
|
||||||
null
|
<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>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -401,7 +401,6 @@ export function SetJsonDotnotation(jsonInput, inputKey) {
|
|||||||
//export const green = "#86c142";
|
//export const green = "#86c142";
|
||||||
export const green = "#02CB70"
|
export const green = "#02CB70"
|
||||||
export const yellow = "#FECC00";
|
export const yellow = "#FECC00";
|
||||||
//export const red = "#ff3632";
|
|
||||||
export const red = "#F53434";
|
export const red = "#F53434";
|
||||||
export const grey = "#b0b0b0";
|
export const grey = "#b0b0b0";
|
||||||
|
|
||||||
@@ -729,6 +728,8 @@ const AngularWorkflow = (defaultprops) => {
|
|||||||
"List tickets",
|
"List tickets",
|
||||||
"Send Email",
|
"Send Email",
|
||||||
"Get specific ticket",
|
"Get specific ticket",
|
||||||
|
"Update ticket",
|
||||||
|
"Add ticket comment",
|
||||||
],
|
],
|
||||||
"multiselect": true,
|
"multiselect": true,
|
||||||
},
|
},
|
||||||
@@ -2258,7 +2259,7 @@ const AngularWorkflow = (defaultprops) => {
|
|||||||
currentnode.removeClass("shuffle-hover-highlight");
|
currentnode.removeClass("shuffle-hover-highlight");
|
||||||
currentnode.removeClass("awaiting-data-highlight");
|
currentnode.removeClass("awaiting-data-highlight");
|
||||||
currentnode.addClass("success-highlight");
|
currentnode.addClass("success-highlight");
|
||||||
incomingEdges.addClass("success-highlight");
|
|
||||||
outgoingEdges.addClass("success-highlight");
|
outgoingEdges.addClass("success-highlight");
|
||||||
|
|
||||||
if (visited !== undefined && visited !== null && !visited.includes(label)) {
|
if (visited !== undefined && visited !== null && !visited.includes(label)) {
|
||||||
@@ -5368,7 +5369,8 @@ const AngularWorkflow = (defaultprops) => {
|
|||||||
if (!branchFound) {
|
if (!branchFound) {
|
||||||
var relevantNodes = []
|
var relevantNodes = []
|
||||||
|
|
||||||
const minDistance = 185
|
//const minDistance = 185
|
||||||
|
const minDistance = 85
|
||||||
const draggedNode = event.target
|
const draggedNode = event.target
|
||||||
const allnodes = cy.nodes().jsons()
|
const allnodes = cy.nodes().jsons()
|
||||||
for (var nodekey in allnodes) {
|
for (var nodekey in allnodes) {
|
||||||
@@ -5398,7 +5400,7 @@ const AngularWorkflow = (defaultprops) => {
|
|||||||
if (decoratorNodeIds.includes(node.data.id)) {
|
if (decoratorNodeIds.includes(node.data.id)) {
|
||||||
|
|
||||||
// Drag a little farther to remove it
|
// Drag a little farther to remove it
|
||||||
if (distance > minDistance + 75) {
|
if (distance > minDistance + 125) {
|
||||||
// Remove the branch? Why?
|
// Remove the branch? Why?
|
||||||
const edgeToRemove = cy.getElementById(branches[branchkey].data.id)
|
const edgeToRemove = cy.getElementById(branches[branchkey].data.id)
|
||||||
if (edgeToRemove !== null && edgeToRemove !== undefined) {
|
if (edgeToRemove !== null && edgeToRemove !== undefined) {
|
||||||
@@ -18187,8 +18189,8 @@ const AngularWorkflow = (defaultprops) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
const defaultEnvironment = environments && environments?.find(
|
const defaultEnvironment = environments.find(
|
||||||
(env) => env?.default && env?.Name?.toLowerCase() !== "cloud"
|
(env) => env.default && env.Name.toLowerCase() !== "cloud"
|
||||||
);
|
);
|
||||||
|
|
||||||
if (selectedTrigger.trigger_type === "PIPELINE" && selectedTrigger.environment === "onprem" && defaultEnvironment !== undefined) {
|
if (selectedTrigger.trigger_type === "PIPELINE" && selectedTrigger.environment === "onprem" && defaultEnvironment !== undefined) {
|
||||||
@@ -20997,7 +20999,10 @@ const AngularWorkflow = (defaultprops) => {
|
|||||||
</div>
|
</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, }}>
|
<div style={{ marginTop: 50, }}>
|
||||||
<Typography variant="body2" color="textSecondary">
|
<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.
|
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 }}
|
style={{ zIndex: 50000 }}
|
||||||
>
|
>
|
||||||
<ArrowLeftIcon style={{
|
<ArrowLeftIcon style={{
|
||||||
color: relevant_errors.length > 0 ? yellow : theme.palette.textColor,
|
color: relevant_errors.length > 0 ? red : theme.palette.textColor,
|
||||||
}} />
|
}} />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</IconButton>
|
</IconButton>
|
||||||
@@ -22783,6 +22788,26 @@ const AngularWorkflow = (defaultprops) => {
|
|||||||
</span>
|
</span>
|
||||||
: null}
|
: 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" &&
|
{data.action.app_name === "shuffle-subflow" &&
|
||||||
validate.result.success !== undefined &&
|
validate.result.success !== undefined &&
|
||||||
validate.result.success === true ? (
|
validate.result.success === true ? (
|
||||||
@@ -22944,7 +22969,7 @@ const AngularWorkflow = (defaultprops) => {
|
|||||||
>
|
>
|
||||||
<b>Action Logs</b>
|
<b>Action Logs</b>
|
||||||
</Typography>
|
</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.
|
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>
|
</Typography>
|
||||||
</div>
|
</div>
|
||||||
@@ -23002,7 +23027,9 @@ const AngularWorkflow = (defaultprops) => {
|
|||||||
variant="body1"
|
variant="body1"
|
||||||
style={{}}
|
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>
|
</Typography>
|
||||||
}
|
}
|
||||||
{open ?
|
{open ?
|
||||||
@@ -23024,8 +23051,8 @@ const AngularWorkflow = (defaultprops) => {
|
|||||||
<Typography
|
<Typography
|
||||||
variant="body2"
|
variant="body2"
|
||||||
style={{
|
style={{
|
||||||
|
marginTop: 5,
|
||||||
whiteSpace: 'pre-line',
|
whiteSpace: 'pre-line',
|
||||||
color: showlink ? "#FF8544" : theme.palette.text.primary,
|
|
||||||
cursor: showlink ? "pointer" : "default",
|
cursor: showlink ? "pointer" : "default",
|
||||||
}}
|
}}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
@@ -23035,7 +23062,7 @@ const AngularWorkflow = (defaultprops) => {
|
|||||||
window.open(data.value, "_blank")
|
window.open(data.value, "_blank")
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
color={showlink ? "inherit" : "textSecondary"}
|
color={showlink ? "#ff8544" : "textSecondary"}
|
||||||
>
|
>
|
||||||
{data.value}
|
{data.value}
|
||||||
</Typography>
|
</Typography>
|
||||||
@@ -23199,8 +23226,9 @@ const AngularWorkflow = (defaultprops) => {
|
|||||||
sx: {
|
sx: {
|
||||||
pointerEvents: "auto",
|
pointerEvents: "auto",
|
||||||
color: theme.palette.text.primary,
|
color: theme.palette.text.primary,
|
||||||
minWidth: isMobile ? "90%" : "750px",
|
minWidth: isMobile ? "90%" : 900,
|
||||||
maxHeight: "550px",
|
minHeight: 500,
|
||||||
|
maxHeight: 650,
|
||||||
overflowY: "auto",
|
overflowY: "auto",
|
||||||
overflowX: "hidden",
|
overflowX: "hidden",
|
||||||
border: theme.palette.defaultBorder,
|
border: theme.palette.defaultBorder,
|
||||||
@@ -23418,7 +23446,9 @@ const AngularWorkflow = (defaultprops) => {
|
|||||||
>
|
>
|
||||||
<b>{selectedResult.action.label.replaceAll("_", " ")}</b>
|
<b>{selectedResult.action.label.replaceAll("_", " ")}</b>
|
||||||
</div>
|
</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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -25588,12 +25618,14 @@ const AngularWorkflow = (defaultprops) => {
|
|||||||
const actionIndex = workflow?.actions.findIndex(action => action.id === actionId);
|
const actionIndex = workflow?.actions.findIndex(action => action.id === actionId);
|
||||||
if (actionIndex >= 0) {
|
if (actionIndex >= 0) {
|
||||||
// Find the parameter with matching name
|
// Find the parameter with matching name
|
||||||
console.log("fieldName", fieldName)
|
|
||||||
const paramIndex = workflow.actions[actionIndex].parameters.findIndex(param => param.name === fieldName);
|
const paramIndex = workflow.actions[actionIndex].parameters.findIndex(param => param.name === fieldName);
|
||||||
if (paramIndex >= 0) {
|
if (paramIndex >= 0) {
|
||||||
// Update the parameter value
|
// Update the parameter value
|
||||||
workflow.actions[actionIndex].parameters[paramIndex].value = newData;
|
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
|
// Update workflow state to trigger re-render
|
||||||
setWorkflow({...workflow});
|
setWorkflow({...workflow});
|
||||||
setLastSaved(false);
|
setLastSaved(false);
|
||||||
|
|||||||
@@ -233,6 +233,7 @@ const buttonBackground = "linear-gradient(to right, #f86a3e, #f34079)";
|
|||||||
const [secondaryApp, setSecondaryApp] = useState({});
|
const [secondaryApp, setSecondaryApp] = useState({});
|
||||||
const [firstRequest, setFirstRequest] = useState(true);
|
const [firstRequest, setFirstRequest] = useState(true);
|
||||||
const [publishModalOpen, setPublishModalOpen] = React.useState(false);
|
const [publishModalOpen, setPublishModalOpen] = React.useState(false);
|
||||||
|
const [showDistributionPopup, setShowDistributionPopup] = React.useState(false);
|
||||||
|
|
||||||
const [categories, setCategories] = useState(appCategories)
|
const [categories, setCategories] = useState(appCategories)
|
||||||
const [newWorkflowCategories, setNewWorkflowCategories] = React.useState([]);
|
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) {
|
if (serverside === true) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -753,11 +754,14 @@ const buttonBackground = "linear-gradient(to right, #f86a3e, #f34079)";
|
|||||||
if (action !== undefined && action !== null) {
|
if (action !== undefined && action !== null) {
|
||||||
url = `${globalUrl}/api/v1/apps/${appId}/${action}`
|
url = `${globalUrl}/api/v1/apps/${appId}/${action}`
|
||||||
}
|
}
|
||||||
fetch(url, {
|
|
||||||
|
|
||||||
|
fetch(url, {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"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",
|
credentials: "include",
|
||||||
})
|
})
|
||||||
@@ -784,7 +788,7 @@ const buttonBackground = "linear-gradient(to right, #f86a3e, #f34079)";
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (checkLogin !== undefined && checkLogin !== null) {
|
if ((checkLogin !== undefined && checkLogin !== null) && !multiple_request) {
|
||||||
checkLogin()
|
checkLogin()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -795,6 +799,9 @@ const buttonBackground = "linear-gradient(to right, #f86a3e, #f34079)";
|
|||||||
toast("App activated for your organization!")
|
toast("App activated for your organization!")
|
||||||
}
|
}
|
||||||
} else {
|
} 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>
|
</Dialog>
|
||||||
) : null;
|
) : 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 = (
|
const landingpageDataBrowser = (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
@@ -3918,6 +4131,7 @@ const buttonBackground = "linear-gradient(to right, #f86a3e, #f34079)";
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{publishModal}
|
{publishModal}
|
||||||
|
{appDistributinModal}
|
||||||
<div style={{ display: "flex", position: "relative" }}>
|
<div style={{ display: "flex", position: "relative" }}>
|
||||||
{isMobile ? null : (
|
{isMobile ? null : (
|
||||||
<Breadcrumbs
|
<Breadcrumbs
|
||||||
@@ -4066,7 +4280,7 @@ const buttonBackground = "linear-gradient(to right, #f86a3e, #f34079)";
|
|||||||
</Button>
|
</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
|
<Button
|
||||||
variant={userdata.active_apps !== undefined && userdata.active_apps !== null && userdata.active_apps.includes(appId) ? "outlined": "contained"}
|
variant={userdata.active_apps !== undefined && userdata.active_apps !== null && userdata.active_apps.includes(appId) ? "outlined": "contained"}
|
||||||
component="label"
|
component="label"
|
||||||
@@ -4168,7 +4382,8 @@ const buttonBackground = "linear-gradient(to right, #f86a3e, #f34079)";
|
|||||||
Try the API
|
Try the API
|
||||||
</Button>
|
</Button>
|
||||||
</a>
|
</a>
|
||||||
<Select
|
{app?.reference_org === userdata?.active_org?.id ? (
|
||||||
|
<Select
|
||||||
value={sharingConfiguration}
|
value={sharingConfiguration}
|
||||||
disabled={!isCloud}
|
disabled={!isCloud}
|
||||||
onChange={(event) => {
|
onChange={(event) => {
|
||||||
@@ -4223,6 +4438,11 @@ const buttonBackground = "linear-gradient(to right, #f86a3e, #f34079)";
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</Select>
|
</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>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<a
|
<a
|
||||||
|
|||||||
@@ -315,7 +315,7 @@ const RadialChart = ({keys, setSelectedCategory}) => {
|
|||||||
// What data do we fill in here? Idk
|
// What data do we fill in here? Idk
|
||||||
const Dashboard = (props) => {
|
const Dashboard = (props) => {
|
||||||
|
|
||||||
const { globalUrl, isLoggedIn } = props;
|
const { globalUrl, userdata, isLoggedIn } = props;
|
||||||
//const alert = useAlert();
|
//const alert = useAlert();
|
||||||
const [bigChartData, setBgChartData] = useState("data1");
|
const [bigChartData, setBgChartData] = useState("data1");
|
||||||
const [dayAmount, setDayAmount] = useState(7);
|
const [dayAmount, setDayAmount] = useState(7);
|
||||||
@@ -323,10 +323,10 @@ const Dashboard = (props) => {
|
|||||||
const [stats, setStats] = useState({});
|
const [stats, setStats] = useState({});
|
||||||
const [changeme, setChangeme] = useState("");
|
const [changeme, setChangeme] = useState("");
|
||||||
const [statsRan, setStatsRan] = useState(false);
|
const [statsRan, setStatsRan] = useState(false);
|
||||||
const [keys, setKeys] = useState([])
|
const [keys, setKeys] = useState([])
|
||||||
const [treeKeys, setTreeKeys] = useState([])
|
const [treeKeys, setTreeKeys] = useState([])
|
||||||
|
|
||||||
const [selectedUsecaseCategory, setSelectedUsecaseCategory] = useState("");
|
const [selectedUsecaseCategory, setSelectedUsecaseCategory] = useState("")
|
||||||
const [selectedUsecases, setSelectedUsecases] = useState([]);
|
const [selectedUsecases, setSelectedUsecases] = useState([]);
|
||||||
const [usecases, setUsecases] = useState([]);
|
const [usecases, setUsecases] = useState([]);
|
||||||
const [workflows, setWorkflows] = useState([]);
|
const [workflows, setWorkflows] = useState([]);
|
||||||
@@ -750,8 +750,6 @@ const Dashboard = (props) => {
|
|||||||
|
|
||||||
const newname = data.key !== undefined ? data.key.replaceAll("_", " ") : ""
|
const newname = data.key !== undefined ? data.key.replaceAll("_", " ") : ""
|
||||||
|
|
||||||
console.log("KEYDATA: ", data)
|
|
||||||
|
|
||||||
const loadNewStats = (newkey) => {
|
const loadNewStats = (newkey) => {
|
||||||
const resp = LoadStats(globalUrl, newkey)
|
const resp = LoadStats(globalUrl, newkey)
|
||||||
if (resp !== undefined) {
|
if (resp !== undefined) {
|
||||||
@@ -807,7 +805,7 @@ const Dashboard = (props) => {
|
|||||||
backgroundColor: theme.palette.inputColor,
|
backgroundColor: theme.palette.inputColor,
|
||||||
color: "white",
|
color: "white",
|
||||||
height: 40,
|
height: 40,
|
||||||
maxWidth: 150,
|
maxWidth: 200,
|
||||||
borderRadius: theme.palette?.borderRadius,
|
borderRadius: theme.palette?.borderRadius,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -829,10 +827,12 @@ const Dashboard = (props) => {
|
|||||||
</Select>
|
</Select>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<DashboardBarchart
|
<DashboardBarchart
|
||||||
timelineData={data}
|
timelineData={data}
|
||||||
height={50}
|
height={50}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
</Paper>
|
</Paper>
|
||||||
</Draggable>
|
</Draggable>
|
||||||
)
|
)
|
||||||
@@ -848,14 +848,6 @@ const Dashboard = (props) => {
|
|||||||
: null}
|
: null}
|
||||||
</div>
|
</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 === undefined || newWidgetData === null || newWidgetData === [] ? null :
|
||||||
newWidgetData.map((data, index) => {
|
newWidgetData.map((data, index) => {
|
||||||
|
|
||||||
@@ -872,7 +864,9 @@ const Dashboard = (props) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const dataWrapper = (
|
const dataWrapper = (
|
||||||
<div style={{ maxWidth: 1366, margin: "auto", paddingTop: 10, }}>{data}</div>
|
<div style={{ maxWidth: 1366, margin: "auto", paddingTop: 10, }}>
|
||||||
|
{data}
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
return dataWrapper;
|
return dataWrapper;
|
||||||
|
|||||||
@@ -558,7 +558,7 @@ export const handleReactJsonClipboard = (copy) => {
|
|||||||
document.execCommand("copy");
|
document.execCommand("copy");
|
||||||
|
|
||||||
console.log("COPYING!");
|
console.log("COPYING!");
|
||||||
toast("Copied value to clipboard, NOT json path.")
|
toast.success("Copied Value, NOT json path.")
|
||||||
} else {
|
} else {
|
||||||
console.log("Failed to copy from " + elementName + ": ", copyText);
|
console.log("Failed to copy from " + elementName + ": ", copyText);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user