Merge branch 'nightly' of https://github.com/Shuffle/Shuffle into release-changes

This commit is contained in:
lalitdeore12@gmail.com
2025-06-03 13:54:30 +05:30
44 changed files with 2818 additions and 1209 deletions
+1 -1
View File
@@ -468,7 +468,7 @@ const App = (message, props) => {
<Route
exact
path="/AdminSetup"
path="/adminsetup"
element={
<AdminSetup
isLoaded={isLoaded}
+9 -1
View File
@@ -208,9 +208,17 @@ const AdminNavBar = (props) => {
const ComponentToRender = selectedItemData.component;
const componentProps = selectedItemData.props;
return <ComponentToRender {...componentProps} />;
const updatedProps = {
...componentProps,
notifications: notifications,
setNotifications: setNotifications,
userdata: userdata,
selectedOrganization: selectedOrganization
};
return <ComponentToRender {...updatedProps} />;
};
const defaultImage = "/images/logos/orange_logo.svg"
const imageData =
selectedOrganization?.image === undefined || selectedOrganization?.image.length === 0
+42 -1
View File
@@ -66,7 +66,7 @@ import { Context } from '../context/ContextApi.jsx';
const searchClient = algoliasearch(
"JNSS5CFDZZ",
"db08e40265e2941b9a7d8f644b6e5240"
"c8f882473ff42d41158430be09ec2b4e"
)
const AppAuthTab = memo((props) => {
@@ -1181,6 +1181,47 @@ const AppAuthTab = memo((props) => {
</IconButton>
</Tooltip>
)}
<Tooltip
title={"Copy Auth ID"}
style={{}}
aria-label={"copy"}
>
<IconButton
style = {{padding: "6px"}}
onClick={() => {
navigator.clipboard.writeText(data.id);
document.execCommand("copy");
toast(data.id + " copied to clipboard");
}}
>
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<rect
width="24"
height="24"
fillOpacity="1"
/>
<path
d="M14 4H7.6C7.17565 4 6.76869 4.16857 6.46863 4.46863C6.16857 4.76869 6 5.17565 6 5.6V18.4C6 18.8243 6.16857 19.2313 6.46863 19.5314C6.76869 19.8314 7.17565 20 7.6 20H17.2C17.6243 20 18.0313 19.8314 18.3314 19.5314C18.6314 19.2313 18.8 18.8243 18.8 18.4V8.8L14 4Z"
stroke={themeMode === "dark" ? "#F1F1F1" : "#333"}
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M14 4V8.8H18.8"
stroke={themeMode === "dark" ? "#F1F1F1" : "#333"}
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
</IconButton>
</Tooltip>
<IconButton
style={{ }}
disabled={data.org_id !== selectedOrganization.id}
+1 -1
View File
@@ -53,7 +53,7 @@ import {
const searchClient = algoliasearch(
"JNSS5CFDZZ",
"db08e40265e2941b9a7d8f644b6e5240"
"c8f882473ff42d41158430be09ec2b4e"
);
//const searchClient = algoliasearch("L55H18ZINA", "a19be455e7e75ee8f20a93d26b9fc6d6")
+1 -1
View File
@@ -35,7 +35,7 @@ import { Context } from '../context/ContextApi.jsx';
const searchClient = algoliasearch(
"JNSS5CFDZZ",
"db08e40265e2941b9a7d8f644b6e5240"
"c8f882473ff42d41158430be09ec2b4e"
);;
const AppModal = ({ open, onClose, app, globalUrl, getApps}) => {
+1 -1
View File
@@ -13,7 +13,7 @@ import {
InputAdornment,
Typography,
} from '@mui/material';
const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240")
const searchClient = algoliasearch("JNSS5CFDZZ", "c8f882473ff42d41158430be09ec2b4e")
const Appsearch = props => {
const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs, newSelectedApp, setNewSelectedApp, defaultSearch, showSearch, ConfiguredHits, userdata, cy, isCreatorPage, actionImageList, setActionImageList, setUserSpecialzedApp, placeholder,
+1 -1
View File
@@ -20,7 +20,7 @@ import {
} from '@mui/material';
import aa from 'search-insights'
const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240")
const searchClient = algoliasearch("JNSS5CFDZZ", "c8f882473ff42d41158430be09ec2b4e")
const Appsearch = props => {
const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs, newSelectedApp, setNewSelectedApp, defaultSearch, showSearch, ConfiguredHits, userdata, cy, isCreatorPage, actionImageList, setActionImageList, setUserSpecialzedApp } = props
const { themeMode } = useContext(Context)
+76 -51
View File
@@ -52,11 +52,13 @@ import {
//import { useAlert
import { typecost, typecost_single, } from "../views/HandlePaymentNew.jsx";
import BillingStats from "./BillingStats.jsx";
import BillingStats from "../components/BillingStats.jsx";
import LicencePopup from "../components/LicencePopup.jsx";
import { handlePayasyougo } from "../views/HandlePaymentNew.jsx"
import DeleteIcon from '@mui/icons-material/Delete';
import { Context } from "../context/ContextApi.jsx";
import LicencePopup from "./LicencePopup.jsx";
import DeleteIcon from '@mui/icons-material/Delete';
import { DataGrid } from "@mui/x-data-grid";
const Billing = memo((props) => {
@@ -2597,64 +2599,87 @@ const Billing = memo((props) => {
Utilization & Stats
</Typography>
</div>
{isChildOrg ? (
<BillingStats
isCloud={isCloud}
clickedFromOrgTab={clickedFromOrgTab}
globalUrl={globalUrl}
selectedOrganization={selectedOrganization}
userdata={userdata}
/>
): (
<>
<span>
<Tabs
value={currentTab}
onChange={(event, newValue) => setCurrentTab(newValue)}
onChange={(event, newValue) => {
setCurrentTab(-1)
// Force re-render
setTimeout(() => {
setCurrentTab(newValue)
}, 100);
}}
style={{ marginTop: 20 }}
TabIndicatorProps={{
style: {
height: 3,
backgroundColor: theme.palette.primary.main,
marginLeft: 12,
marginRight: 12,
height: 3,
backgroundColor: theme.palette.primary.main,
marginLeft: 12,
marginRight: 12,
}
}}
>
<Tab
label="Parent Organization"
style={{ textTransform: 'none', fontSize: 16, minWidth: 'auto', paddingLeft: 12, paddingRight: 12 }}
/>
<Tab
label="Child Organization"
style={{ textTransform: 'none', fontSize: 16, minWidth: 'auto', paddingLeft: 12, paddingRight: 12 }}
/>
<Tab
label="Parent Organization"
style={{ textTransform: 'none',}}
value={0}
/>
{isCloud ?
<Tab
label="Cloud-Synced Stats"
style={{ textTransform: 'none', }}
value={1}
/>
: null}
<Tab
label="Child Organization Stats"
disabled={isChildOrg}
style={{ textTransform: 'none', }}
value={2}
/>
</Tabs>
{currentTab === 0 ? (
<div style={{ marginTop: 30,}}>
<BillingStats
isCloud={isCloud}
clickedFromOrgTab={clickedFromOrgTab}
globalUrl={globalUrl}
selectedOrganization={selectedOrganization}
userdata={userdata}
/>
</div>
): (
<BillingStatsChildOrg
isCloud={isCloud}
clickedFromOrgTab={clickedFromOrgTab}
globalUrl={globalUrl}
selectedOrganization={selectedOrganization}
userdata={userdata}
allChildOrgs={allChildOrgs}
setAllChildOrgs={setAllChildOrgs}
allChildOrgsStats={allChildOrgsStats}
setAllChildOrgsStats={setAllChildOrgsStats}
/>
)}
</>
)}
<div style={{paddingBottom: 200, minHeight: 750, }}>
{currentTab === 0 ?
<div style={{ marginTop: 30,}}>
<BillingStats
isCloud={isCloud}
clickedFromOrgTab={clickedFromOrgTab}
globalUrl={globalUrl}
selectedOrganization={selectedOrganization}
userdata={userdata}
/>
</div>
: currentTab === 1 ?
<div style={{ marginTop: 30,}}>
<BillingStats
isCloud={isCloud}
clickedFromOrgTab={clickedFromOrgTab}
globalUrl={globalUrl}
selectedOrganization={selectedOrganization}
userdata={userdata}
syncStats={true}
/>
</div>
:
<BillingStatsChildOrg
isCloud={isCloud}
clickedFromOrgTab={clickedFromOrgTab}
globalUrl={globalUrl}
selectedOrganization={selectedOrganization}
userdata={userdata}
allChildOrgs={allChildOrgs}
setAllChildOrgs={setAllChildOrgs}
allChildOrgsStats={allChildOrgsStats}
setAllChildOrgsStats={setAllChildOrgsStats}
/>
}
</div>
</span>
</div>
</Wrapper>
)
+181 -104
View File
@@ -33,8 +33,15 @@ import {
import {
BarChart,
BarSeries,
Bar,
BarLabel,
GridlineSeries,
Gridline,
TooltipArea,
ChartTooltip,
TooltipTemplate,
} from 'reaviz';
import { typecost, typecost_single, } from "../views/HandlePaymentNew.jsx";
@@ -45,31 +52,52 @@ const LineChartWrapper = ({keys, inputname, height, width}) => {
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: 15, }}>
<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 { globalUrl, selectedOrganization, userdata, isCloud, inputWorkflows,clickedFromOrgTab } = defaultprops;
const {
globalUrl,
selectedOrganization,
userdata,
isCloud,
inputWorkflows,
clickedFromOrgTab,
syncStats,
} = defaultprops;
const [keys, setKeys] = useState([])
const [searches, setSearches] = useState([]);
const [appRuns, setAppruns] = useState(undefined);
const [childOrgsAppRuns, setChildOrgsAppRuns] = useState(undefined);
const [appRunCosts, setApprunCosts] = useState(undefined);
const [workflowRuns, setWorkflowRuns] = useState(undefined);
const [subflowRuns, setSubflowRuns] = useState(undefined);
@@ -99,9 +127,6 @@ const AppStats = (defaultprops) => {
const getWorkflowStats = async (workflow, startTime, endTime) => {
if (!userdata.support) {
return workflow
}
if (workflow.id === undefined || workflow.id === null || workflow.id === "") {
return workflow
@@ -166,12 +191,8 @@ const AppStats = (defaultprops) => {
}
const loadWorkflowStats = (foundWorkflows, startTime, endTime) => {
if (!userdata.support) {
return
}
if (foundWorkflows === undefined || foundWorkflows === null || foundWorkflows.length === 0) {
console.log("Not workflows")
setResultLoading(false)
return
}
@@ -180,6 +201,9 @@ const AppStats = (defaultprops) => {
const promises = foundWorkflows.slice(0, 50).map(wf => getWorkflowStats(wf, startTime, endTime));
const allData = Promise.all(promises);
if (allData === undefined || allData === null) {
setResultLoading(false)
}
allData.then((data) => {
var total = 0
@@ -239,15 +263,16 @@ const AppStats = (defaultprops) => {
return
}
if (statistics["daily_statistics"] === undefined || statistics["daily_statistics"] === null) {
const statKey = syncStats === true ? "onprem_stats" : "daily_statistics"
if (statistics[statKey] === undefined || statistics[statKey] === null) {
setFilteredStatistics(statistics)
return
}
// Calculate month to date cost
var mtd_cost = 0
for (let key in statistics["daily_statistics"]) {
const item = statistics["daily_statistics"][key]
for (let key in statistics[statKey]) {
const item = statistics[statKey][key]
if (item["date"] === undefined) {
continue
}
@@ -305,8 +330,8 @@ const AppStats = (defaultprops) => {
// Check if start time is before the daily statistics["date"] string
var newlist = []
for (let key in statistics["daily_statistics"]) {
const item = statistics["daily_statistics"][key]
for (let key in statistics[statKey]) {
const item = statistics[statKey][key]
if (item["date"] === undefined) {
continue
}
@@ -337,7 +362,7 @@ const AppStats = (defaultprops) => {
var appexecutions = 0
var estimatedcost = 0
if (newlist.length > 0) {
tmpstats["daily_statistics"] = newlist
tmpstats[statKey] = newlist
for (let key in newlist) {
const item = newlist[key]
@@ -391,7 +416,8 @@ const AppStats = (defaultprops) => {
return
}
const dailyStats = inputdata.daily_statistics
const statKey = syncStats === true ? "onprem_stats" : "daily_statistics"
const dailyStats = inputdata[statKey]
if (dailyStats === undefined || dailyStats === null) {
return
}
@@ -401,6 +427,11 @@ const AppStats = (defaultprops) => {
"data": []
}
var childorgappRuns = {
"key": "Child Org App Runs",
"data": []
}
var workflowRuns = {
"key": "Workflow Runs (includes subflows)",
"data": []
@@ -442,6 +473,13 @@ const AppStats = (defaultprops) => {
})
}
if (item["child_app_executions"] !== undefined && item["child_app_executions"] !== null) {
childorgappRuns["data"].push({
key: new Date(item["date"]),
data: inputdata["child_app_executions"]
})
}
// Check if workflow_executions key in item
if (item["workflow_executions"] !== undefined && item["workflow_executions"] !== null) {
workflowRuns["data"].push({
@@ -471,6 +509,15 @@ const AppStats = (defaultprops) => {
})
}
if (inputdata["daily_child_app_executions"] !== undefined && inputdata["daily_app_executions"] !== null) {
childorgappRuns["data"].push({
key: new Date(),
data: inputdata["daily_child_app_executions"]
})
//setApprunCosts(appcostRuns)
}
if (inputdata["daily_workflow_executions"] !== undefined && inputdata["daily_workflow_executions"] !== null) {
workflowRuns["data"].push({
key: new Date(),
@@ -485,6 +532,11 @@ const AppStats = (defaultprops) => {
})
}
// Only for parent orgs
if (childorgappRuns["data"].length > 0) {
setChildOrgsAppRuns(childorgappRuns)
}
setSubflowRuns(subflowRuns)
setWorkflowRuns(workflowRuns)
setAppruns(appRuns)
@@ -659,44 +711,57 @@ const AppStats = (defaultprops) => {
style={{ textDecoration: "none", color: theme.palette.linkColor,}}
>Your Organisation Statistics. </a>
It exists to give you more insight into your workflows, and to understand your utilization of the Shuffle platform. <b>The billing tracker is in Beta, and is always calculated manually before being invoiced.</b>
<br style={{}}/>
{syncStats !== true ? null :
"PS: You are currently looking at data from your onprem synced org"}
</Typography>
<div style={{display: "flex", flexDirection: "column", textAlign: "center",}}>
<div style={{flexDirection: "row", }}>
{filteredStatistics !== undefined ?
<div style={{flex: 1, display: "flex", textAlign: "center",}}>
<Tooltip title={
<Typography variant="body1" style={{padding: 10, }}>
The cost of app runs in the selected period based on {filteredStatistics.monthly_app_executions} App Runs. These numbers do not exclude your included 10.000/month or {includedExecutions} App Runs per month. App Run cost: ${invocationCost}.
</Typography>
}>
<Box sx={paperStyle}>
<Typography variant="h4">
${selectedOrganization?.lead_info?.customer === false && selectedOrganization?.lead_info?.pov === false ?
0
:
apprunCost
}
{syncStats == true ? null :
<Tooltip title={
<Typography variant="body1" style={{padding: 10, }}>
The cost of app runs in the selected period based on {filteredStatistics.monthly_app_executions} App Runs. These numbers do not exclude your included 10.000/month or {includedExecutions} App Runs per month. App Run cost: ${invocationCost}.
</Typography>
<Typography variant="h6">
Period Cost
</Typography>
</Box>
</Tooltip>
}>
<Box sx={paperStyle}>
<Typography variant="h4">
${selectedOrganization?.lead_info?.customer === false && selectedOrganization?.lead_info?.pov === false ?
0
:
apprunCost
}
</Typography>
<Typography variant="h6">
Period Cost
</Typography>
</Box>
</Tooltip>
}
{syncStats === true ? null :
<Tooltip title={
<Typography variant="body1" style={{padding: 10, }}>
App runs in the selected period
</Typography>
}>
<Box sx={paperStyle}>
<Typography variant="h4">
{filteredStatistics.monthly_app_executions === null || filteredStatistics.monthly_app_executions === undefined ? 0 : filteredStatistics.monthly_app_executions}
</Typography>
<Typography variant="h6">
App Runs
</Typography>
</Box>
<Box sx={paperStyle}>
<Typography variant="h4">
{filteredStatistics.monthly_app_executions === null || filteredStatistics.monthly_app_executions === undefined ? 0 : filteredStatistics.monthly_app_executions}
</Typography>
<Typography variant="h6">
App Runs
</Typography>
</Box>
</Tooltip>
}
{syncStats === true ? null :
<Tooltip title={
<Typography variant="body1" style={{padding: 10, }}>
Workflow runs in the selected period
@@ -711,20 +776,24 @@ const AppStats = (defaultprops) => {
</Typography>
</Box>
</Tooltip>
<Tooltip title={
<Typography variant="body1" style={{padding: 10, }}>
Estimated cost to be billed at the end of the current month. Subtracted contractually included app runs. Actual cost month to date: ${monthToDateCost}. App Run cost: ${invocationCost}.
</Typography>
}>
<Box sx={paperStyle}>
<Typography variant="h4">
${monthTotalCost}
}
{syncStats === true ? null :
<Tooltip title={
<Typography variant="body1" style={{padding: 10, }}>
Estimated cost to be billed at the end of the current month. Subtracted contractually included app runs. Actual cost month to date: ${monthToDateCost}. App Run cost: ${invocationCost}.
</Typography>
<Typography variant="h6">
Estimated cost
</Typography>
</Box>
</Tooltip>
}>
<Box sx={paperStyle}>
<Typography variant="h4">
${monthTotalCost}
</Typography>
<Typography variant="h6">
Estimated cost
</Typography>
</Box>
</Tooltip>
}
</div>
: null}
</div>
@@ -895,7 +964,13 @@ const AppStats = (defaultprops) => {
{appRuns === undefined ?
null
:
<LineChartWrapper keys={appRuns} height={300} width={"100%"} inputname={"Daily App Runs"}/>
<LineChartWrapper keys={appRuns} height={300} width={"100%"} inputname={"App Runs - Current Org"}/>
}
{childOrgsAppRuns === undefined ?
null
:
<LineChartWrapper keys={childOrgsAppRuns} height={300} width={"100%"} inputname={"Child Org App Runs"}/>
}
{workflowRuns === undefined ?
@@ -916,56 +991,58 @@ const AppStats = (defaultprops) => {
<LineChartWrapper keys={appRunCosts} height={300} width={"100%"} inputname={"Apprun cost - Cost per day"}/>
*/}
{syncStats === true ? null :
<div style={{height: 150+resultRows.length * 25, padding: "10px 0px 10px 0px", }}>
{resultLoading ?
<div style={{margin: "auto", alignItems: "center", width: 350, height: "100%", }}>
<Typography variant="body2" color="textSecondary" component="p" style={{textAlign: "center", marginTop: 50, marginBottom: 15, }}>
Loading usage for selected period (may take a while)
<CircularProgress style={{marginTop: 15, }} />
</Typography>
</div>
:
<DataGrid
rows={resultRows}
columns={columns}
pageSize={100}
rowsPerPageOptions={[10, 20, 50, 100]}
checkboxSelection
disableSelectionOnClick
onPageSizeChange={(newPageSize) => {
//setRowsPerPage(newPageSize)
//submitSearch(workflowId, status, startTime, endTime, rowCursor, newPageSize)
}}
// event for when clicking next page
// Hide page changer
onPageChange={(params) => {
console.log("page params: ", params)
}}
onSelectionModelChange={(newSelection) => {
console.log("newSelection: ", newSelection)
//console.log("newSelection: ", newSelection)
//setSelectedWorkflowExecutionsIndexes(newSelection)
//var found = []
//for (var i = 0; i < newSelection.length; i++) {
// // Find the workflow in the resultRows
// var selected = resultRows.find((workflow) => {
// return workflow.id === newSelection[i]
// })
<div style={{height: 150+resultRows.length * 25, padding: "10px 0px 10px 0px", }}>
{resultLoading ?
<div style={{margin: "auto", alignItems: "center", width: 350, height: "100%", }}>
<Typography variant="body2" color="textSecondary" component="p" style={{textAlign: "center", marginTop: 50, marginBottom: 15, }}>
Loading usage for selected period (may take a while)
</Typography>
<CircularProgress style={{}} />
</div>
:
<DataGrid
rows={resultRows}
columns={columns}
pageSize={100}
rowsPerPageOptions={[10, 20, 50, 100]}
checkboxSelection
disableSelectionOnClick
onPageSizeChange={(newPageSize) => {
//setRowsPerPage(newPageSize)
//submitSearch(workflowId, status, startTime, endTime, rowCursor, newPageSize)
}}
// event for when clicking next page
// Hide page changer
onPageChange={(params) => {
console.log("page params: ", params)
}}
onSelectionModelChange={(newSelection) => {
console.log("newSelection: ", newSelection)
//console.log("newSelection: ", newSelection)
//setSelectedWorkflowExecutionsIndexes(newSelection)
//var found = []
//for (var i = 0; i < newSelection.length; i++) {
// // Find the workflow in the resultRows
// var selected = resultRows.find((workflow) => {
// return workflow.id === newSelection[i]
// })
// if (selected === undefined || selected === null) {
// continue
// }
// if (selected === undefined || selected === null) {
// continue
// }
// found.push(selected)
//}
// found.push(selected)
//}
//setSelectedWorkflowExecutions(found)
}}
// Track which items are selected
/>
}
</div>
//setSelectedWorkflowExecutions(found)
}}
// Track which items are selected
/>
}
</div>
}
</div>
)
+2 -2
View File
@@ -39,7 +39,7 @@ const Branding = (props) => {
const theme = getTheme(themeMode, brandColor)
const [selectedBrandColor, setSelectedBrandColor] = useState(theme?.palette?.main || "#FF8544")
const [selectedBrandName, setSelectedBrandName] = useState(selectedOrganization?.branding?.brand_name || "")
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
const [isLoading,setIsLoading] = useState(false);
const handleEditOrg = (joinStatus) => {
@@ -391,7 +391,7 @@ const Branding = (props) => {
</div>
{ integrationPartner ? (
{integrationPartner ? (
<>
<Divider style={{marginTop: 50, marginBottom: 50, color: theme.palette.defaultBorder}} />
<Typography style={{fontSize: 24, fontWeight: "bold"}}>
+62 -2
View File
@@ -867,7 +867,7 @@ const CacheView = memo((props) => {
overflowX: "auto",
}}>
<ListItem style={{width: isSelectedDataStore?"100%":null, borderBottom:isSelectedDataStore? theme.palette.defaultBorder :null, display: "table-row"}}>
{["Key", "Value", "Actions", "Updated", "Distribution"].map((header, index) => (
{["Key", "Value", "workflow", "Actions", "Updated", "Distribution"].map((header, index) => (
<ListItemText
key={index}
primary={header}
@@ -891,7 +891,7 @@ const CacheView = memo((props) => {
backgroundColor: theme.palette.platformColor,
}}
>
{Array(5)
{Array(6)
.fill()
.map((_, colIndex) => (
<ListItemText
@@ -1001,6 +1001,66 @@ const CacheView = memo((props) => {
data.value
}
/>
<ListItemText
primary={
data.workflow_id === "" || data.workflow_id === null || data.workflow_id === undefined ?
<IconButton
disabled={data.workflow_id?.length === 0}
style={{marginLeft: 10}}
>
<OpenInNewIcon
style={{
color:
data.workflow_id?.length !== 0
? "#FF8444"
: "grey",
}}
/>
</IconButton>
: (
<Tooltip
title={"Go to workflow"}
style={{}}
aria-label={"Download"}
>
<span>
<a
rel="noopener noreferrer"
style={{
textDecoration: "none",
color: "#f85a3e",
}}
href={`/workflows/${data.workflow_id}`}
target="_blank"
>
<IconButton
disabled={data.workflow_id?.length ===0}
style={{marginLeft: 10}}
>
<OpenInNewIcon
style={{
width: 24, height: 24,
color:
data.workflow_id?.length !== 0
? "#FF8444"
: "grey",
}}
/>
</IconButton>
</a>
</span>
</Tooltip>
)
}
style={{
display: "table-cell",
overflow: "hidden",
verticalAlign: "middle",
padding: "8px 8px 8px 15px",
maxWidth: 200,
overflowX: "auto",
}}
/>
<ListItemText
style={{
display: "table-cell",
+23 -6
View File
@@ -48,15 +48,21 @@ const CloudSyncTab = (props) => {
const [, forceUpdate] = React.useState();
const itemColor = "white";
const isCloud = window?.location?.host === "localhost:3002" || window?.location?.host === "shuffler.io";
const { themeMode, brandColor } = useContext(Context);
const theme = getTheme(themeMode, brandColor);
useEffect(() => { getSettings(); }, []);
const GridItem = (props) => {
const [expanded, setExpanded] = React.useState(false);
const [showEdit, setShowEdit] = React.useState(false);
const [newValue, setNewValue] = React.useState(-100);
const primary = props.data.primary;
var primary = props.data.primary
const shownName = props.data.newname !== undefined && props.data.newname !== null && props.data.newname !== primary ? props.data.newname : primary
const secondary = props.data.secondary;
const primaryIcon = props.data.icon;
const secondaryIcon = props.data.active ?
@@ -191,7 +197,7 @@ const CloudSyncTab = (props) => {
</ListItemAvatar>
<ListItemText
style={{ textTransform: "capitalize", color: theme.palette.text.primary, fontSize: 14, fontWeight: 400, }}
primary={primary}
primary={shownName}
/>
{isCloud && userdata.support === true ?
<Tooltip title="Edit features (support users only)">
@@ -477,7 +483,7 @@ const CloudSyncTab = (props) => {
} else {
toast("Cloud Syncronization successfully set up!");
setOrgSyncResponse(
"Successfully started syncronization. Cloud features you now have access to can be seen below."
"Successfully started syncronization. Cloud/Hybrid features are available below."
);
}
@@ -535,8 +541,8 @@ const CloudSyncTab = (props) => {
Cloud syncronization
</Typography>
<Typography variant="body2" style={{ color: theme.palette.text.secondary, fontSize: 16, fontWeight: 400, }}>
What does <a href="/docs/organizations#cloud_sync" target="_blank" rel="noopener noreferrer" style={{ color: theme.palette.linkColor, fontSize: 16, textDecoration: 'none', }}>cloud sync</a> do? Cloud synchronization is a way of getting more out of Shuffle. Shuffle will ALWAYS make every option open source, but features relying on other users can't be done without a collaborative approach.
</Typography>
What does <a href="/docs/organizations#cloud_sync" target="_blank" rel="noopener noreferrer" style={{ color: theme.palette.linkColor, fontSize: 16, textDecoration: 'none', }}>cloud sync</a> do? Cloud synchronization is a way of getting more out of Shuffle. Shuffle will ALWAYS make every option open source, but features relying on other users can't be done without a collaborative approach. This will by default back up apps and workflows.
</Typography>
</div>
{isCloud ? (
@@ -708,7 +714,7 @@ const CloudSyncTab = (props) => {
)}
<Typography variant="h5" style={{ marginLeft: 5, marginTop: 40, marginBottom: 5 }}>
Features
{isCloud ? "Cloud" : "Hybrid"} Features
</Typography>
<Typography variant="body2" color="textSecondary" style={{ marginBottom: 24, fontSize: 16, fontWeight: 400, marginLeft: 5, color: theme.palette.text.secondary }}>
Features and Limitations that are currently available to you in your Cloud or Hybrid Organization. App Executions (App Runs) reset monthly. If the organization is a customer or in a trial, these features limitations are not always enforced. </Typography>
@@ -717,7 +723,9 @@ const CloudSyncTab = (props) => {
{selectedOrganization.sync_features === undefined ||
selectedOrganization.sync_features === null
? <Grid container spacing={2} justifyContent="center">
{[...Array(18)].map((_, i) => (
<Grid item xs={12} sm={6} md={4} key={i}>
<div
style={{
@@ -756,6 +764,13 @@ const CloudSyncTab = (props) => {
}
const newkey = key.replaceAll("_", " ");
// Rewrites to frontend names
var newname = newkey
if (newkey === "app executions") {
newname = "app runs"
}
const griditem = {
primary: newkey,
secondary:
@@ -770,6 +785,8 @@ const CloudSyncTab = (props) => {
data_collection: "None",
active: item.active,
icon: <PolylineIcon style={{ color: "#1a1a1a" }} />,
newname: newname,
};
return (
@@ -633,7 +633,7 @@ const ConfigureWorkflow = (props) => {
if (aa !== undefined) {
aa('init', {
appId: "JNSS5CFDZZ",
apiKey: "db08e40265e2941b9a7d8f644b6e5240",
apiKey: "c8f882473ff42d41158430be09ec2b4e",
})
const timestamp = new Date().getTime()
+1 -1
View File
@@ -37,7 +37,7 @@ import {
AvatarGroup,
} from "@mui/material"
const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240")
const searchClient = algoliasearch("JNSS5CFDZZ", "c8f882473ff42d41158430be09ec2b4e")
const CreatorGrid = props => {
const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs, isHeader } = props
const rowHandler = maxRows === undefined || maxRows === null ? 50 : maxRows
+1 -1
View File
@@ -29,7 +29,7 @@ import {
const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240")
const searchClient = algoliasearch("JNSS5CFDZZ", "c8f882473ff42d41158430be09ec2b4e")
const DocsGrid = props => {
const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs, userdata, } = props
const rowHandler = maxRows === undefined || maxRows === null ? 50 : maxRows
+4 -4
View File
@@ -188,7 +188,7 @@ const EditWorkflow = (props) => {
}
const newWorkflow = isEditing === true ? false : true
const priority = userdata === undefined || userdata === null ? null : userdata.priorities.find(prio => prio.type === "usecase" && prio.active === true)
const priority = userdata === undefined || userdata === null || userdata.priorities === null || userdata.priorities === undefined ? null : userdata?.priorities?.find(prio => prio.type === "usecase" && prio.active === true)
var upload = "";
var total_count = 0
@@ -226,8 +226,8 @@ const EditWorkflow = (props) => {
<div style={{ display: "flex" }}>
<div style={{ flex: 1, color: theme.palette.textColor }}>
<div style={{ display: "flex" }}>
<Typography variant="h4" style={{ flex: 9, }}>
{newWorkflow ? "New" : "Editing"} workflow
<Typography variant="h4" style={{ flex: 9, marginTop: 25, }}>
{newWorkflow ? "New" : "Editing"} Workflow
</Typography>
{newWorkflow === true ? null :
@@ -393,7 +393,7 @@ const EditWorkflow = (props) => {
</Button>
</div>
<DialogContent style={{ paddingTop: 10, display: "flex", minHeight: 300, zIndex: 1001, paddingBottom: 200, paddingLeft: "50px" }}>
<DialogContent style={{ paddingTop: 10, display: "flex", minHeight: 300, zIndex: 1001, paddingBottom: 400, paddingLeft: 50, }}>
<div style={{ minWidth: newWorkflow ? 500 : 550, maxWidth: newWorkflow ? 450 : 500, }}>
<TextField
onChange={(event) => {
+15 -13
View File
@@ -403,7 +403,6 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
const handleChangeTheme = (newTheme) => {
toast.info("Changing theme to " + newTheme + " - please wait!");
const data = {
"org_id": userdata?.active_org?.id,
@@ -449,8 +448,11 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
});
};
const handleUpdateTheme = (newTheme) => {
handleThemeChange(newTheme);
setCurrentSelectedTheme(newTheme)
const data = {
"user_id": userdata?.id,
"theme": newTheme,
@@ -470,14 +472,11 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
}).then((response) =>
response.json().then((responseJson) => {
if (responseJson["success"] === false) {
toast("Failed updating theme: ", responseJson.reason);
} else {
handleThemeChange(newTheme);
setCurrentSelectedTheme(newTheme);
toast("Failed saving your theme: ", responseJson.reason);
}
})
).catch((error) => {
console.log("Error changing theme: ", error);
console.log("Error saving your theme: ", error);
});
};
@@ -518,6 +517,7 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
console.error("Logout error:", error);
});
};
const avatarMenu = (
<span>
<IconButton
@@ -588,7 +588,8 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
}
if (userdata?.org_status?.includes("integration_partner")){
handleChangeTheme(newTheme);
}else {
} else {
handleUpdateTheme(newTheme);
}
}}
@@ -646,7 +647,7 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
<Divider style={{ marginTop: 10, marginBottom: 10, }} />
<Link to="/docs" style={hrefStyle}>
<Link to={userdata && userdata?.org_status?.includes("integration_partner") && userdata?.active_org?.branding?.documentation_link?.length > 0 ? userdata?.active_org?.branding?.documentation_link : "/docs" } target={userdata?.active_org?.branding?.documentation_link?.length > 0 && userdata?.org_status?.includes("integration_partner") ? "_blank" : "_self" } style={hrefStyle}>
<MenuItem
onClick={(event) => {
handleClose();
@@ -670,7 +671,7 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
<Divider style={{ marginBottom: 10, }} />
<Typography color="textSecondary" align="center" style={{ marginTop: 5, marginBottom: 5, fontSize: 18 }}>
Version: 2.0.2
Version: 2.1.0-rc1
</Typography>
</Menu>
</span>
@@ -1632,7 +1633,8 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
<Button
component={Link}
to="/docs"
to={userdata?.org_status?.includes("integration_partner") && userdata?.active_org?.branding?.documentation_link?.length > 0 ? userdata?.active_org?.branding?.documentation_link : "/docs"}
target={userdata?.org_status?.includes("integration_partner") && userdata?.active_org?.branding?.documentation_link?.length > 0 ? "_blank" : "_self"}
onClick={(event) => {
setCurrentOpenTab("docs");
localStorage.setItem("lastTabOpenByUser", "docs");
@@ -1683,7 +1685,7 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
<Button
component={Link}
to={isCloud ? "/admin?admin_tab=billingstats" : "/admin?admin_tab=locations"}
to={isCloud ? "/admin" : "/admin"}
onClick={(event) => {
setCurrentOpenTab("admin");
localStorage.setItem("lastTabOpenByUser", "admin");
@@ -2105,4 +2107,4 @@ const ModalView = memo(({searchBarModalOpen, setSearchBarModalOpen, globalUrl, s
</Dialog>
)
)
});
});
+1 -3
View File
@@ -1127,7 +1127,6 @@ const LicencePopup = (props) => {
color: "white",
}
console.log("Priceitem: ", shuffleVariant)
// const isLoggedInHandler = () => {
// if (calculatedCost === payasyougo) {
// handlePayasyougo(props.userdata)
@@ -1237,12 +1236,11 @@ const LicencePopup = (props) => {
});
};
console.log("Selected Organization: ", selectedOrganization.subscriptions)
return (
<div>
<Grid container spacing={2} style={{ flexDirection: "row", flexWrap: "nowrap", borderRadius: '16px', display: "flex"}}>
<Grid item maxWidth={licensePopup ? 400 : 450}>
{selectedOrganization.subscriptions === undefined || selectedOrganization.subscriptions === null || selectedOrganization.subscriptions.length === 0 ?
{(selectedOrganization.subscriptions === undefined || selectedOrganization.subscriptions === null || selectedOrganization.subscriptions.length === 0) && isCloud ?
<SubscriptionObject
index={0}
globalUrl={globalUrl}
+28 -24
View File
@@ -1216,7 +1216,7 @@ const ParsedAction = (props) => {
selectedActionParameters[1].value = splitparsed[1]
selectedAction.parameters[1].value = splitparsed[1]
if (splitparsed.length > 2) {
if (splitparsed.length >= 2) {
toast.warn("Filter list only supports filtering on the first list. If you want multi-level filtering, please use the 'execute python' action with the 'filter a list' function in the code editor.", {
autoClose: 10000,
})
@@ -1494,7 +1494,7 @@ const ParsedAction = (props) => {
// Helpertext for openapi fields
//if (helperText === "" && name === "body" && selectedApp.generated && selectedApp.activated) {
// helperText = <span style={{color: "white", marginBottom: 5, marginleft: 5}}>
// helperText = <span style={{color: theme.palette.text.primary, marginBottom: 5, marginleft: 5}}>
//}
return helperText
@@ -1563,7 +1563,7 @@ const ParsedAction = (props) => {
}
return <Paper style={{ padding: 10, backgroundColor: theme.palette.surfaceColor, border: "1px solid red", }}>
<Typography variant="body" style={{ color: "white", }}>
<Typography variant="body" style={{ color: theme.palette.text.primary, }}>
<b>Tip:</b> {suggestionText}
</Typography>
</Paper>
@@ -2606,7 +2606,7 @@ const ParsedAction = (props) => {
}}
style={{
backgroundColor: theme.palette.inputColor,
color: "white",
color: theme.palette.text.primary,
height: "50px",
borderRadius: theme.palette?.borderRadius,
}}
@@ -2623,7 +2623,7 @@ const ParsedAction = (props) => {
key={data.Name}
style={{
backgroundColor: theme.palette.inputColor,
color: "white",
color: theme.palette.text.primary,
}}
value={data.Name}
>
@@ -2702,7 +2702,7 @@ const ParsedAction = (props) => {
}}
style={{
backgroundColor: theme.palette.inputColor,
color: "white",
color: theme.palette.text.primary,
height: "50px",
borderRadius: theme.palette?.borderRadius,
}}
@@ -2710,7 +2710,7 @@ const ParsedAction = (props) => {
<MenuItem
style={{
backgroundColor: theme.palette.inputColor,
color: "white",
color: theme.palette.text.primary,
}}
value="No selection"
>
@@ -2721,7 +2721,7 @@ const ParsedAction = (props) => {
<MenuItem
style={{
backgroundColor: theme.palette.inputColor,
color: "white",
color: theme.palette.text.primary,
}}
value={data.name}
>
@@ -2986,7 +2986,7 @@ const ParsedAction = (props) => {
<div
style={{
marginTop: "10px",
borderColor: "white",
borderColor: theme.palette.text.primary,
borderWidth: "2px",
marginBottom: hideExtraTypes ? 50 : 200,
}}
@@ -3182,7 +3182,7 @@ const ParsedAction = (props) => {
ListboxProps={{
style: {
backgroundColor: theme.palette.inputColor,
color: "white",
color: theme.palette.text.primary,
},
}}
getOptionLabel={(option) => {
@@ -4422,7 +4422,7 @@ const ParsedAction = (props) => {
}}
open={!!menuPosition}
style={{
color: "white",
color: theme.palette.text.primary,
marginTop: 2,
maxHeight: 650,
}}
@@ -4550,7 +4550,7 @@ const ParsedAction = (props) => {
}
parentMenuOpen={!!menuPosition}
style={{
color: "white",
color: theme.palette.text.primary,
minWidth: 250,
maxWidth: 250,
maxHeight: 50,
@@ -4566,7 +4566,7 @@ const ParsedAction = (props) => {
key={innerdata.name}
style={{
marginLeft: 15,
color: "white",
color: theme.palette.text.primary,
minWidth: 250,
maxWidth: 250,
padding: 0,
@@ -4607,7 +4607,7 @@ const ParsedAction = (props) => {
<MenuItem
key={pathdata.name}
style={{
color: "white",
color: theme.palette.text.primary,
minWidth: 250,
maxWidth: 250,
padding: boxPadding,
@@ -4664,7 +4664,7 @@ const ParsedAction = (props) => {
key={innerdata.name}
style={{
backgroundColor: theme.palette.inputColor,
color: "white",
color: theme.palette.text.primary,
minWidth: 250,
maxWidth: 250,
marginRight: 0,
@@ -4835,17 +4835,21 @@ const ParsedAction = (props) => {
<OpenInFullIcon
style={{ color: theme.palette.textPrimary, cursor: "pointer", margin: multiline ? 5 : 0, height: 20, width: 20, }}
onMouseOver={(event) => {
const clickedField = document.getElementById(clickedFieldId)
if (clickedField !== null) {
clickedField.focus()
if(selectedAction?.name !== "filter_list"){
const clickedField = document.getElementById(clickedFieldId)
if (clickedField !== null) {
clickedField.focus()
}
}
}}
onClick={(event) => {
// Set focus to the Textfield we just clicked
// This is to ensure focus is set correctly at all times with blur
const clickedField = document.getElementById(clickedFieldId)
if (clickedField !== null) {
clickedField.focus()
if(selectedAction?.name !== "filter_list"){
const clickedField = document.getElementById(clickedFieldId)
if (clickedField !== null) {
clickedField.focus()
}
}
event.preventDefault()
@@ -4887,7 +4891,7 @@ const ParsedAction = (props) => {
<FormControl fullWidth style={{ marginTop: 0 }}>
<InputLabel
id="action-autocompleter"
style={{ marginLeft: 10, color: "white" }}
style={{ marginLeft: 10, color: theme.palette.text.primary }}
>
Autocomplete
</InputLabel>
@@ -4915,7 +4919,7 @@ const ParsedAction = (props) => {
fullWidth
open={showAutocomplete}
style={{
color: "white",
color: theme.palette.text.primary,
height: 35,
marginTop: 2,
borderRadius: theme.palette?.borderRadius,
@@ -4954,7 +4958,7 @@ const ParsedAction = (props) => {
key={data.name}
style={{
backgroundColor: theme.palette.inputColor,
color: "white",
color: theme.palette.text.primary,
}}
value={data}
onMouseOver={() => { }}
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -47,7 +47,7 @@ const chipStyle = {
backgroundColor: "#3d3f43", height: 30, marginRight: 5, paddingLeft: 5, paddingRight: 5, height: 28, cursor: "pointer", borderColor: "#3d3f43", color: "white",
}
const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240")
const searchClient = algoliasearch("JNSS5CFDZZ", "c8f882473ff42d41158430be09ec2b4e")
const SearchData = props => {
const { serverside, globalUrl, userdata } = props
let navigate = useNavigate();
@@ -132,7 +132,7 @@ const CodeEditor = (props) => {
fieldname,
contentLoading,
editorData,
handleSubflowParamChange,
handleTriggerParamChange,
setAiQueryModalOpen,
fullScreenMode,
environment,
@@ -212,7 +212,7 @@ const CodeEditor = (props) => {
const triggerField = searchParams.get('trigger_field');
const triggerName = searchParams.get('trigger_name');
const conditionId = searchParams.get('condition_id');
const conditionField = searchParams.get('field');
const conditionField = searchParams.get('condition_field');
useEffect(() => {
if (actionId === undefined || actionId === null) {
@@ -251,7 +251,7 @@ const CodeEditor = (props) => {
setSelectedCondition(condition);
// Update available variables when condition changes
updateAvailableVariables(actionlist);
}, [conditionId, fieldName])
}, [conditionId, conditionField])
// Extract variable updating logic into a separate function
const updateAvailableVariables = (actionlist) => {
@@ -2588,7 +2588,7 @@ const CodeEditor = (props) => {
// Handle condition fields
if (conditionField !== null && handleConditionFieldChange !== undefined) {
handleConditionFieldChange(conditionField, fieldName, fixedcodedata);
handleConditionFieldChange(conditionField, fixedcodedata);
}
// Handle action fields
else if (actionId !== undefined && actionId !== null && actionId.length > 0) {
@@ -2596,7 +2596,7 @@ const CodeEditor = (props) => {
}
// Handle trigger fields
else if (triggerId !== undefined && triggerId !== null && triggerId.length > 0) {
handleSubflowParamChange(triggerId, triggerField, fixedcodedata)
handleTriggerParamChange(triggerId, triggerField, fixedcodedata)
}
setExpansionModalOpen(false)
+33 -1
View File
@@ -1359,7 +1359,7 @@ const UserManagmentTab = memo((props) => {
}}
>
<ListItem style={{ width: "100%", padding: "10px 10px 10px 0px", verticalAlign: 'middle', borderBottom: theme.palette.defaultBorder, display: "table-row" }}>
{["Username", /*"API Key",*/ "Role", /*"Active",*/ "Type", "MFA", ...(selectedOrganization?.child_orgs?.length > 0 ? ["Suborgs"]: []), "Actions", "Last Login"].map((header, index) => (
{["Region", "Username", /*"API Key",*/ "Role", /*"Active",*/ "Type", "MFA", ...(selectedOrganization?.child_orgs?.length > 0 ? ["Suborgs"]: []), "Actions", "Last Login"].map((header, index) => (
<ListItemText
key={index}
primary={header}
@@ -1459,8 +1459,40 @@ const UserManagmentTab = memo((props) => {
);
}
const getRegionFlag = (region_url) => {
let regiontag = "UK";
let regionCode = "gb";
const regionsplit = region_url?.split(".");
if (regionsplit?.length > 2 && !regionsplit[0]?.includes("shuffler")) {
const namesplit = regionsplit[0]?.split("/");
regiontag = namesplit[namesplit?.length - 1];
if (regiontag === "california") {
regiontag = "US";
regionCode = "us";
} else if (regiontag === "frankfurt") {
regiontag = "EU-2";
regionCode = "eu";
} else if (regiontag === "ca") {
regiontag = "CA";
regionCode = "ca";
}else if (regiontag === "au") {
regiontag = "AUS";
regionCode = "au"
}
}
return regionCode;
};
const userRegion = data?.user_geo_info?.country?.iso_code?.length > 0 ? data?.user_geo_info?.country?.iso_code : data?.active_org?.region_url?.length > 0 ? getRegionFlag(data?.active_org?.region_url) : "eu";
return (
<ListItem key={index} style={{ backgroundColor: bgColor, display: 'table-row', borderBottomLeftRadius: users?.length - 1 === index ? 8 : 0, borderBottomRightRadius: users?.length - 1 === index ? 8 : 0 }}>
<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, }} />)}
style={{ display: 'table-cell', verticalAlign: 'middle', textAlign: 'center' }}
/>
<ListItemText
primary={(
<Tooltip title={data.username || 'No username available'}>
+1 -1
View File
@@ -24,7 +24,7 @@ import {
import WorkflowPaper from "../components/WorkflowPaper.jsx"
import WorkflowPaperNew from "../components/WorkflowPaperNew.jsx"
const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240")
const searchClient = algoliasearch("JNSS5CFDZZ", "c8f882473ff42d41158430be09ec2b4e")
const AppGrid = props => {
const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs, alternativeView, onlyResults, inputsearch } = props
+1 -1
View File
@@ -10,7 +10,7 @@ import algoliasearch from 'algoliasearch';
import { InstantSearch, connectSearchBox, connectHits } from 'react-instantsearch-dom';
import { Grid, Paper, TextField, ButtonBase, InputAdornment, Typography, Button, Tooltip} from '@mui/material';
const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240")
const searchClient = algoliasearch("JNSS5CFDZZ", "c8f882473ff42d41158430be09ec2b4e")
const WorkflowSearch = props => {
const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs, newSelectedApp, setNewSelectedApp, defaultSearch, showSearch, ConfiguredHits, selectAble, } = props
const rowHandler = maxRows === undefined || maxRows === null ? 50 : maxRows
+2 -1
View File
@@ -334,7 +334,8 @@ const Admin2 = (props) => {
}
return (
<div style={{ display: 'flex', justifyContent: 'center', paddingTop: 29, zoom: 0.9}}>
//<div style={{ display: 'flex', justifyContent: 'center', paddingTop: 29, zoom: 0.9}}>
<div style={{ display: 'flex', justifyContent: 'center', paddingTop: 29, zoom: 1, }}>
<AdminNavBar userdata={userdata} isLoaded={isLoaded} isOrgLoaded={isOrgLoaded} selectedStatus={selectedStatus} setSelectedStatus={setSelectedStatus} selectedTab={selectedTab} orgId={selectedOrganization.id} handleStatusChange={handleStatusChange} handleEditOrg={handleEditOrg} handleGetOrg={handleGetOrg} setSelectedOrganization={setSelectedOrganization} selectedOrganization={selectedOrganization} setNotifications={setNotifications} stripeKey={stripeKey} notifications={notifications} checkLogin={checkLogin} globalUrl={globalUrl} isCloud={isCloud}/>
</div>
);
+9 -3
View File
@@ -68,13 +68,19 @@ const AdminAccount = (props) => {
.then((response) =>
response.json().then((responseJson) => {
if (responseJson["success"] === false) {
setLoginInfo(responseJson["reason"]);
setLoginInfo(responseJson["reason"])
if (responseJson?.reason?.toLowerCase().includes("connection refused")) {
navigate("/loginsetup")
}
} else {
if (responseJson.reason === "redirect") {
setTimeout(() => {
window.location.pathname = "/login";
window.location.pathname = "/login"
}, 2500)
}
}
})
)
@@ -111,7 +117,7 @@ const AdminAccount = (props) => {
if (responseJson["success"] === false) {
setLoginInfo(responseJson["reason"]);
} else {
setLoginInfo("Successful register :)");
setLoginInfo("Successful register! Redirecting in a moment...");
setTimeout(() => {
window.location.pathname = "/login";
+54 -26
View File
@@ -438,7 +438,7 @@ const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
//const referenceUrl = "https://shuffler.io/functions/webhooks/"
//const referenceUrl = window.location.origin+"/api/v1/hooks/"
const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240")
const searchClient = algoliasearch("JNSS5CFDZZ", "c8f882473ff42d41158430be09ec2b4e")
const AngularWorkflow = (defaultprops) => {
const { globalUrl, setCookie, isLoggedIn, isLoaded, userdata, data_id, ReactGA, } = defaultprops;
const {themeMode, supportEmail, brandColor} = useContext(Context)
@@ -790,7 +790,7 @@ const AngularWorkflow = (defaultprops) => {
},
{
"name": "fields",
"value": "",
"value": '{\n "ticket_id": "123456",\n "comment": "This is a comment"\n}',
"required": false,
"multiline": true,
},
@@ -3573,6 +3573,12 @@ const AngularWorkflow = (defaultprops) => {
if (curapp?.actions === undefined || curapp?.actions === null || curapp?.actions?.length === 0 || curapp?.actions?.length === 1) {
loadAppConfig(curapp?.id, false, true)
}
if (key > 10) {
console.log("Breaking on 10 sideloads of total", responseJson.length)
break
}
}
// Find app with ID "794e51c3c1a8b24b89ccc573a3defc47" (gmail) to force-break it,
@@ -7000,7 +7006,11 @@ const AngularWorkflow = (defaultprops) => {
})
.then((responseJson) => {
if (responseJson.success === false) {
toast("Failed to auto-activate the app. Go to /apps and activate it.")
if (responseJson.reason !== undefined && responseJson.reason !== null && responseJson.reason.length > 0) {
toast.error("Failed to auto-activate the app: " + responseJson.reason)
} else {
toast.error("Failed to auto-activate the app. Go to /apps and activate it.")
}
} else {
if (refresh === true) {
setHighlightedApp(appid)
@@ -10312,7 +10322,7 @@ const AngularWorkflow = (defaultprops) => {
}
}
toast("Creating schedule")
toast.info("Creating schedule")
var data = {
name: trigger.name,
frequency: workflow.triggers[triggerindex].parameters[0].value,
@@ -10361,9 +10371,9 @@ const AngularWorkflow = (defaultprops) => {
})
.then((responseJson) => {
if (!responseJson.success) {
toast("Failed to set schedule: " + responseJson.reason);
toast.error("Failed to set schedule: " + responseJson.reason);
} else {
toast("Successfully created schedule");
toast.success("Successfully created schedule");
workflow.triggers[triggerindex].status = "running";
trigger.status = "running";
setSelectedTrigger(trigger);
@@ -11852,7 +11862,7 @@ const AngularWorkflow = (defaultprops) => {
if (queryID !== undefined && queryID !== null) {
aa('init', {
appId: "JNSS5CFDZZ",
apiKey: "db08e40265e2941b9a7d8f644b6e5240",
apiKey: "c8f882473ff42d41158430be09ec2b4e",
})
const timestamp = new Date().getTime()
@@ -11876,8 +11886,9 @@ const AngularWorkflow = (defaultprops) => {
var type = "app"
const baseImage = <LibraryBooksIcon />
const width = 230
return (
<div style={{ position: "relative", marginTop: 15, marginLeft: 0, marginRight: 10, position: "absolute", color: theme.palette.textColor, zIndex: 1001, backgroundColor: theme.palette.textFieldStyle.backgroundColor, minWidth: leftBarSize+30, maxWidth: 340, boxShadows: "none", overflowX: "hidden", }}>
<div style={{ position: "relative", marginTop: 15, marginLeft: 0, marginRight: 10, position: "absolute", color: theme.palette.textColor, zIndex: 1001, backgroundColor: theme.palette.textFieldStyle.backgroundColor, /*minWidth: leftBarSize+30,*/ minWidth: width, maxWidth: width, boxShadows: "none", overflowX: "hidden", }}>
<List style={{ backgroundColor: theme.palette.inputColor, }}>
{hits.length === 0 ?
<ListItem style={outerlistitemStyle}>
@@ -11971,7 +11982,7 @@ const AngularWorkflow = (defaultprops) => {
}}
defaultPosition={{ x: 0, y: 0 }}
>
<div style={{ textDecoration: "none", color: theme.palette.text.primary, }} onClick={(event) => {
<div style={{ overflow: "hidden", textDecoration: "none", color: theme.palette.text.primary, }} onClick={(event) => {
clickedApp(hit)
}}>
@@ -12125,7 +12136,12 @@ const AngularWorkflow = (defaultprops) => {
}
if ((app.id === "integration" || app.id === "shuffle_agent") && userdata.support !== true) {
return null
console.log("APPID: ", app.id, isCloud)
if (isCloud === false && app.id === "integration") {
} else {
console.log("RETURNING", app.id)
return null
}
}
if (viewedApps.includes(app.id)) {
@@ -12190,7 +12206,7 @@ const AngularWorkflow = (defaultprops) => {
</div>
) : apps.length > 0 ? (
<div
style={{ textAlign: "center", width: leftBarSize, marginTop: 10, marginLeft: 5, marginRight: 5, }}
style={{ textAlign: "center", width: leftBarSize, marginTop: 25, marginLeft: 10 , marginRight: 10, }}
onLoad={() => {
console.log("Should load in extra apps?")
}}
@@ -12198,6 +12214,7 @@ const AngularWorkflow = (defaultprops) => {
<Typography variant="body1" color="textSecondary">
Couldn't find the apps you were looking for? Searching unactivated apps. Click one of these apps to Activate it for your organisation.
</Typography>
<InstantSearch searchClient={searchClient} indexName="appsearch" onClick={() => {
console.log("CLICKED")
}}>
@@ -13021,7 +13038,7 @@ const AngularWorkflow = (defaultprops) => {
event.preventDefault()
setExpansionModalOpen(true)
setActiveDialog("codeeditor")
navigate(`?condition_id=${data.id}&field=${data.name}`)
navigate(`?condition_id=${data.id}&condition_field=${data.name}`)
setEditorData({
"name": data.name,
"value": data.value || "",
@@ -13106,9 +13123,9 @@ const AngularWorkflow = (defaultprops) => {
// Update the field value based on type
if (type === "source") {
handleConditionFieldChange("source", "value", toComplete);
handleConditionFieldChange("source", toComplete);
} else if (type === "destination") {
handleConditionFieldChange("destination", "value", toComplete);
handleConditionFieldChange("destination", toComplete);
}
handleMenuClose();
@@ -13985,7 +14002,7 @@ const AngularWorkflow = (defaultprops) => {
/>
</Dialog>
const handleConditionFieldChange = (fieldType, fieldName, value) => {
const handleConditionFieldChange = (fieldType, value) => {
if (fieldType === "source") {
setSourceValue({
...sourceValue,
@@ -15186,7 +15203,7 @@ const AngularWorkflow = (defaultprops) => {
}
]
const handleSubflowParamChange = (triggerId, triggerField, newData) => {
const handleTriggerParamChange = (triggerId, triggerField, newData) => {
var updateFail = ""
if (workflow !== undefined && workflow !== null) {
@@ -15303,7 +15320,8 @@ const AngularWorkflow = (defaultprops) => {
<Typography>Name</Typography>
<TextField
style={{
backgroundColor: "#212121",
backgroundColor: theme.palette.inputColor,
color: theme.palette.text.primary,
borderRadius: theme.palette?.borderRadius,
marginTop: 3,
}}
@@ -15332,7 +15350,8 @@ const AngularWorkflow = (defaultprops) => {
<Typography>Delay</Typography>
<TextField
style={{
backgroundColor: "#212121",
backgroundColor: theme.palette.inputColor,
color: theme.palette.text.primary,
marginTop: 3,
maxWidth: 50,
}}
@@ -17323,9 +17342,13 @@ const AngularWorkflow = (defaultprops) => {
fullWidth
rows="4"
multiline
defaultValue={selectedTrigger.parameters[0]?.value}
value={selectedTriggerValue || ""}
color="primary"
placeholder=""
onChange={(e) => {
setLastSaved(false)
setSelectedTriggerValue(e.target.value)
}}
onBlur={(e) => {
setLastSaved(false)
setTriggerTextInformationWrapper(e.target.value);
@@ -18681,7 +18704,9 @@ const AngularWorkflow = (defaultprops) => {
{originalWorkflow?.suborg_distribution === undefined || originalWorkflow?.suborg_distribution === null || originalWorkflow?.suborg_distribution?.length === 0 || originalWorkflow?.suborg_distribution.includes("none") ?
originalWorkflow?.parentorg_workflow !== undefined && originalWorkflow?.parentorg_workflow !== null && originalWorkflow?.parentorg_workflow.length > 0 || workflow?.parentorg_workflow !== undefined && workflow?.parentorg_workflow !== null && workflow?.parentorg_workflow.length > 0 ?
<div>
{originalWorkflow?.parentorg_workflow !== undefined && originalWorkflow?.parentorg_workflow !== null && originalWorkflow?.parentorg_workflow.length > 0 || workflow?.parentorg_workflow !== undefined && workflow?.parentorg_workflow !== null && workflow?.parentorg_workflow.length > 0 ?
<Button
color="secondary"
variant="outlined"
@@ -18691,7 +18716,7 @@ const AngularWorkflow = (defaultprops) => {
textTransform: "none",
marginLeft: 10,
}}
onClick={() => {
onClick={() => {
navigate(`/workflows/${workflow.parentorg_workflow}`)
// Reload the page
window.location.reload()
@@ -18699,7 +18724,10 @@ const AngularWorkflow = (defaultprops) => {
>
Go to parent org workflow
</Button>
: userdata !== undefined && userdata !== null && userdata.orgs !== undefined && userdata.orgs !== null && userdata.orgs.length > 1 && workflow?.id !== undefined && workflow?.id && workflow?.id?.length > 0 ?
: null}
{userdata !== undefined && userdata !== null && userdata.orgs !== undefined && userdata.orgs !== null && userdata.orgs.length > 1 && workflow?.id !== undefined && workflow?.id && workflow?.id?.length > 0 ?
<Button
color="secondary"
variant="outlined"
@@ -18715,8 +18743,8 @@ const AngularWorkflow = (defaultprops) => {
>
Enable Suborg Distribution
</Button>
: null
: null}
</div>
:
<Tooltip title={lastSaved === false && originalWorkflow.id === workflow.id ?
@@ -20063,7 +20091,7 @@ const AngularWorkflow = (defaultprops) => {
if (queryID !== undefined && queryID !== null) {
aa('init', {
appId: "JNSS5CFDZZ",
apiKey: "db08e40265e2941b9a7d8f644b6e5240",
apiKey: "c8f882473ff42d41158430be09ec2b4e",
})
const timestamp = new Date().getTime()
@@ -25459,7 +25487,7 @@ const AngularWorkflow = (defaultprops) => {
// selectedTrigger={selectedTrigger}
aiSubmit={aiSubmit}
toolsAppId={toolsApp.id}
handleSubflowParamChange={handleSubflowParamChange}
handleTriggerParamChange={handleTriggerParamChange}
codedata={editorData.value}
setcodedata={setcodedata}
selectedEdge={selectedEdge}
+1 -1
View File
@@ -50,7 +50,7 @@ import { green } from "../views/AngularWorkflow.jsx"
const searchClient = algoliasearch(
"JNSS5CFDZZ",
"db08e40265e2941b9a7d8f644b6e5240"
"c8f882473ff42d41158430be09ec2b4e"
)
// Lazy loading of ApiExplorer component to reduce initial load time
+3 -3
View File
@@ -93,7 +93,7 @@ import aa from "search-insights";
// 2 = OpenAPI (Invalid)
const searchClient = algoliasearch(
"JNSS5CFDZZ",
"db08e40265e2941b9a7d8f644b6e5240"
"c8f882473ff42d41158430be09ec2b4e"
)
const AppExplorer = (props) => {
@@ -3996,7 +3996,7 @@ const buttonBackground = "linear-gradient(to right, #f86a3e, #f34079)";
if (queryID !== undefined && queryID !== null) {
aa("init", {
appId: "JNSS5CFDZZ",
apiKey: "db08e40265e2941b9a7d8f644b6e5240",
apiKey: "c8f882473ff42d41158430be09ec2b4e",
});
const timestamp = new Date().getTime();
@@ -4085,7 +4085,7 @@ const buttonBackground = "linear-gradient(to right, #f86a3e, #f34079)";
if (queryID !== undefined && queryID !== null) {
aa("init", {
appId: "JNSS5CFDZZ",
apiKey: "db08e40265e2941b9a7d8f644b6e5240",
apiKey: "c8f882473ff42d41158430be09ec2b4e",
});
const timestamp = new Date().getTime();
+3 -3
View File
@@ -280,7 +280,7 @@ export const GetParsedPaths = (inputdata, basekey) => {
return parsedValues;
};
const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240")
const searchClient = algoliasearch("JNSS5CFDZZ", "c8f882473ff42d41158430be09ec2b4e")
const Apps = (props) => {
const { globalUrl, isLoggedIn, isLoaded, userdata, serverside, } = props;
@@ -1305,7 +1305,7 @@ const Apps = (props) => {
if (queryID !== undefined && queryID !== null) {
aa("init", {
appId: "JNSS5CFDZZ",
apiKey: "db08e40265e2941b9a7d8f644b6e5240",
apiKey: "c8f882473ff42d41158430be09ec2b4e",
});
const timestamp = new Date().getTime();
@@ -2036,7 +2036,7 @@ const Apps = (props) => {
if (queryID !== undefined && queryID !== null) {
aa('init', {
appId: "JNSS5CFDZZ",
apiKey: "db08e40265e2941b9a7d8f644b6e5240",
apiKey: "c8f882473ff42d41158430be09ec2b4e",
})
const timestamp = new Date().getTime()
+68 -102
View File
@@ -46,7 +46,7 @@ import AppCreationModal from "../components/AppCreationModal.jsx";
const searchClient = algoliasearch(
"JNSS5CFDZZ",
"db08e40265e2941b9a7d8f644b6e5240"
"c8f882473ff42d41158430be09ec2b4e"
);
// AppCard Component
@@ -1122,6 +1122,7 @@ const Apps2 = (props) => {
const [defaultSearch, setDefaultSearch] = useState("");
const [apps, setApps] = useState([]);
const [backupApps, setBackupApps] = useState([]);
const [filteredApps, setFilteredApps] = useState([]);
const [appSearchLoading, setAppSearchLoading] = useState(false);
const [creatorProfile, setCreatorProfile] = useState({});
@@ -1198,57 +1199,9 @@ const Apps2 = (props) => {
getFramework();
}, []);
// Fetch apps based on the current tab : 0 -> org_apps, 1 -> my_apps, 2 -> all_apps
const fetchApps = async () => {
const baseUrl = globalUrl;
let url;
setIsLoading(true);
const userId = userdata?.id;
if (currTab === 1 && userId) {
url = `${baseUrl}/api/v1/users/${userId}/apps`;
} else if (currTab === 0) {
url = `${baseUrl}/api/v1/apps`;
}
try {
const response = await fetch(url, {
method: "GET",
credentials: "include",
headers: {
"Content-Type": "application/json",
},
});
const data = await response.json();
if (currTab === 1) {
setAppsToShow(data);
setUserApps(data);
} else if (currTab === 0) {
setAppsToShow(data);
setOrgApps(data);
// For testing the empty state
// setAppsToShow([]);
// setOrgApps([]);
}
setIsLoading(false);
} catch (err) {
console.error("Error fetching apps:", err);
setIsLoading(false);
}
};
useEffect(() => {
// Only fetch if we have required data
if (globalUrl && (currTab === 0 || (currTab === 1 && userdata?.id))) {
fetchApps();
}
}, [currTab, globalUrl, userdata?.id]); // Remove location.search dependency
// useEffect(() => {
// // setSearchQuery("");
// setSelectedCategory([]);
// setSelectedLabel([]);
// }, [currTab])
getApps()
}, [])
// Find top categories and tags based on the current tab
useEffect(() => {
@@ -1293,11 +1246,13 @@ const Apps2 = (props) => {
});
};
/*
useEffect(() => {
if (serverside) {
return null;
}
}, [serverside]);
*/
const getApps = () => {
// Get apps from localstorage
@@ -1308,7 +1263,7 @@ const Apps2 = (props) => {
if (storageApps === null || storageApps === undefined || storageApps.length === 0) {
storageApps = []
} else {
setAppsToShow(storageApps)
//setAppsToShow(storageApps)
setOrgApps(storageApps)
setApps(storageApps)
// setFilteredApps(storageApps)
@@ -1344,18 +1299,25 @@ const Apps2 = (props) => {
var privateapps = [];
var valid = [];
var invalid = [];
var backups = []
for (var key in responseJson) {
const app = responseJson[key];
if (app.categories !== undefined && app.categories !== null && app?.categories.includes("Eradication")) {
if (app?.reference_info?.onprem_backup === true) {
backups.push(app)
continue
}
if (app?.categories !== undefined && app?.categories !== null && app?.categories?.includes("Eradication")) {
app.categories = ["EDR"]
}
if (app.is_valid && !(!app.activated && app.generated)) {
if (app?.is_valid && !(!app?.activated && app?.generated)) {
privateapps.push(app);
} else if (
app.private_id !== undefined &&
app.private_id.length > 0
app?.private_id !== undefined &&
app?.private_id.length > 0
) {
valid.push(app);
} else {
@@ -1363,6 +1325,11 @@ const Apps2 = (props) => {
}
}
console.log("BACKUPAPPS: ", backups)
if (backups.length > 0) {
setBackupApps(backups)
}
privateapps.push(...valid);
privateapps.push(...invalid);
console.log("privateapps: setting apps ", privateapps)
@@ -1372,39 +1339,22 @@ const Apps2 = (props) => {
// setFilteredApps(privateapps);
if (privateapps.length > 0) {
if (selectedApp.id === undefined || selectedApp.id === null) {
if (privateapps[0].owner !== undefined && privateapps[0].owner !== null) {
getUserProfile(privateapps[0].owner);
if (selectedApp?.id === undefined || selectedApp?.id === null) {
if (privateapps[0]?.owner !== undefined && privateapps[0]?.owner !== null) {
getUserProfile(privateapps[0]?.owner);
}
// setContact(privateapps[0].contact_info)
// setSelectedApp(privateapps[0]);
// setSharingConfiguration(privateapps[0].sharing === true ? "public" : "you")
}
// if (
// privateapps[0].actions !== null &&
// privateapps[0].actions.length > 0
// ) {
// setSelectedAction(privateapps[0].actions[0]);
// } else {
// setSelectedAction({});
// }
}
if (privateapps.length > 0 && storageApps.length === 0) {
if (privateapps?.length > 0 && storageApps?.length === 0) {
try {
localStorage.setItem("apps", JSON.stringify(privateapps))
} catch (e) {
console.log("Failed to set apps in localstorage: ", e)
}
}
//setTimeout(() => {
// setFirstLoad(false)
//}, 5000)
})
.catch((error) => {
console.log("Failed to get apps: ", error.toString());
toast(error.toString());
setIsLoading(false);
});
@@ -1780,7 +1730,6 @@ const Apps2 = (props) => {
// setOpenModal(true);
};
useEffect(() => {
const apps = currTab === 1 ? userApps : orgApps;
const filteredUserAppdata = filterApps(apps, searchQuery, selectedCategory, selectedLabel);
@@ -1798,33 +1747,38 @@ const Apps2 = (props) => {
} else if (newTab === 1) {
const filteredUserApps = filterApps(userApps, searchQuery, selectedCategory, selectedLabel);
setAppsToShow(filteredUserApps);
}
} else if (newTab === 3) {
const filteredUserApps = filterApps(backupApps, searchQuery, selectedCategory, selectedLabel);
setAppsToShow(filteredUserApps);
return
}
// Update URL query params based on tab index
const tabMapping = {
0: 'org_apps',
1: 'my_apps',
2: 'all_apps'
2: 'all_apps',
3: 'backup_apps',
};
const queryParams = new URLSearchParams(location.search);
queryParams.set('tab', tabMapping[newTab]);
const queryParams = new URLSearchParams(location.search);
queryParams.set('tab', tabMapping[newTab]);
// Maintain search query in URL regardless of tab
if (searchQuery) {
queryParams.set('q', searchQuery);
} else {
queryParams.delete('q');
}
// Maintain search query in URL regardless of tab
if (searchQuery) {
queryParams.set('q', searchQuery);
} else {
queryParams.delete('q');
}
navigate(`${location.pathname}?${queryParams.toString()}`);
navigate(`${location.pathname}?${queryParams.toString()}`);
};
// Update useEffect for filtering without URL manipulation
useEffect(() => {
if (currTab === 2) return; // Skip for "Discover Apps" tab as it uses Algolia
const apps = currTab === 1 ? userApps : orgApps;
const apps = currTab === 1 ? userApps : currTab === 3 ? backupApps : orgApps;
const filteredApps = filterApps(apps, searchQuery, selectedCategory, selectedLabel);
setAppsToShow(filteredApps);
}, [searchQuery, selectedCategory, selectedLabel, currTab, userApps, orgApps]);
@@ -1914,7 +1868,7 @@ const Apps2 = (props) => {
<div style={boxStyle}>
<div style={{ display: "flex", flexDirection: "row", width: "100%", justifyContent: "space-between" }}>
<Typography variant="h4" style={{ marginBottom: 20, paddingLeft: 15, textTransform: 'none', fontFamily: theme?.typography?.fontFamily }}>
{currTab === 0 ? "Org" : currTab === 1 ? "Your" : "Discover"} Apps
{currTab === 0 ? "Org" : currTab === 1 ? "Your" : currTab === 3 ? "Backup" : "Discover"} Apps
</Typography>
{isCloud ? null : (
<span style={{ display: "flex", gap: 15 }}>
@@ -1940,7 +1894,7 @@ const Apps2 = (props) => {
style={{
height: 45,
minWidth: 45,
backgroundColor: "#2F2F2F",
backgroundColor: theme.palette.platformColor,
borderRadius: 4,
padding: "8px 16px",
}}
@@ -1950,9 +1904,9 @@ const Apps2 = (props) => {
}}
>
{isLoading ? (
<CircularProgress size={20} style={{ color: "#FF8544" }} />
<CircularProgress size={20} style={{ color: theme.palette.primary.main }} />
) : (
<CachedIcon style={{ color: "#F1F1F1" }} />
<CachedIcon style={{ color: theme.palette.text.primary }} />
)}
</Button>
</Tooltip>
@@ -1980,7 +1934,7 @@ const Apps2 = (props) => {
style={{
height: 45,
minWidth: 45,
backgroundColor: "#2F2F2F",
backgroundColor: theme.palette.platformColor,
borderRadius: 4,
padding: "8px 16px",
}}
@@ -1992,9 +1946,9 @@ const Apps2 = (props) => {
}}
>
{isLoading ? (
<CircularProgress size={20} style={{ color: "#FF8544" }} />
<CircularProgress size={20} style={{ color: theme.palette.primary.main }} />
) : (
<CloudDownloadIcon style={{ color: "#F1F1F1" }} />
<CloudDownloadIcon style={{ color: theme.palette.text.primary }} />
)}
</Button>
</Tooltip>
@@ -2027,6 +1981,7 @@ const Apps2 = (props) => {
...(currTab === 1 ? tabActive : {})
}}
/>
<Tab
label="Discover Public Apps"
style={{
@@ -2035,6 +1990,17 @@ const Apps2 = (props) => {
...(currTab === 2 ? tabActive : {})
}}
/>
{backupApps.length > 0 &&
<Tab
label={`Onprem Backup (${backupApps.length})`}
style={{
...tabStyle,
marginLeft: 25,
...(currTab === 3 ? tabActive : {})
}}
/>
}
</Tabs>
</div>
<div style={{ display: "flex", justifyContent: "space-between", gap: 10, marginBottom: 20, height: 45, paddingRight: 25 }}>
@@ -2043,7 +2009,7 @@ const Apps2 = (props) => {
minWidth: "25%",
maxWidth: "25%"
}}>
{(currTab === 0 || currTab === 1) ? (
{(currTab === 0 || currTab === 1 || currTab === 3) ? (
<TextField
fullWidth
variant="outlined"
@@ -2253,7 +2219,7 @@ const Apps2 = (props) => {
<div>
{
currTab === 0 && (
currTab === 0 || currTab === 3 && (
<div style={{ minHeight: 570 }}>
{isLoading ? (
<LoadingGrid />
@@ -2285,7 +2251,7 @@ const Apps2 = (props) => {
handleAppClick={handleAppClick}
leftSideBarOpenByClick={leftSideBarOpenByClick}
userdata={userdata}
fetchApps={fetchApps}
fetchApps={getApps}
setUserApps={setUserApps}
appsToShow={appsToShow}
@@ -2338,7 +2304,7 @@ const Apps2 = (props) => {
{appsToShow.map((data, index) => (
<AppCard key={index} data={data} index={index} mouseHoverIndex={mouseHoverIndex} setMouseHoverIndex={setMouseHoverIndex} globalUrl={globalUrl} deactivatedIndexes={deactivatedIndexes} currTab={currTab} userdata={userdata}
handleAppClick={handleAppClick} leftSideBarOpenByClick={leftSideBarOpenByClick}
fetchApps={fetchApps}
fetchApps={getApps}
setUserApps={setUserApps}
appsToShow={appsToShow}
setAppsToShow={setAppsToShow}
+41 -27
View File
@@ -314,12 +314,15 @@ const LoginPage = props => {
},
})
if (serverside !== true) {
const tmpMessage = new URLSearchParams(window.location.search).get("message")
if (tmpMessage !== undefined && tmpMessage !== null && message !== tmpMessage) {
setMessage(tmpMessage)
useEffect(() => {
if (serverside !== true) {
const tmpMessage = new URLSearchParams(window.location.search).get("message")
if (tmpMessage !== undefined && tmpMessage !== null && message !== tmpMessage) {
setMessage(tmpMessage)
toast(tmpMessage)
}
}
}
}, [])
if (document !== undefined) {
if (register) {
@@ -361,7 +364,7 @@ const LoginPage = props => {
console.log("Should login instead of register!")
setRegister(!register)
} else {
console.log("Path: " + path, "Register: " + register)
//console.log("Path: " + path, "Register: " + register)
}
}
@@ -444,13 +447,17 @@ const LoginPage = props => {
}
if (isLoggedIn === true && serverside !== true) {
const tmpView = new URLSearchParams(window.location.search).get("view")
if (tmpView !== undefined && tmpView !== null && tmpView === "pricing") {
window.location.pathname = "/pricing"
return
} else if (tmpView !== undefined && tmpView !== null) {
window.location.pathname = tmpView
return
const tmpView = new URLSearchParams(window.location.search).get("view");
if (tmpView !== undefined && tmpView !== null) {
let pathOnly = tmpView.split("?")[0];
if (!pathOnly.startsWith("/")) pathOnly = "/" + pathOnly;
if (pathOnly === "/pricing" || pathOnly === "admin") {
window.location.replace(pathOnly + window.location.search);
} else {
window.location.replace(pathOnly);
}
return;
}
window.location.pathname = "/workflows"
@@ -468,6 +475,11 @@ const LoginPage = props => {
response.json().then((responseJson) => {
if (responseJson["success"] === false) {
setLoginInfo(responseJson["reason"]);
if (responseJson?.reason?.toLowerCase().includes("connection refused")) {
navigate("/loginsetup")
}
} else {
if (responseJson.sso_url !== undefined && responseJson.sso_url !== null) {
setSSOUrl(responseJson.sso_url);
@@ -570,20 +582,17 @@ const LoginPage = props => {
setCookie(responseJson["cookies"][key].key, responseJson["cookies"][key].value, { path: "/" })
}
const tmpView = new URLSearchParams(window.location.search).get("view")
const tmpView = new URLSearchParams(window.location.search).get("view");
if (tmpView !== undefined && tmpView !== null) {
//const newUrl = `/${tmpView}${decodeURIComponent(window.location.search)}`
// Check if slash in the url
let pathOnly = tmpView.split("?")[0];
if (!pathOnly.startsWith("/")) pathOnly = "/" + pathOnly;
var newUrl = `/${tmpView}`
if (tmpView.startsWith("/")) {
newUrl = `${tmpView}`
if (pathOnly === "/pricing" || pathOnly === "admin") {
window.location.replace(pathOnly + window.location.search);
} else {
window.location.replace(pathOnly);
}
console.log("Found url: ", newUrl)
window.location.pathname = newUrl
return
return;
}
console.log("LOGIN DATA: ", responseJson)
@@ -642,9 +651,14 @@ const LoginPage = props => {
const tmpView = new URLSearchParams(window.location.search).get("view")
if (tmpView !== undefined && tmpView !== null) {
//const newUrl = `/${tmpView}${decodeURIComponent(window.location.search)}`
const newUrl = `/${tmpView}`
window.location.pathname = newUrl
let pathOnly = tmpView.split("?")[0];
if (!pathOnly.startsWith("/")) pathOnly = "/" + pathOnly;
if (pathOnly === "/pricing" || pathOnly === "admin") {
window.location.replace(pathOnly + window.location.search);
} else {
window.location.replace(pathOnly);
}
return
}
+7 -1
View File
@@ -85,7 +85,13 @@ const LoginDialog = (props) => {
.then((response) =>
response.json().then((responseJson) => {
if (responseJson["success"] === false) {
setLoginInfo(responseJson["reason"]);
setLoginInfo(responseJson["reason"])
if (responseJson?.reason?.toLowerCase().includes("connection refused")) {
setLoginViewLoading(true)
start()
}
} else {
if (responseJson.sso_url !== undefined && responseJson.sso_url !== null) {
+6 -1
View File
@@ -458,7 +458,12 @@ const Settings = (props) => {
if (responseJson["success"] === false) {
setPasswordFormMessage(responseJson["reason"]);
} else {
toast("Changed password!");
var reason = ""
if (responseJson.reason !== undefined && responseJson.reason !== null && responseJson.reason.length > 0) {
reason += responseJson.reason
}
toast.success("Changed password! " + reason);
setPasswordFormMessage("");
}
})
+8 -1
View File
@@ -126,6 +126,13 @@ const UsecaseListComponent = (props) => {
const { themeMode, brandName } = useContext(Context)
const theme = getTheme(themeMode)
const usecaseLightThemeColor = {
"collect": "#FB47A0",
"enrich": "#F38B14",
"detect": "#0AAD65",
"respond": "#289BDB",
"verify": "#624CE9",
}
const [expandedIndex, setExpandedIndex] = useState(-1);
const [expandedItem, setExpandedItem] = useState(-1);
const [inputUsecase, setInputUsecase] = useState({});
@@ -743,7 +750,7 @@ const UsecaseListComponent = (props) => {
{keys.map((usecase, index) => {
return (
<div key={index} style={{marginTop: 75, }}>
<Typography variant="body1" style={{color: usecase.color, textAlign: "left", marginBottom: 10, }}>
<Typography variant="body1" style={{color: themeMode === "dark" ? usecase.color : usecaseLightThemeColor[usecase.name.slice(3, 100).toLowerCase()], textAlign: "left", marginBottom: 10, }}>
<b>{index+1}. {usecase.name.slice(3, 100)}</b>
</Typography>
<Grid container spacing={1}>
+74 -20
View File
@@ -111,7 +111,7 @@ import { removeQuery } from "../components/ScrollToTop.jsx";
import {green, yellow, red, grey } from "../views/AngularWorkflow.jsx"
const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240");
const searchClient = algoliasearch("JNSS5CFDZZ", "c8f882473ff42d41158430be09ec2b4e");
const svgSize = 24;
const imagesize = 22;
@@ -678,6 +678,7 @@ const Workflows2 = (props) => {
var upload = "";
const [workflows, setWorkflows] = React.useState([]);
const [backupWorkflows, setBackupWorkflows] = React.useState([]);
const [_, setUpdate] = React.useState(""); // Used for rendering, don't remove
const [selectedUsecases, setSelectedUsecases] = React.useState([]);
const [filteredWorkflows, setFilteredWorkflows] = React.useState([]);
@@ -756,7 +757,8 @@ const Workflows2 = (props) => {
const tabMapping = {
0: 'org_workflows',
1: 'my_workflows',
2: 'all_workflows'
2: 'all_workflows',
3: 'backup_apps',
};
const queryParams = new URLSearchParams(location.search);
queryParams.set('tab', tabMapping[newValue]);
@@ -1107,23 +1109,31 @@ const Workflows2 = (props) => {
setSelectedWorkflowId("");
}}
PaperProps={{
style: {
backgroundColor: theme.palette.surfaceColor,
color: "white",
minWidth: 500,
padding: 50,
},
}}
sx: {
borderRadius: theme?.palette?.DialogStyle?.borderRadius,
border: theme?.palette?.DialogStyle?.border,
minWidth: '440px',
fontFamily: theme?.typography?.fontFamily,
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
zIndex: 1000,
'& .MuiDialogContent-root': {
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
},
'& .MuiDialogTitle-root': {
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
},
}
}}
>
<DialogTitle>
<div style={{ textAlign: "center", color: "rgba(255,255,255,0.9)" }}>
<div style={{ textAlign: "center", color: theme.palette.DialogStyle?.color }}>
Are you sure you want to delete {selectedWorkflowId.length > 0 ? filteredWorkflows.find((w) => w.id === selectedWorkflowId)?.name : `${selectedWorkflowIndexes.length} workflow${selectedWorkflowIndexes.length === 1 ? '' : 's'}`}? <div />
Other workflows relying on {selectedWorkflowIndexes.length > 0 ? "them" : "it"} one will stop working
</div>
</DialogTitle>
<DialogContent
style={{ color: "rgba(255,255,255,0.65)", textAlign: "center" }}
style={{ color: theme.palette.DialogStyle.color, textAlign: "center" }}
>
<Button
style={{}}
@@ -1216,7 +1226,7 @@ const Workflows2 = (props) => {
data.status
).then((response) => {
if (response !== undefined) {
toast(`Successfully imported ${data.name}`);
toast.success(`Successfully imported ${data.name}`);
}
});
}
@@ -1341,21 +1351,33 @@ const Workflows2 = (props) => {
// When there are no workflows, we can set the loading to false
setIsLoadingWorkflow(false)
if (currTab !== 2) {
toast("No workflows found. Showing workflow discovery")
toast.info("No workflows found in this org. Feel free to look into our public workflows!" , {
timeout: 7500,
})
setCurrTab(2)
}
}
var newarray = []
var backupWf = []
for (var wfkey in responseJson) {
const wf = responseJson[wfkey]
if (wf.public === true || wf.hidden === true) {
continue
}
if (wf?.backup_config?.onprem_backup === true) {
backupWf.push(wf)
continue
}
newarray.push(wf)
}
if (backupWf.length > 0) {
setBackupWorkflows(backupWf)
}
var setProdFilter = false
var actionnamelist = [];
@@ -2073,7 +2095,7 @@ const Workflows2 = (props) => {
}
} else {
if (bulk !== true) {
toast(`Deleted workflow ${id}. Child Workflows in Suborgs were also removed.`)
toast.success(`Deleted workflow ${id}. Child Workflows in Suborgs were also removed.`)
}
}
@@ -2087,7 +2109,7 @@ const Workflows2 = (props) => {
}
})
.catch((error) => {
toast(error.toString());
toast.error(error.toString());
})
}
@@ -3040,7 +3062,7 @@ const Workflows2 = (props) => {
data.status,
).then((response) => {
if (response !== undefined) {
toast("Successfully imported " + data.name);
toast.success("Imported " + data.name);
}
});
}
@@ -4322,6 +4344,17 @@ const Workflows2 = (props) => {
}}
/>
{backupWorkflows.length > 0 &&
<Tab
label={`Onprem Backup (${backupWorkflows.length})`}
style={{
...tabStyle,
marginLeft: 25,
...(currTab === 3 ? tabActive : {})
}}
/>
}
<Tab
label="Org Forms"
onClick={() => {
@@ -4331,7 +4364,7 @@ const Workflows2 = (props) => {
...tabStyle,
marginRight: 0,
marginLeft: 25,
...(currTab === 3 ? tabActive : {})
...(currTab === 4 ? tabActive : {})
}}
/>
</Tabs>
@@ -4666,8 +4699,6 @@ const Workflows2 = (props) => {
paddingBottom: 40
}}>
{currTab === 0 && orgWorkflows.map((data, index) => {
// Shouldn't be a part of this list
if (data.public === true) {
@@ -4689,6 +4720,27 @@ const Workflows2 = (props) => {
)
})}
{currTab === 3 && backupWorkflows.map((data, index) => {
// Shouldn't be a part of this list
if (data.public === true) {
return null
}
// if (firstLoad) {
// workflowDelay += 75
// } else {
// return <WorkflowPaper key={index} data={data} />
// }
return (
<span key={index}>
{/*<Zoom key={index} in={true} style={{ transitionDelay: `${workflowDelay}ms` }}>*/}
<WorkflowPaper data={data} />
{/*</Zoom>*/}
</span>
)
})}
{
currTab === 1 && myWorkflows.map((data, index) => {
if (data.public === true) {
@@ -5134,7 +5186,9 @@ const Workflows2 = (props) => {
}}
>
<CircularProgress />
<Typography>Loading Workflows</Typography>
<Typography style={{marginTop: 5, }}>
Loading Workflows and Apps
</Typography>
</div>
);