Merge branch 'nightly' of https://github.com/Shuffle/Shuffle into automation

This commit is contained in:
lalitdeore12@gmail.com
2025-03-11 15:57:59 +05:30
96 changed files with 1342 additions and 867 deletions
+53 -12
View File
@@ -25,6 +25,7 @@ import AdminSetup from "./views/AdminSetup.jsx";
import Admin from "./views/Admin.jsx";
import Docs from "./views/Docs.jsx";
import Usecases2 from "./views/Usecases2.jsx";
import DashboardViews from "./views/DashboardViews.jsx";
//import Introduction from "./views/Introduction";
import SetAuthentication from "./views/SetAuthentication.jsx";
import SetAuthenticationSSO from "./views/SetAuthenticationSSO.jsx";
@@ -650,18 +651,58 @@ const App = (message, props) => {
/>
}
/>
<Route
exact
path="/dashboards"
element={
<DashboardView
isLoaded={isLoaded}
isLoggedIn={isLoggedIn}
globalUrl={globalUrl}
{...props}
/>
}
/>
<Route
exact
path="/dashboard"
element={
<DashboardViews
serverside={false}
isLoaded={isLoaded}
isLoggedIn={isLoggedIn}
globalUrl={globalUrl}
wut={userdata}
/>
}
/>
<Route
exact
path="/dashboards"
element={
<DashboardViews
serverside={false}
isLoaded={isLoaded}
isLoggedIn={isLoggedIn}
globalUrl={globalUrl}
wut={userdata}
/>
}
/>
<Route
exact
path="/dashboard/:key"
element={
<DashboardViews
serverside={false}
isLoaded={isLoaded}
isLoggedIn={isLoggedIn}
globalUrl={globalUrl}
wut={userdata}
/>
}
/>
<Route
exact
path="/dashboards/:key"
element={
<DashboardViews
serverside={false}
isLoaded={isLoaded}
isLoggedIn={isLoggedIn}
globalUrl={globalUrl}
wut={userdata}
/>
}
/>
<Route
exact
path="/welcome"
+1 -1
View File
@@ -96,7 +96,7 @@ const AdminNavBar = (props) => {
return (
<Wrapper>
<div style={{ flexDirection: 'column', width: 220, }}>
<nav style={{ padding: '25px 25px 3px 25px', height: "calc(100% - 30px)", fontSize: '16px', borderTopLeftRadius: 8, borderBottomLeftRadius: 8, background: '#212121', color: '#9CA3AF' }}>
<nav style={{ padding: '25px 25px 3px 25px', height: isCloud ? "calc(100% - 60px)" : "calc(100% - 30px)" , fontSize: '16px', borderTopLeftRadius: 8, borderBottomLeftRadius: 8, background: '#212121', color: '#9CA3AF' }}>
<div style={{ display: 'flex', alignItems: 'center', }}>
<img loading="lazy" src={imageData} alt="Logo" style={{ width: '30px', borderRadius: 8, height: '30px', marginRight: '8px' }} />
<div style={{
+267 -264
View File
@@ -1,275 +1,278 @@
import React, { useEffect, useState } from 'react';
import Switch from '@mui/material/Switch';
import { Typography, Button } from '@mui/material';
import { useNavigate, Link, useParams } from "react-router-dom";
import { Bar } from 'react-chartjs-2';
import Grid from '@mui/material/Grid';
import SearchIcon from '@mui/icons-material/Search';
import NewReleasesIcon from '@mui/icons-material/NewReleases';
import MailOutlineIcon from '@mui/icons-material/MailOutline';
// import React, { useEffect, useState } from 'react';
// import Switch from '@mui/material/Switch';
// import { Typography, Button } from '@mui/material';
// import { useNavigate, Link, useParams } from "react-router-dom";
// import { Bar } from 'react-chartjs-2';
// import Grid from '@mui/material/Grid';
// import SearchIcon from '@mui/icons-material/Search';
// import NewReleasesIcon from '@mui/icons-material/NewReleases';
// import MailOutlineIcon from '@mui/icons-material/MailOutline';
const AnalyticsTab = (props) => {
const { userdata, globalUrl, serverside } = props;
const [checked, setChecked] = useState(false);
const [selectedOption, setSelectedOption] = useState('all');
const [expand, setExpand] = useState(false)
const isCloud = (window.location.host === "localhost:3002" || window.location.host === "shuffler.io") ? true : (process.env.IS_SSR === "true");
console.log("if you need this, work without react-rechartjs-2")
// const [checked, setChecked] = useState(false);
// const [selectedOption, setSelectedOption] = useState('all');
// const [expand, setExpand] = useState(false)
// const isCloud = (window.location.host === "localhost:3002" || window.location.host === "shuffler.io") ? true : (process.env.IS_SSR === "true");
const handleOptionChange = (option) => {
setSelectedOption(option);
// You can perform actions based on the selected option, such as filtering data
};
// const handleOptionChange = (option) => {
// setSelectedOption(option);
// // You can perform actions based on the selected option, such as filtering data
// };
const handleChange = (event) => {
setChecked(event.target.checked);
};
// const handleChange = (event) => {
// setChecked(event.target.checked);
// };
const allData = {
labels: ['Email analysis', 'Email Management', 'EDR to Ticket', 'Ticket Analysis'],
datasets: [
{
label: 'Revisions',
data: [12, 19, 3, 5], // Data for revisions
backgroundColor: '#FF8444',
borderWidth: 1,
// borderRadius: 60,
barPercentage: 0.7,
categoryPercentage: 0.5,
},
{
label: 'Runs',
data: [8, 15, 5, 8], // Data for runs
backgroundColor: '#9747FF',
borderWidth: 1,
// borderRadius: 60,
barPercentage: 0.7,
categoryPercentage: 0.5
}
]
};
// const allData = {
// labels: ['Email analysis', 'Email Management', 'EDR to Ticket', 'Ticket Analysis'],
// datasets: [
// {
// label: 'Revisions',
// data: [12, 19, 3, 5], // Data for revisions
// backgroundColor: '#FF8444',
// borderWidth: 1,
// // borderRadius: 60,
// barPercentage: 0.7,
// categoryPercentage: 0.5,
// },
// {
// label: 'Runs',
// data: [8, 15, 5, 8], // Data for runs
// backgroundColor: '#9747FF',
// borderWidth: 1,
// // borderRadius: 60,
// barPercentage: 0.7,
// categoryPercentage: 0.5
// }
// ]
// };
let data;
if (selectedOption === 'all') {
data = allData;
} else if (selectedOption === 'revisions') {
data = {
labels: allData.labels,
datasets: [allData.datasets[0]] // Show only revisions data
};
} else if (selectedOption === 'run') {
data = {
labels: allData.labels,
datasets: [allData.datasets[1]] // Show only runs data
};
}
// let data;
// if (selectedOption === 'all') {
// data = allData;
// } else if (selectedOption === 'revisions') {
// data = {
// labels: allData.labels,
// datasets: [allData.datasets[0]] // Show only revisions data
// };
// } else if (selectedOption === 'run') {
// data = {
// labels: allData.labels,
// datasets: [allData.datasets[1]] // Show only runs data
// };
// }
// Options for the chart
const options = {
scales: {
yAxes: [
{
ticks: {
beginAtZero: true
}
}
]
},
};
return (
<div style={{ width: 1030, marginLeft: 20, marginTop:10, paddingRight: 17, paddingLeft: 17}}>
<div style={{ width: 985, display: 'flex', alignItems: 'start', paddingTop: 16, paddingBottom: '48px', fontSize: '16px', color: '#ffffff', backgroundColor: '#1A1A1A', borderRadius: '16px', }}>
<div style={{ marginLeft: 18 }}>Timeline</div>
</div>
<div style={{ display: "flex", width: "100%", marginTop: 16 }}>
<div>
<div style={{ width: 595, alignItems: 'start', paddingTop: 16, paddingBottom: checked ? 28 : 20, marginRight: 20, fontSize: '16px', color: '#ffffff', backgroundColor: '#1A1A1A', borderRadius: '16px', }}>
<div style={{ display: "flex", alignItems: "center" }}>
<div style={{ marginLeft: 18, marginRight: 310 }}>Apps</div>
<Switch checked={checked}
onChange={handleChange} /> Category
<div style={{ marginLeft: 16, borderLeft: "1px solid #D9D9D9", width: 10, height: 15 }} />
<Link onClick={() => { setExpand(prevExpand => !prevExpand); }} style={{ color: "#FF8444" }}>Expand</Link>
</div>
{expand ? null :
<div style={{ marginTop: 8, display: "flex" }}>
<div style={{ marginLeft: 19, }}>
<Typography style={{ fontSize: 13, color: "#9E9E9E", textAlign: "start" }}>Onboarding</Typography>
<Grid container spacing={2} style={{ marginTop: 1 }} >
<Grid item xs={4}>
<div style={{ position: 'relative' }}>
<img src="/images/adminpage/app1.svg" style={{ margin: 'auto' }} />
{checked ?
<div style={{
position: 'absolute',
bottom: 0,
left: '50%',
top: 30,
transform: 'translateX(-50%)',
backgroundColor: '#2f2f2f',
borderRadius: '50%',
height: 24,
width: 24,
transition: 'transform 0.5s ease',
}}><SearchIcon style={{ width: 16, height: 16, marginTop: 5 }} /></div> :
null
}
</div>
</Grid>
<Grid item xs={4}>
<div style={{ position: 'relative' }}>
<img src="/images/adminpage/app1.svg" style={{ margin: 'auto' }} />
{checked ?
<div style={{
position: 'absolute',
bottom: 0,
left: '50%',
top: 30,
transform: 'translateX(-50%)',
backgroundColor: '#2f2f2f',
borderRadius: '50%',
height: 24,
width: 24,
transition: 'transform 0.5s ease',
}}><MailOutlineIcon style={{ width: 16, height: 16, marginTop: 5 }} /></div> :
null
}
</div>
</Grid>
<Grid item xs={4}>
<div style={{ position: 'relative' }}>
<img src="/images/adminpage/app1.svg" style={{ margin: 'auto' }} />
{checked ?
<div style={{
position: 'absolute',
bottom: 0,
left: '50%',
top: 30,
transform: 'translateX(-50%)',
backgroundColor: '#2f2f2f',
borderRadius: '50%',
height: 24,
width: 24,
transition: 'transform 0.5s ease',
}}><NewReleasesIcon style={{ width: 16, height: 16, marginTop: 5 }} /></div> :
null
}
</div>
</Grid>
</Grid>
</div>
<div style={{ borderLeft: "1px solid #494949", justifyContent: "center", alignItems: "center", marginTop: 40, marginLeft: 20, marginRight: 20, height: 40 }}></div>
<div style={{}}>
<Typography style={{ fontSize: 13, color: "#9E9E9E", textAlign: "start" }}>Other</Typography>
<Grid container spacing={2} style={{ marginTop: 1 }} >
<Grid item xs={4}>
<div style={{ position: 'relative' }}>
<img src="/images/adminpage/app1.svg" style={{ margin: 'auto' }} />
{checked ?
<div style={{
position: 'absolute',
bottom: 0,
left: '50%',
top: 30,
transform: 'translateX(-50%)',
backgroundColor: '#2f2f2f',
borderRadius: '50%',
height: 24,
width: 24,
transition: 'transform 0.5s ease',
}}><SearchIcon style={{ width: 16, height: 16, marginTop: 5 }} /></div> :
null
}
</div>
</Grid>
<Grid item xs={4}>
<div style={{ position: 'relative' }}>
<img src="/images/adminpage/app1.svg" style={{ margin: 'auto' }} />
{checked ?
<div style={{
position: 'absolute',
bottom: 0,
left: '50%',
top: 30,
transform: 'translateX(-50%)',
backgroundColor: '#2f2f2f',
borderRadius: '50%',
height: 24,
width: 24,
transition: 'transform 10s ease-in-out',
}}><NewReleasesIcon style={{ width: 16, height: 16, marginTop: 5 }} /></div> :
null
}
</div>
</Grid>
<Grid item xs={4}>
<div style={{ position: 'relative' }}>
<img src="/images/adminpage/app1.svg" style={{ margin: 'auto' }} />
{checked ?
<div style={{
position: 'absolute',
bottom: 0,
left: '50%',
top: 30,
transform: 'translateX(-50%)',
backgroundColor: '#2f2f2f',
borderRadius: '50%',
height: 24,
width: 24,
transition: 'transform 10s ease-in-out',
}}><MailOutlineIcon style={{ width: 16, height: 16, marginTop: 5 }} /></div> :
null
}
</div>
</Grid>
</Grid>
</div>
</div>}
</div>
<div style={{ width: 595, height: 322, marginTop: 16, paddingTop: 16, paddingBottom: '48px', fontSize: '16px', color: '#ffffff', backgroundColor: '#1A1A1A', borderRadius: '16px', }}>
<div style={{ marginLeft: 18, textAlign: "start" }}>Workflows</div>
<div style={{textAlign:"center"}}>
<Button style={{ textTransform: "capitalize", background: selectedOption === 'all' ? "#494949" : "#1A1A1A", borderRadius: selectedOption === 'all' ? 15 : null, color: "#FFFFFF" }} onClick={() => handleOptionChange('all')}>All</Button>
<Button style={{ textTransform: "capitalize", background: selectedOption === 'revisions' ? "#494949" : "#1A1A1A", borderRadius: selectedOption === 'revisions' ? 15 : null, color: "#FFFFFF" }} onClick={() => handleOptionChange('revisions')}>Revisions</Button>
<Button style={{ textTransform: "capitalize", background: selectedOption === 'run' ? "#494949" : "#1A1A1A", borderRadius: selectedOption === 'run' ? 15 : null, color: "#FFFFFF" }} onClick={() => handleOptionChange('run')}>Runs</Button>
</div>
<Bar data={data} options={options} style={{ width: 400, marginLeft: 25, padding:20,marginTop: 10 }} />
</div>
</div>
<div style={{ width: 375, height: 440, display: 'flex', alignItems: 'start', paddingTop: '16px', paddingBottom: '48px', fontSize: '16px', color: '#ffffff', backgroundColor: '#1A1A1A', borderRadius: '16px', }}>
<div style={{ marginLeft: 18 }}>Insights</div>
</div>
</div>
<div style={{ width: 985, paddingTop: 16, marginTop: 16, paddingBottom: '48px', fontSize: '16px', color: '#ffffff', backgroundColor: '#1A1A1A', borderRadius: '16px', }}>
<div style={{ marginLeft: 18, textAlign: 'start', marginBottom: 16 }}>Sessions Overview</div>
<div style={{ display: "flex", width: "100%", justifyContent: "center" }}>
<div style={{ fontSize: '16px',width: "30%", borderRadius: 8, background: '#212121', color: '#9CA3AF' }}>
<div style={{ marginTop: 21, color: "#ffffff", marginLeft:20,fontSize: 16, fontWeight: "bold", }}>
2.8 Hours
</div>
<div style={{ marginTop: 13, marginBottom: 16, marginLeft:20, }}>
Avg. Activity per session
</div>
</div>
<div style={{ marginLeft: 16, fontSize: '16px', width: "30%", borderRadius: 8, background: '#212121', color: '#9CA3AF' }}>
<div style={{ marginTop: 21, marginLeft:20, color: "#ffffff", fontSize: 16, fontWeight: "bold", }}>
/usercases/edr to ticket
</div>
<div style={{ marginTop: 13, marginLeft:20, marginBottom: 16, }}>
Last visited page
</div>
</div>
<div style={{ marginLeft: 16, fontSize: '16px', width: "30%", borderRadius: 8, background: '#212121', color: '#9CA3AF' }}>
<div style={{ marginTop: 21, marginLeft:20,color: "#ffffff", fontSize: 16, fontWeight: "bold", }}>
/workflow/email management
</div>
<div style={{ marginTop: 13, marginLeft:20, marginBottom: 16, }}>
Most visited page
</div>
</div>
</div>
</div>
</div>
);
// // Options for the chart
// const options = {
// scales: {
// yAxes: [
// {
// ticks: {
// beginAtZero: true
// }
// }
// ]
// },
// };
// return (
// <div style={{ width: 1030, marginLeft: 20, marginTop:10, paddingRight: 17, paddingLeft: 17}}>
// <div style={{ width: 985, display: 'flex', alignItems: 'start', paddingTop: 16, paddingBottom: '48px', fontSize: '16px', color: '#ffffff', backgroundColor: '#1A1A1A', borderRadius: '16px', }}>
// <div style={{ marginLeft: 18 }}>Timeline</div>
// </div>
// <div style={{ display: "flex", width: "100%", marginTop: 16 }}>
// <div>
// <div style={{ width: 595, alignItems: 'start', paddingTop: 16, paddingBottom: checked ? 28 : 20, marginRight: 20, fontSize: '16px', color: '#ffffff', backgroundColor: '#1A1A1A', borderRadius: '16px', }}>
// <div style={{ display: "flex", alignItems: "center" }}>
// <div style={{ marginLeft: 18, marginRight: 310 }}>Apps</div>
// <Switch checked={checked}
// onChange={handleChange} /> Category
// <div style={{ marginLeft: 16, borderLeft: "1px solid #D9D9D9", width: 10, height: 15 }} />
// <Link onClick={() => { setExpand(prevExpand => !prevExpand); }} style={{ color: "#FF8444" }}>Expand</Link>
// </div>
// {expand ? null :
// <div style={{ marginTop: 8, display: "flex" }}>
// <div style={{ marginLeft: 19, }}>
// <Typography style={{ fontSize: 13, color: "#9E9E9E", textAlign: "start" }}>Onboarding</Typography>
// <Grid container spacing={2} style={{ marginTop: 1 }} >
// <Grid item xs={4}>
// <div style={{ position: 'relative' }}>
// <img src="/images/adminpage/app1.svg" style={{ margin: 'auto' }} />
// {checked ?
// <div style={{
// position: 'absolute',
// bottom: 0,
// left: '50%',
// top: 30,
// transform: 'translateX(-50%)',
// backgroundColor: '#2f2f2f',
// borderRadius: '50%',
// height: 24,
// width: 24,
// transition: 'transform 0.5s ease',
// }}><SearchIcon style={{ width: 16, height: 16, marginTop: 5 }} /></div> :
// null
// }
// </div>
// </Grid>
// <Grid item xs={4}>
// <div style={{ position: 'relative' }}>
// <img src="/images/adminpage/app1.svg" style={{ margin: 'auto' }} />
// {checked ?
// <div style={{
// position: 'absolute',
// bottom: 0,
// left: '50%',
// top: 30,
// transform: 'translateX(-50%)',
// backgroundColor: '#2f2f2f',
// borderRadius: '50%',
// height: 24,
// width: 24,
// transition: 'transform 0.5s ease',
// }}><MailOutlineIcon style={{ width: 16, height: 16, marginTop: 5 }} /></div> :
// null
// }
// </div>
// </Grid>
// <Grid item xs={4}>
// <div style={{ position: 'relative' }}>
// <img src="/images/adminpage/app1.svg" style={{ margin: 'auto' }} />
// {checked ?
// <div style={{
// position: 'absolute',
// bottom: 0,
// left: '50%',
// top: 30,
// transform: 'translateX(-50%)',
// backgroundColor: '#2f2f2f',
// borderRadius: '50%',
// height: 24,
// width: 24,
// transition: 'transform 0.5s ease',
// }}><NewReleasesIcon style={{ width: 16, height: 16, marginTop: 5 }} /></div> :
// null
// }
// </div>
// </Grid>
// </Grid>
// </div>
// <div style={{ borderLeft: "1px solid #494949", justifyContent: "center", alignItems: "center", marginTop: 40, marginLeft: 20, marginRight: 20, height: 40 }}></div>
// <div style={{}}>
// <Typography style={{ fontSize: 13, color: "#9E9E9E", textAlign: "start" }}>Other</Typography>
// <Grid container spacing={2} style={{ marginTop: 1 }} >
// <Grid item xs={4}>
// <div style={{ position: 'relative' }}>
// <img src="/images/adminpage/app1.svg" style={{ margin: 'auto' }} />
// {checked ?
// <div style={{
// position: 'absolute',
// bottom: 0,
// left: '50%',
// top: 30,
// transform: 'translateX(-50%)',
// backgroundColor: '#2f2f2f',
// borderRadius: '50%',
// height: 24,
// width: 24,
// transition: 'transform 0.5s ease',
// }}><SearchIcon style={{ width: 16, height: 16, marginTop: 5 }} /></div> :
// null
// }
// </div>
// </Grid>
// <Grid item xs={4}>
// <div style={{ position: 'relative' }}>
// <img src="/images/adminpage/app1.svg" style={{ margin: 'auto' }} />
// {checked ?
// <div style={{
// position: 'absolute',
// bottom: 0,
// left: '50%',
// top: 30,
// transform: 'translateX(-50%)',
// backgroundColor: '#2f2f2f',
// borderRadius: '50%',
// height: 24,
// width: 24,
// transition: 'transform 10s ease-in-out',
// }}><NewReleasesIcon style={{ width: 16, height: 16, marginTop: 5 }} /></div> :
// null
// }
// </div>
// </Grid>
// <Grid item xs={4}>
// <div style={{ position: 'relative' }}>
// <img src="/images/adminpage/app1.svg" style={{ margin: 'auto' }} />
// {checked ?
// <div style={{
// position: 'absolute',
// bottom: 0,
// left: '50%',
// top: 30,
// transform: 'translateX(-50%)',
// backgroundColor: '#2f2f2f',
// borderRadius: '50%',
// height: 24,
// width: 24,
// transition: 'transform 10s ease-in-out',
// }}><MailOutlineIcon style={{ width: 16, height: 16, marginTop: 5 }} /></div> :
// null
// }
// </div>
// </Grid>
// </Grid>
// </div>
// </div>}
// </div>
// <div style={{ width: 595, height: 322, marginTop: 16, paddingTop: 16, paddingBottom: '48px', fontSize: '16px', color: '#ffffff', backgroundColor: '#1A1A1A', borderRadius: '16px', }}>
// <div style={{ marginLeft: 18, textAlign: "start" }}>Workflows</div>
// <div style={{textAlign:"center"}}>
// <Button style={{ textTransform: "capitalize", background: selectedOption === 'all' ? "#494949" : "#1A1A1A", borderRadius: selectedOption === 'all' ? 15 : null, color: "#FFFFFF" }} onClick={() => handleOptionChange('all')}>All</Button>
// <Button style={{ textTransform: "capitalize", background: selectedOption === 'revisions' ? "#494949" : "#1A1A1A", borderRadius: selectedOption === 'revisions' ? 15 : null, color: "#FFFFFF" }} onClick={() => handleOptionChange('revisions')}>Revisions</Button>
// <Button style={{ textTransform: "capitalize", background: selectedOption === 'run' ? "#494949" : "#1A1A1A", borderRadius: selectedOption === 'run' ? 15 : null, color: "#FFFFFF" }} onClick={() => handleOptionChange('run')}>Runs</Button>
// </div>
// <Bar data={data} options={options} style={{ width: 400, marginLeft: 25, padding:20,marginTop: 10 }} />
// </div>
// </div>
// <div style={{ width: 375, height: 440, display: 'flex', alignItems: 'start', paddingTop: '16px', paddingBottom: '48px', fontSize: '16px', color: '#ffffff', backgroundColor: '#1A1A1A', borderRadius: '16px', }}>
// <div style={{ marginLeft: 18 }}>Insights</div>
// </div>
// </div>
// <div style={{ width: 985, paddingTop: 16, marginTop: 16, paddingBottom: '48px', fontSize: '16px', color: '#ffffff', backgroundColor: '#1A1A1A', borderRadius: '16px', }}>
// <div style={{ marginLeft: 18, textAlign: 'start', marginBottom: 16 }}>Sessions Overview</div>
// <div style={{ display: "flex", width: "100%", justifyContent: "center" }}>
// <div style={{ fontSize: '16px',width: "30%", borderRadius: 8, background: '#212121', color: '#9CA3AF' }}>
// <div style={{ marginTop: 21, color: "#ffffff", marginLeft:20,fontSize: 16, fontWeight: "bold", }}>
// 2.8 Hours
// </div>
// <div style={{ marginTop: 13, marginBottom: 16, marginLeft:20, }}>
// Avg. Activity per session
// </div>
// </div>
// <div style={{ marginLeft: 16, fontSize: '16px', width: "30%", borderRadius: 8, background: '#212121', color: '#9CA3AF' }}>
// <div style={{ marginTop: 21, marginLeft:20, color: "#ffffff", fontSize: 16, fontWeight: "bold", }}>
// /usercases/edr to ticket
// </div>
// <div style={{ marginTop: 13, marginLeft:20, marginBottom: 16, }}>
// Last visited page
// </div>
// </div>
// <div style={{ marginLeft: 16, fontSize: '16px', width: "30%", borderRadius: 8, background: '#212121', color: '#9CA3AF' }}>
// <div style={{ marginTop: 21, marginLeft:20,color: "#ffffff", fontSize: 16, fontWeight: "bold", }}>
// /workflow/email management
// </div>
// <div style={{ marginTop: 13, marginLeft:20, marginBottom: 16, }}>
// Most visited page
// </div>
// </div>
// </div>
// </div>
// </div>
// );
};
export default AnalyticsTab;
+2 -1
View File
@@ -842,7 +842,7 @@ const AppAuthTab = memo((props) => {
hitsPerPage={5}
searchQuery={searchQuery}
globalUrl={globalUrl}
isCloud={isCloud}
isCloud={isCloud !== true}
userdata={userdata}
getAppAuthentication={getAppAuthentication}
/>
@@ -878,6 +878,7 @@ const AppAuthTab = memo((props) => {
style={{ color: '#1a1a1a', textTransform: 'none', backgroundColor: "#FF8444", marginLeft:"auto", borderRadius: 4,fontSize: 16, minWidth: 162, height: 40, boxShadow:'none', }}
variant="contained"
color="primary"
disabled={!isCloud}
onClick={() => setShowAppModal(true)}
>
Add App Auth
+1 -1
View File
@@ -198,7 +198,7 @@ export const findSpecificApp = (framework, inputcategory) => {
id: "",
}
} else {
console.log("findSpecificApp: unknown category: ", category)
//console.log("findSpecificApp: unknown category: ", category)
}
return null
+64 -18
View File
@@ -1,4 +1,4 @@
import React, { useCallback, useEffect, useState } from 'react';
import React, { memo, useCallback, useEffect, useState } from 'react';
import { useNavigate } from 'react-router';
import {
@@ -29,8 +29,15 @@ import theme from "../theme.jsx";
import YAML from 'yaml';
import { toast } from 'react-toastify';
import { Link } from 'react-router-dom';
import { InstantSearch, connectHits, connectSearchBox } from 'react-instantsearch-dom';
import algoliasearch from "algoliasearch/lite";
const AppModal = ({ open, onClose, app, globalUrl, getApps }) => {
const searchClient = algoliasearch(
"JNSS5CFDZZ",
"db08e40265e2941b9a7d8f644b6e5240"
);;
const AppModal = ({ open, onClose, app, globalUrl, getApps}) => {
const [frameworkData, setFrameworkData] = useState({})
const [userdata, setUserdata] = useState({})
@@ -79,8 +86,7 @@ const AppModal = ({ open, onClose, app, globalUrl, getApps }) => {
});
}, [app]);
const getFramework = () => {
const getFramework = () => {
fetch(globalUrl + "/api/v1/apps/frameworkConfiguration", {
method: "GET",
headers: {
@@ -295,8 +301,6 @@ const AppModal = ({ open, onClose, app, globalUrl, getApps }) => {
})
}
useEffect(() => {
setUsecaseLoading(true)
getAvailableWorkflows()
@@ -329,6 +333,7 @@ const AppModal = ({ open, onClose, app, globalUrl, getApps }) => {
//delete apps from local storage
localStorage.removeItem("apps");
getApps();
onClose();
}, 1000);
} else {
toast("Failed deleting app. Does it still exist?");
@@ -720,18 +725,7 @@ const AppModal = ({ open, onClose, app, globalUrl, getApps }) => {
textAlign: "start",
flex: 1,
}}>
<Typography
variant="h6"
sx={{
fontFamily: theme?.typography?.fontFamily,
fontSize: '24px',
fontWeight: 600,
mb: 0.3,
color: '#fff'
}}
>
20
</Typography>
<WorkflowCard app={app}/>
<Typography
variant="body2"
sx={{
@@ -930,3 +924,55 @@ const AppModal = ({ open, onClose, app, globalUrl, getApps }) => {
};
export default AppModal;
const WorkflowCard = memo(({ app }) => {
const [name, setName] = useState("");
const [relatedWorkflows, setRelatedWorkflows] = useState(0);
const WorkflowHits = ({ hits }) => {
useEffect(() => {
setRelatedWorkflows(hits?.length || 0);
}, [hits]);
return null;
};
const CustomWorkflowHits = connectHits(WorkflowHits);
const SearchBox = ({ refine, currentRefinement }) => {
useEffect(() => {
if (name.length > 0 && currentRefinement !== name) {
refine(name?.split(" ")[0]);
}
}, [name, refine, currentRefinement]);
return null;
};
const CustomSearchBox = connectSearchBox(SearchBox);
useEffect(() => {
if (app?.name && app.name.trim().length > 0 && name !== app.name) {
const formattedName = app.name
.charAt(0)
.toUpperCase() + app.name.substring(1)
.replaceAll("_", " ");
setName(formattedName);
}
}, [app?.name]);
return (
<Typography variant="h4">
{name.length > 0 ? (
<InstantSearch key={name} searchClient={searchClient} indexName="workflows">
<CustomSearchBox defaultRefinement={name?.split(" ")[0]}/>
<CustomWorkflowHits />
{relatedWorkflows}
</InstantSearch>
) : (
relatedWorkflows
)}
</Typography>
);
});
+1 -1
View File
@@ -88,7 +88,7 @@ const AppSearchButtons = (props) => {
const foundApp = findSpecificApp(appFramework, appType)
if (foundApp === undefined || foundApp === null) {
console.log("AppSearchButtons: App not found in appFramework: " + appType)
//console.log("AppSearchButtons: App not found in appFramework: " + appType)
return null
}
+1 -1
View File
@@ -126,7 +126,7 @@ const CacheView = memo((props) => {
const listOrgCache = (orgId, category) => {
const url = `${globalUrl}/api/v1/orgs/${orgId}/list_cache${category !== undefined ? `?category=${category.replaceAll(" ", "_")}` : ""}`
const url = `${globalUrl}/api/v1/orgs/${orgId}/list_cache${category !== undefined ? `?category=${category.replaceAll(" ", "%20")}` : ""}`
fetch(url, {
method: "GET",
headers: {
+75 -72
View File
@@ -65,80 +65,83 @@ export const LoadStats = (globalUrl, cachekey) => {
}
const DashboardBarchart = (props) => {
const { timelineData, title, height, } = props;
var inputHeight = 15
if (height !== undefined && height !== null) {
inputHeight = height
}
// this is clearly unfinished and not worth my time.
// refer to health page to see how i made it work there w/o using chartjs
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
}
}
}
}
// const { timelineData, title, height, } = props;
// var inputHeight = 15
// if (height !== undefined && height !== null) {
// inputHeight = height
// }
return (
<Bar
data={timelineData}
options={barOptions}
height={inputHeight}
getElementAtEvent={(elements) => {
if (elements && elements.length > 0) {
//toast("Click event")
console.log("Clicked: ", elements)
}
}}
/>
)
// 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;
+4 -4
View File
@@ -1,8 +1,8 @@
import React, { useEffect, useState, useContext } from 'react';
import OrgHeaderexpanded from "./OrgHeaderexpandedNew.jsx";
import OrgHeader from './OrgHeaderNew.jsx';
import OrgHeaderexpanded from "../components/OrgHeaderexpandedNew.jsx";
import OrgHeader from '../components/OrgHeaderNew.jsx';
import { toast } from "react-toastify";
import CloudSyncTab from './CloudSyncTab.jsx';
import CloudSyncTab from '../components/CloudSyncTab.jsx';
import {
FileCopy as FileCopyIcon,
} from "@mui/icons-material";
@@ -443,4 +443,4 @@ If you're interested, please let me know a time that works for you, or set up a
)
}
export default EditOrgTab;
export default EditOrgTab;
+1 -1
View File
@@ -581,7 +581,7 @@ const EditWorkflow = (props) => {
</Typography>
<Typography variant="body2" color="textSecondary" style={{ marginTop: 30, marginBottom: 10, }}>
Multi-Tenant Workflows. Make one workflow, and keep a separate, synced copy in all your tenants. Control distributed auth, runtime locations, files, datastore keys etc. (<b>late beta</b> - contact support@shuffler.io if you want a demo. Please try it!)
Multi-Tenant Workflows. Make one workflow, and keep a separate, synced copy in all your tenants. Control distributed auth, runtime locations, files, datastore keys etc. (contact support@shuffler.io if you want a demo. Please try it!)
</Typography>
{userdata !== undefined && userdata !== null && userdata.orgs !== undefined && userdata.orgs !== null && userdata.orgs.length > 0 ?
+1 -1
View File
@@ -421,7 +421,7 @@ const EnvironmentTab = memo((props) => {
-e AUTH="${auth}" \\
-e ENVIRONMENT_NAME="${environment.Name}" \\
-e ORG="${environment.org_id}" \\
-e SHUFFLE_WORKER_IMAGE="ghcr.io/shuffle/shuffle-worker:nightly" \\
-e SHUFFLE_WORKER_IMAGE="ghcr.io/shuffle/shuffle-worker:latest" \\
-e SHUFFLE_SWARM_CONFIG=run \\
-e SHUFFLE_LOGS_DISABLED=true \\
-e BASE_URL="${newUrl}" \\${addProxy ? `
+1 -1
View File
@@ -525,7 +525,7 @@ useEffect(() => {
<Divider style={{ marginBottom: 10, }} />
<Typography color="textSecondary" align="center" style={{ marginTop: 5, marginBottom: 5, fontSize: 18 }}>
Version: 2.0.0-rc7
Version: 2.0.0
</Typography>
</Menu>
</span>
@@ -549,7 +549,7 @@ const OrgHeaderexpandedNew = (props) => {
{isCloud ? (
<div style={{ marginLeft: 13, fontSize: 16, color: "#9E9E9E" }} >
Region
Change Region
<RegionChangeModal selectedOrganization={selectedOrganization} setSelectedRegion={setSelectedRegion} userdata={userdata} handleSendChangeRegionMail={handleSendChangeRegionMail} />
</div>
) : null}
@@ -3,7 +3,6 @@ import { Link, useNavigate, useLocation } from "react-router-dom";
import Billing from "../components/Billing.jsx";
import Priorities from "../components/Priorities.jsx";
import Branding from "../components/Branding.jsx";
import AnalyticsTab from '../components/AnalyticsTab.jsx';
import EditOrgTab from '../components/EditOrgTab.jsx';
import CloudSyncTab from '../components/CloudSyncTab.jsx';
import SSOTab from "../components/ssoTab.jsx"
+13 -1
View File
@@ -361,7 +361,7 @@ const ParsedAction = (props) => {
}
if (keyorder.join(",") !== newkeyorder.join(",")) {
console.log("KEYORDER CHANGED! DID ACTION AS WELL?", keyorder, newkeyorder)
//console.log("KEYORDER CHANGED! DID ACTION AS WELL?", keyorder, newkeyorder)
setSelectedActionParameters(newparams)
selectedAction.parameters = newparams
@@ -1160,6 +1160,12 @@ const ParsedAction = (props) => {
selectedActionParameters[1].value = splitparsed[1]
selectedAction.parameters[1].value = splitparsed[1]
if (splitparsed.length > 2) {
toast.warn("Filter list only supports filtering at the first level. If you want multi-level filtering, please use the 'execute python' action with the 'filter a list' function in the code editor.")
} else if (selectedAction.parameters[1].value.includes(".#")) {
toast.warn("This filter may not work due to using .# indexing. Please use the 'execute python' action and try the 'filter a list' function in the code editor.")
}
} else {
// Remove .# and after
const splitparsed = parsedvalue.split(".#")
@@ -3658,6 +3664,12 @@ const ParsedAction = (props) => {
</Box>
);
if (selectedApp.name === "email") {
//hideBody = false
showButtonField = false
hideBodyButtonValue = null
}
if ((multiline === undefined || multiline === false) && ((data?.autocompleted === true || data?.field_active === true) || data.name.startsWith("${") && data.name.endsWith("}"))) {
multiline = true
+14 -6
View File
@@ -89,6 +89,7 @@ const liquidFilters = [
const pythonFilters = [
{ "name": "Hello World", "value": `print("hello world")`, "example": `` },
{ "name": "Using Shuffle variables", "value": `import json\nnodevalue = r\"\"\"$exec\"\"\"\nif not nodevalue:\n nodevalue = r\"\"\"{\"sample\": \"string\", \"int\": 1}\"\"\"\n \njsondata = json.loads(nodevalue)\nprint(jsondata)`, "example": `` },
{ "name": "Filter a list", "value": `import json\nnodevalue = r\"\"\"$exec\"\"\"\nif not nodevalue:\n nodevalue = r\"\"\"[{\"sample\": \"string\", \"int\": 1, "malicious": "no"}, {\"sample\": \"string2\", \"int\": 1, "malicious": "yes"}]\"\"\"\n \njsondata = json.loads(nodevalue)\nfiltered = []\nfor item in jsondata:\n try:\n if item[\"malicious\"] == \"yes\":\n filtered.append(item)\n except:\n pass\nprint(json.dumps(filtered))`, "example": `` },
{ "name": "Print Execution ID", "value": `print(self.current_execution_id)`, "example": `` },
{ "name": "Get full execution details", "value": `print(self.full_execution)`, "example": `` },
{ "name": "Use files", "value": `# Create a sample file\nfiles = [{\n \"filename\": \"test.txt\",\n \"data\": \"Testdata\"\n}]\nret = self.set_files(files)\n\n# Get the content of the file from Shuffle storage\n# Originally a byte string in the \"data\" key\nfile_content = (self.get_file(ret[0])[\"data\"]).decode()\nprint(file_content)`, "example": `` },
@@ -657,6 +658,7 @@ const CodeEditor = (props) => {
}
if (!inputvariable.includes(".")) {
inputvariable = inputvariable.toLowerCase()
return inputvariable
}
@@ -665,6 +667,9 @@ const CodeEditor = (props) => {
var removedIndexes = 0
for (var key in itemsplit) {
var tmpitem = itemsplit[key]
if (key == 0) {
tmpitem = tmpitem.toLowerCase()
}
// Makes sure #0 and # are same, as we only visualize first one anyway
if (tmpitem.startsWith("#")) {
@@ -695,20 +700,24 @@ const CodeEditor = (props) => {
var variable_occurence = current_code_line.match(/[\\]{0,1}[$]{1}([a-zA-Z0-9_@-]+\.?){1}([a-zA-Z0-9#_@-]+\.?){0,}/g)
if (!variable_occurence) {
continue;
continue
}
var new_occurences = variable_occurence.filter((occurrence) => occurrence[0]);
variable_occurence = new_occurences
//var new_occurences = variable_occurence.filter((occurrence) => occurrence[0]);
//variable_occurence = new_occurences
variable_occurence = variable_occurence.filter((occurrence) => occurrence[0]);
// Checks code lines, not variable occurences. Then remaps later
var dollar_occurence = [];
for (let ch = 0; ch < current_code_line.length; ch++) {
//if (current_code_line[ch] === '$' && (ch === 0)) {
if (current_code_line[ch] === '$') {
dollar_occurence.push(ch);
dollar_occurence.push(ch)
}
}
// Lowercase anything between the $ and first .
var dollar_occurence_len = []
try {
for (let occ = 0; occ < variable_occurence.length; occ++) {
@@ -1005,7 +1014,6 @@ const CodeEditor = (props) => {
// Replace quotes with nothing
} else {
console.log("NO TYPE? ", typeof new_input)
try {
new_input = new_input.toString()
} catch (e) {
@@ -1897,7 +1905,7 @@ const CodeEditor = (props) => {
overflow: "hidden",
}}
onClick={() => {
console.log(innerdata.example)
//console.log(innerdata.example)
//const handleClick = (item) => {
handleItemClick([innerdata]);
@@ -299,7 +299,7 @@ const WorkflowValidationTimeline = (props) => {
var scheduleNotStarted = false
if (workflow.validation !== undefined && workflow.validation !== null && workflow.validation.validation_ran === false) {
console.log("Validation didn't run or get set for workflow. Why?")
//console.log("Validation didn't run or get set for workflow. Why?")
return null
}
+501 -95
View File
@@ -74,6 +74,7 @@ import {
import {
Storage as StorageIcon,
Code as CodeIcon,
Folder as FolderIcon,
VerifiedUser as VerifiedUserIcon,
@@ -503,7 +504,7 @@ const AngularWorkflow = (defaultprops) => {
const [allRevisions, setAllRevisions] = useState([])
const [menuPosition, setMenuPosition] = useState(null);
const [showDropdown, setShowDropdown] = React.useState(false);
const [subflowActionList, setSubflowActionList] = React.useState([]);
const [triggerActionList, setTriggerActionList] = React.useState([]);
const [apps, setApps] = React.useState([]);
const [filteredApps, setFilteredApps] = React.useState([]);
@@ -574,11 +575,12 @@ const AngularWorkflow = (defaultprops) => {
}, [editWorkflowModalOpen])
useEffect(() => {
if (selectedTrigger !== undefined && selectedTrigger?.parameters !== undefined && selectedTrigger?.parameters !== null && selectedTrigger?.parameters?.length > 1) {
// Right now just setting for the subflow
setSelectedTriggerValue(selectedTrigger?.parameters[1]?.value)
}
}, [selectedTrigger])
if(selectedTrigger?.trigger_type === "SUBFLOW"){
setSelectedTriggerValue(workflow.triggers[selectedTriggerIndex].parameters[1].value || "")
} else if(selectedTrigger?.trigger_type === "USERINPUT"){
setSelectedTriggerValue(workflow.triggers[selectedTriggerIndex].parameters[0].value || "Do you want to continue the workflow? Start parameters: $exec")
}
}, [selectedTriggerIndex, selectedTrigger, workflow])
useEffect(() => {
if(selectedEdge && Object.keys(selectedEdge).length > 0){
@@ -1136,23 +1138,66 @@ const AngularWorkflow = (defaultprops) => {
useEffect(() => {
// if (subflowActionList.length === 0) {
const newActionList = [];
// FIXME: Have previous execution values in here
newActionList.push({
type: "Runtime Argument",
name: "Runtime Argument",
value: "$exec",
highlight: "exec",
autocomplete: "exec",
example: "hello",
})
newActionList.push({
type: "Shuffle Database",
name: "Shuffle Database",
if (workflowExecutions.length > 0) {
for (let execution of workflowExecutions) {
const execArg = execution.execution_argument;
if (execArg && execArg.length > 0) {
const valid = validateJson(execArg);
if (valid.valid) {
newActionList.push({
type: "Runtime Argument",
name: "Runtime Argument",
value: "$exec",
highlight: "exec",
autocomplete: "exec",
example: valid.result,
})
break
}
}
}
}
// Add default Runtime Argument if none were added
if (newActionList.length === 0) {
newActionList.push({
type: "Runtime Argument",
name: "Runtime Argument",
value: "$exec",
highlight: "exec",
autocomplete: "exec",
example: "",
})
}
// Add Shuffle DB with cache keys if available
let cacheKey = {
type: "Shuffle DB",
name: "Shuffle DB",
value: "$shuffle_cache",
highlight: "shuffle_db",
highlight: "shuffle_cache",
autocomplete: "shuffle_cache",
example: "hello",
})
example: "Hello",
}
if (listCache?.keys?.length > 0) {
cacheKey.example = {};
for (let item of listCache.keys) {
if (item.key) {
let itemValue = item.value ?? "";
if (itemValue.length > 10000) {
itemValue = "";
}
cacheKey.example[item.key.split(" ").join("_")] = { value: itemValue };
}
}
}
newActionList.push(cacheKey);
// FIXME: Have previous execution values in here
if (
workflow.workflow_variables !== null &&
workflow.workflow_variables !== undefined &&
@@ -1216,7 +1261,7 @@ const AngularWorkflow = (defaultprops) => {
const validated = validateJson(foundResult.result)
if (validated.valid) {
exampledata = validateJson.result
exampledata = validated.result
break
}
}
@@ -1234,7 +1279,7 @@ const AngularWorkflow = (defaultprops) => {
}
}
setSubflowActionList(newActionList);
setTriggerActionList(newActionList);
}, [workflow.workflow_variables, workflow.execution_variables, workflow, selectedTrigger]);
@@ -1687,7 +1732,7 @@ const AngularWorkflow = (defaultprops) => {
if (filter === undefined || filter === null || filter.toUpperCase() === "ALL") {
// Check for
if (executionFilter !== undefined && executionFilter !== null && executionFilter.length > 0) {
if (executionFilter !== undefined && executionFilter !== null && executionFilter.length > 0 && filter !== "ALL") {
filter = executionFilter
} else {
filter = "ALL"
@@ -2715,10 +2760,12 @@ const AngularWorkflow = (defaultprops) => {
return newExample
}
/*
for (var i = 0; i < found.length; i++) {
//newExample = newExample.replace(found[i], "REQUIRED")
newExample = newExample.replace(found[i], "REPLACE_ME")
}
*/
return newExample
}
@@ -7116,7 +7163,6 @@ const AngularWorkflow = (defaultprops) => {
data: newcybranch,
};
console.log("SHOULD STITCH WITH STARTNODE");
cy.add(edgeToBeAdded);
}
@@ -12416,13 +12462,7 @@ const AngularWorkflow = (defaultprops) => {
// Get parent nodes and their data
if (selectedEdge) {
let nodeId;
if (type === "source") {
nodeId = selectedEdge.source;
} else if (type === "destination") {
nodeId = selectedEdge.target;
}
const nodeId = selectedEdge.target;
const node = cy.getElementById(nodeId);
if (node?.length > 0) {
@@ -13457,7 +13497,7 @@ const AngularWorkflow = (defaultprops) => {
Shuffle AI
</Typography>
<Typography variant="body2" color="textSecondary">
What you write here will be fed to the Shuffle AI to generate a change for the selected action or field. Best used for when you are stuck with formatting. Uses your AI credits (resets monthly). Alpha feature. Please give feedback to support@shuffler.io {"<"}3
What you write here will be fed to the Shuffle AI to generate a change for the selected action or field. Best used for when you are stuck with formatting. Uses your AI credits (resets monthly). <b>Beta feature.</b> Please give feedback to support@shuffler.io {"<"}3
</Typography>
<TextField
@@ -13784,6 +13824,7 @@ const AngularWorkflow = (defaultprops) => {
setSourceValue({});
setConditionValue({});
setDestinationValue({});
navigate("")
}}
color="secondary"
>
@@ -13794,6 +13835,7 @@ const AngularWorkflow = (defaultprops) => {
variant="contained"
onClick={() => {
setSelectedEdge({});
navigate("")
var data = {
condition: conditionValue,
@@ -14618,43 +14660,62 @@ const AngularWorkflow = (defaultprops) => {
};
const handleItemClick = (values) => {
console.log("VALUES: ", values)
if (values === undefined || values === null || values.length === 0) {
return;
console.log("VALUES: ", values)
if (values === undefined || values === null || values.length === 0) {
return;
}
// Get the base autocomplete value and normalize it
let toComplete = values[0].autocomplete.toLowerCase().replaceAll(" ", "_");
// Add $ prefix if not already present
if (!toComplete.startsWith("$")) {
toComplete = "$" + toComplete;
}
// Add any additional path components
for (let key in values) {
if (key === "0" || !values[key].autocomplete) {
continue;
}
toComplete += values[key].autocomplete;
}
/*
workflow.triggers[selectedTriggerIndex].parameters[1].value
.trim()
.endsWith("$")
? values[0].autocomplete
: "$" + values[0].autocomplete;
var foundField;
// Update the field value
if(selectedTrigger?.trigger_type === "SUBFLOW"){
foundField = document.getElementById("subflow_exec_field");
}else if(selectedTrigger?.trigger_type === "USERINPUT"){
foundField = document.getElementById("userinput_info_field");
}
for (var key in values) {
if (key === 0 || values[key].autocomplete.length === 0) {
continue;
}
if (foundField) {
// Get current cursor position
const cursorPos = foundField.selectionStart;
const currentValue = foundField.value;
toComplete += values[key].autocomplete
}
*/
if (selectedTrigger.name === "Shuffle Workflow") {
const toComplete = workflow?.triggers?.[selectedTriggerIndex]?.parameters?.[1]?.value + "$" + values[0]?.autocomplete
// selectedTrigger.parameters[1].value = toComplete
workflow.triggers[selectedTriggerIndex].parameters[1].value = toComplete
const foundfield = document.getElementById("subflow_exec_field")
if (foundfield !== undefined && foundfield !== null) {
foundfield.value = toComplete
}
setWorkflow(workflow)
// Insert the new value at cursor position
const newValue = currentValue.slice(0, cursorPos) + toComplete + currentValue.slice(cursorPos);
foundField.value = newValue;
// Update state
setSelectedTriggerValue(newValue);
if(selectedTrigger?.trigger_type === "SUBFLOW"){
workflow.triggers[selectedTriggerIndex].parameters[1].value = newValue
}else if(selectedTrigger?.trigger_type === "USERINPUT"){
workflow.triggers[selectedTriggerIndex].parameters[0].value = newValue
}
setUpdate(Math.random());
setShowDropdown(false);
setMenuPosition(null);
};
// Set cursor position after inserted text
foundField.setSelectionRange(cursorPos + toComplete.length, cursorPos + toComplete.length);
}
setWorkflow(workflow);
setUpdate(Math.random());
setShowDropdown(false);
setMenuPosition(null);
};
const subflowtypes = [
{
@@ -14681,7 +14742,7 @@ const AngularWorkflow = (defaultprops) => {
workflow.triggers[triggerIndex].parameters[paramIndex].value = newData;
// Update workflow state to trigger re-render
setWorkflow({...workflow});
setWorkflow(workflow);
setSelectedTriggerValue(newData)
setLastSaved(false);
}
@@ -15171,7 +15232,7 @@ const AngularWorkflow = (defaultprops) => {
"name": workflow.triggers[selectedTriggerIndex].parameters[1].name,
"value": parsedvalue,
"field_number": 1,
"actionlist": subflowActionList,
"actionlist": triggerActionList,
"field_id": "subflow_exec_field",
})
}}
@@ -15216,15 +15277,13 @@ const AngularWorkflow = (defaultprops) => {
fullWidth
color="primary"
placeholder="Some execution data"
value={
selectedTriggerValue
}
value={selectedTriggerValue || ""}
onChange={(e) => {
setLastSaved(false)
setSelectedTriggerValue(e.target.value)
}}
onBlur={() => {
workflow.triggers[selectedTriggerIndex].parameters[1].value = selectedTriggerValue
onBlur={(e) => {
workflow.triggers[selectedTriggerIndex].parameters[1].value = e.target.value
setWorkflow(workflow)
}}
/>
@@ -15242,16 +15301,17 @@ const AngularWorkflow = (defaultprops) => {
marginTop: 2,
}}
>
{subflowActionList.map((innerdata) => {
{triggerActionList.map((innerdata) => {
const icon =
innerdata.type === "action" ? (
<AppsIcon style={{ marginRight: 10 }} />
) : innerdata.type === "workflow_variable" ||
innerdata.type === "execution_variable" ? (
<FavoriteBorderIcon style={{ marginRight: 10 }} />
) : (
<ScheduleIcon style={{ marginRight: 10 }} />
);
) : innerdata.type === "Shuffle DB" ?
<StorageIcon style={{ marginRight: 10, }} />
:
<ScheduleIcon style={{ marginRight: 10 }} />
const handleExecArgumentHover = (inside) => {
var exec_text_field = document.getElementById(
@@ -16687,21 +16747,43 @@ const AngularWorkflow = (defaultprops) => {
</div>
*/}
<div style={{ flex: "6", marginTop: 10, }}>
<div
style={{
marginTop: "20px",
marginBottom: "7px",
display: "flex",
}}
>
<div style={{ flex: "10" }}>
<b>Information</b>
<Typography variant="body2" color="textSecondary">
The information you want to show the user. Supports variables. Supports Markdown & HTML.
</Typography>
</div>
<div style={{ marginTop: "20px", marginBottom: "5px", display: "flex", width: "100%", flexDirection: "column" }}>
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", width: "100%" }}>
<b>Information</b>
<Tooltip title="Expand editor window" placement="top">
<IconButton
onClick={(event) => {
event.preventDefault()
setCodeEditorModalOpen(true)
setActiveDialog("codeeditor")
var parsedvalue = workflow?.triggers[selectedTriggerIndex]?.parameters[0]?.value
navigate(`?trigger_id=${selectedTrigger.id}&trigger_field=${"alertinfo"}&trigger_name=${selectedTrigger.label}`)
setEditorData({
"name": workflow.triggers[selectedTriggerIndex].parameters[0].name,
"value": parsedvalue,
"field_number": 0,
"actionlist": triggerActionList,
"field_id": "information_field",
})
}}
>
<OpenInFullIcon
style={{
color: "rgba(255,255,255,0.7)",
height: 20,
width: 20
}}
/>
</IconButton>
</Tooltip>
</div>
<Typography color="textSecondary" sx={{fontSize: "13px"}}>
The information you want to show the user. Supports variables. Supports Markdown & HTML.
</Typography>
</div>
<TextField
id="userinput_info_field"
style={{
backgroundColor: theme.palette.inputColor,
borderRadius: theme.palette?.borderRadius,
@@ -16709,19 +16791,347 @@ const AngularWorkflow = (defaultprops) => {
InputProps={{
style: {
},
endAdornment: (
<InputAdornment position="end">
<Tooltip title="Autocomplete text" placement="top">
<AddCircleOutlineIcon
style={{ cursor: "pointer" }}
onClick={(event) => {
setMenuPosition({
top: event.pageY + 10,
left: event.pageX + 10,
});
//setShowDropdownNumber(3)
setShowDropdown(true);
}}
/>
</Tooltip>
</InputAdornment>
),
}}
fullWidth
rows="4"
multiline
defaultValue={
workflow.triggers !== undefined && workflow.triggers !== null && workflow.triggers[selectedTriggerIndex].parameters !== undefined && workflow.triggers[selectedTriggerIndex].parameters.length > 0 && workflow.triggers[selectedTriggerIndex].parameters[0] !== undefined && workflow.triggers[selectedTriggerIndex].parameters[0].value !== undefined ? workflow.triggers[selectedTriggerIndex].parameters[0].value : ""
}
value={selectedTriggerValue}
onChange={(e) => {
setLastSaved(false)
setSelectedTriggerValue(e.target.value)
}}
color="primary"
placeholder=""
onBlur={(e) => {
setTriggerTextInformationWrapper(e.target.value);
}}
/>
{!showDropdown ? null :
<Menu
anchorReference="anchorPosition"
anchorPosition={menuPosition}
onClose={() => {
handleMenuClose();
}}
open={!!menuPosition}
style={{
border: `2px solid #FF8544`,
color: "white",
marginTop: 2,
}}
>
{triggerActionList.map((innerdata) => {
const icon =
innerdata.type === "action" ? (
<AppsIcon style={{ marginRight: 10 }} />
) : innerdata.type === "workflow_variable" ||
innerdata.type === "execution_variable" ? (
<FavoriteBorderIcon style={{ marginRight: 10 }} />
) : innerdata.type === "Shuffle DB" ?
<StorageIcon style={{ marginRight: 10, }} />
:
<ScheduleIcon style={{ marginRight: 10 }} />
const handleExecArgumentHover = (inside) => {
var exec_text_field = document.getElementById(
"execution_argument_input_field"
);
if (exec_text_field !== null) {
if (inside) {
exec_text_field.style.border = "2px solid #FF8544";
} else {
exec_text_field.style.border = "";
}
}
// Also doing arguments
if (
workflow.triggers !== undefined &&
workflow.triggers !== null &&
workflow.triggers.length > 0
) {
for (let triggerkey in workflow.triggers) {
const item = workflow.triggers[triggerkey];
if (cy !== undefined && cy !== null) {
var node = cy.getElementById(item.id);
if (node.length > 0) {
if (inside) {
node.addClass("shuffle-hover-highlight");
} else {
node.removeClass("shuffle-hover-highlight");
}
}
}
}
}
}
const handleActionHover = (inside, actionId) => {
if (cy !== undefined && cy !== null) {
var node = cy.getElementById(actionId);
if (node.length > 0) {
if (inside) {
node.addClass("shuffle-hover-highlight");
} else {
node.removeClass("shuffle-hover-highlight");
}
}
}
};
const handleMouseover = () => {
if (innerdata.type === "Runtime Argument") {
handleExecArgumentHover(true);
} else if (innerdata.type === "action") {
handleActionHover(true, innerdata.id);
}
};
const handleMouseOut = () => {
if (innerdata.type === "Runtime Argument") {
handleExecArgumentHover(false);
} else if (innerdata.type === "action") {
handleActionHover(false, innerdata.id);
}
};
var parsedPaths = [];
if (innerdata.type === "workflow_variable") {
// Try to parse the value if it's a string that could be JSON
if (typeof innerdata.value === "string") {
try {
const parsedValue = JSON.parse(innerdata.value)
if (typeof parsedValue === "object") {
parsedPaths = GetParsedPaths(parsedValue, "");
}
} catch (e) {
// Not valid JSON, use the value directly
parsedPaths = GetParsedPaths(innerdata.value, "");
}
} else if (typeof innerdata.value === "object") {
parsedPaths = GetParsedPaths(innerdata.value, "");
}
} else if (typeof innerdata.example === "object") {
parsedPaths = GetParsedPaths(innerdata.example, "");
}
const coverColor = "#82ccc3"
return parsedPaths.length > 0 ? (
<span>
{/*
<NestedMenuItem
key={innerdata.name}
label={
<div style={{ display: "flex" }}>
{icon} {innerdata.name}
</div>
}
parentMenuOpen={!!menuPosition}
style={{
backgroundColor: theme.palette.inputColor,
color: "white",
minWidth: 250,
}}
onClick={() => {
handleItemClick([innerdata]);
}}
>
{parsedPaths.map((pathdata, index) => {
// FIXME: Should be recursive in here
const icon =
pathdata.type === "value" ? (
<VpnKeyIcon style={iconStyle} />
) : pathdata.type === "list" ? (
<FormatListNumberedIcon style={iconStyle} />
) : (
<ExpandMoreIcon style={iconStyle} />
)
return (
<MenuItem
key={pathdata.name}
style={{
backgroundColor: theme.palette.inputColor,
color: "white",
minWidth: 250,
}}
value={pathdata}
onMouseOver={() => { }}
onClick={() => {
handleItemClick([innerdata, pathdata]);
}}
>
<Tooltip
color="primary"
title={`Ex. value: ${pathdata.value}`}
placement="left"
>
<div style={{ display: "flex" }}>
{icon} {pathdata.name}
</div>
</Tooltip>
</MenuItem>
);
})}
</NestedMenuItem>
*/}
<NestedMenuItem
key={innerdata.name}
label={
<div style={{ display: "flex", marginLeft: 0, }}>
{icon} {innerdata.name}
</div>
}
parentMenuOpen={!!menuPosition}
style={{
color: "white",
minWidth: 250,
maxWidth: 250,
maxHeight: 50,
overflow: "hidden",
}}
onClick={() => {
console.log(innerdata.example)
handleItemClick([innerdata]);
}}
>
<Paper style={{ minHeight: 500, maxHeight: 500, minWidth: 275, maxWidth: 275, position: "fixed", top: menuPosition?.top - 200, left: menuPosition?.left - 455, padding: "10px 0px 10px 10px", overflow: "hidden", overflowY: "auto", border: "1px solid rgba(255,255,255,0.3)", }}>
<MenuItem
key={innerdata.name}
style={{
// backgroundColor: theme.palette.inputColor,
marginLeft: 15,
color: "white",
minWidth: 250,
maxWidth: 250,
padding: 0,
position: "relative",
}}
value={innerdata}
onMouseOver={() => {
//console.log("HOVER: ", pathdata);
}}
onClick={() => {
handleItemClick([innerdata]);
}}
>
<Typography variant="h6" style={{ paddingBottom: 5 }}>
{innerdata.name}
</Typography>
</MenuItem>
{parsedPaths.map((pathdata, index) => {
// FIXME: Should be recursive in here
//<VpnKeyIcon style={iconStyle} />
const icon =
pathdata.type === "value" ? (
<span style={{ marginLeft: 9, }} />
) : pathdata.type === "list" ? (
<FormatListNumberedIcon style={{ marginLeft: 9, marginRight: 10, }} />
) : (
<CircleIcon style={{ marginLeft: 9, marginRight: 10, color: coverColor }} />
);
//<ExpandMoreIcon style={iconStyle} />
const indentation_count = (pathdata.name.match(/\./g) || []).length + 1
const baseIndent = <div style={{ marginLeft: 20, height: 30, width: 1, backgroundColor: coverColor, }} />
//const boxPadding = pathdata.type === "object" ? "10px 0px 0px 0px" : 0
const boxPadding = 0
const namesplit = pathdata.name.split(".")
const newname = namesplit[namesplit.length - 1]
return (
<MenuItem
key={pathdata.name}
style={{
// backgroundColor: theme.palette.inputColor,
color: "white",
minWidth: 250,
maxWidth: 250,
padding: boxPadding,
}}
value={pathdata}
onMouseOver={() => {
//console.log("HOVER: ", pathdata);
}}
onClick={() => {
handleItemClick([innerdata, pathdata]);
}}
>
<Tooltip
color="primary"
title={`Ex. value: ${pathdata.value}`}
placement="left"
>
<div style={{ display: "flex", height: 30, }}>
{Array(indentation_count).fill().map((subdata, subindex) => {
return (
baseIndent
)
})}
{icon} {newname}
{pathdata.type === "list" ? <SquareFootIcon style={{ marginleft: 10, }} onClick={(e) => {
}} /> : null}
</div>
</Tooltip>
</MenuItem>
);
})}
</Paper>
</NestedMenuItem>
</span>
) : (
<MenuItem
key={innerdata.name}
style={{
// backgroundColor: theme.palette.inputColor,
color: "white",
}}
value={innerdata}
onMouseOver={() => handleMouseover()}
onMouseOut={() => {
handleMouseOut();
}}
onClick={() => {
handleItemClick([innerdata]);
}}
>
<Tooltip
color="primary"
title={`Value: ${innerdata.value}`}
placement="left"
>
<div style={{ display: "flex" }}>
{icon} {innerdata.name}
</div>
</Tooltip>
</MenuItem>
);
})}
</Menu>
}
<div
style={{
marginTop: "20px",
@@ -19847,7 +20257,6 @@ const AngularWorkflow = (defaultprops) => {
src={validate.result}
theme={theme.palette.jsonTheme}
style={theme.palette.reactJsonStyle}
collapsed={false}
shouldCollapse={(jsonField) => {
return collapseField(jsonField)
}}
@@ -20191,7 +20600,6 @@ const AngularWorkflow = (defaultprops) => {
src={validate.result}
theme={theme.palette.jsonTheme}
style={theme.palette.reactJsonStyle}
collapsed={parsedCollapse}
shouldCollapse={(jsonField) => {
return collapseField(jsonField)
}}
@@ -21567,7 +21975,6 @@ const AngularWorkflow = (defaultprops) => {
src={validate.result}
theme={theme.palette.jsonTheme}
style={theme.palette.reactJsonStyle}
collapsed={false}
shouldCollapse={(jsonField) => {
return collapseField(jsonField)
}}
@@ -21729,7 +22136,6 @@ const AngularWorkflow = (defaultprops) => {
src={checked.result}
theme={theme.palette.jsonTheme}
style={theme.palette.reactJsonStyle}
collapsed={data.value.length < 10000 ? false : true}
shouldCollapse={(jsonField) => {
return collapseField(jsonField)
}}
@@ -22155,7 +22561,6 @@ const AngularWorkflow = (defaultprops) => {
src={validate.result}
theme={theme.palette.jsonTheme}
style={theme.palette.reactJsonStyle}
collapsed={selectedResult.result.length < 10000 ? false : true}
shouldCollapse={(jsonField) => {
return collapseField(jsonField)
}}
@@ -22216,6 +22621,7 @@ const AngularWorkflow = (defaultprops) => {
</span>
</div>
)}
<div>
{selectedResult.action.parameters !== null &&
selectedResult.action.parameters !== undefined ? (
+1 -1
View File
@@ -2906,7 +2906,7 @@ const AppExplorer = (props) => {
const deduplicateByName = (array) => {
const uniqueNames = {};
return array.filter(item => {
if (!item.hasOwnProperty('name') || !item.name.length) {
if (!item?.hasOwnProperty('name') || !item?.name?.length) {
return true
}
if (!uniqueNames[item.name]) {
+17 -20
View File
@@ -52,12 +52,11 @@ const searchClient = algoliasearch(
const AppCard = ({ data, index, mouseHoverIndex, setMouseHoverIndex, globalUrl, deactivatedIndexes, currTab, handleAppClick, leftSideBarOpenByClick, userdata, fetchApps, appsToShow, setAppsToShow, setUserApps, }) => {
const navigate = useNavigate();
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io" || window.location.host === "localhost:3000";
const appUrl = isCloud ? `/apps/${data.id}` : `https://shuffler.io/apps/${data.id}`;
//const appUrl = isCloud ? `/apps/${data.id}` : `https://shuffler.io/apps/${data.id}`;
const appUrl = `/apps/${data.id}`
var canEditApp = userdata?.support ||
userdata?.id === data?.owner ||
(userdata?.admin === "true" && userdata?.active_org?.id === data?.reference_org) ||
data?.contributors?.includes(userdata?.id)
var canEditApp = userdata?.support || userdata?.id === data?.owner ||
(userdata?.admin === "true" && userdata?.active_org?.id === data?.reference_org) || data?.contributors?.includes(userdata?.id)
const paperStyle = {
backgroundColor: mouseHoverIndex === index ? "rgba(26, 26, 26, 1)" : "#212121",
@@ -71,7 +70,7 @@ const AppCard = ({ data, index, mouseHoverIndex, setMouseHoverIndex, globalUrl,
boxShadow: "0px 0px 10px 0px rgba(0, 0, 0, 0.1)",
marginBottom: 20,
transition: "width 0.3s ease",
};
}
return (
<Grid item xs={12} key={index}>
@@ -133,6 +132,7 @@ const AppCard = ({ data, index, mouseHoverIndex, setMouseHoverIndex, globalUrl,
margin: "12px 0",
fontFamily: theme?.typography?.fontFamily,
}}>
<div style={{
display: 'flex',
flexDirection: 'row',
@@ -144,6 +144,7 @@ const AppCard = ({ data, index, mouseHoverIndex, setMouseHoverIndex, globalUrl,
maxWidth: "90%",
gap: 8
}}>
<div style={{
overflow: "hidden",
textOverflow: "ellipsis",
@@ -152,6 +153,7 @@ const AppCard = ({ data, index, mouseHoverIndex, setMouseHoverIndex, globalUrl,
}}>
{data.name.replace(/_/g, ' ').replace(/\b\w/g, char => char.toUpperCase())}
</div>
</div>
<div style={{
overflow: "hidden",
@@ -162,6 +164,7 @@ const AppCard = ({ data, index, mouseHoverIndex, setMouseHoverIndex, globalUrl,
}}>
{data.categories ? data.categories.join(", ") : "NA"}
</div>
<div style={{
overflow: "hidden",
textOverflow: "ellipsis",
@@ -187,6 +190,7 @@ const AppCard = ({ data, index, mouseHoverIndex, setMouseHoverIndex, globalUrl,
</span>
))}
</div>
{/* Deactivate button */}
{(currTab === 0 || currTab === 1) && !deactivatedIndexes.includes(index) && mouseHoverIndex === index && data.generated === true && (
<div style={{
@@ -222,6 +226,7 @@ const AppCard = ({ data, index, mouseHoverIndex, setMouseHoverIndex, globalUrl,
</button>
)
}
<Button
disabled={data?.reference_org === userdata?.active_org?.id}
className="deactivate-button"
@@ -264,18 +269,6 @@ const AppCard = ({ data, index, mouseHoverIndex, setMouseHoverIndex, globalUrl,
}
} else {
toast.success("App deactivated successfully. Will take effect on refresh..")
/*
// This somehow didn't work
if (appsToShow !== undefined && appsToShow !== null && appsToShow.length > 0) {
const newApps = appsToShow?.filter((app) => app.id !== data.id)
if (newApps !== undefined && newApps !== null && newApps.length > 0) {
setAppsToShow(newApps)
setUserApps(newApps)
}
}
}
*/
}
})
.catch(error => {
@@ -517,7 +510,6 @@ const Hits = ({
fontFamily: theme?.typography?.fontFamily
}}
onClick={() => {
console.log("App modal", data)
handleAppClick(data);
}}
>
@@ -645,6 +637,7 @@ const Hits = ({
</div>
)}
</div>
<div style={{
display: 'flex',
justifyContent: 'flex-end',
@@ -654,7 +647,7 @@ const Hits = ({
}}>
{hoverEffect === index && (
<div>
{allActivatedAppIds && allActivatedAppIds.includes(data.objectID) ? (
{allActivatedAppIds && allActivatedAppIds?.includes(data.objectID) ? (
<Button
style={{
width: 110,
@@ -673,7 +666,9 @@ const Hits = ({
}}>
Deactivate
</Button>
) : (
<Button
style={{
backgroundColor: "#FF8544",
@@ -1303,6 +1298,8 @@ const Apps2 = (props) => {
//console.log("Failed to get apps from localstorage: ", e)
}
setIsLoading(true);
fetch(globalUrl + "/api/v1/apps", {
method: "GET",
headers: {
+6 -5
View File
@@ -6,12 +6,9 @@ import classNames from "classnames";
import theme from '../theme.jsx';
import { useNavigate, Link, useParams } from "react-router-dom";
// react plugin used to create charts
//import { Line, Bar } from "react-chartjs-2";
//import { useAlert
import { ToastContainer, toast } from "react-toastify"
import Draggable from "react-draggable";
import DashboardBarchart, { LoadStats } from '../components/DashboardBarchart.jsx';
// import DashboardBarchart, { LoadStats } from '../components/DashboardBarchart.jsx';
import {
Autocomplete,
@@ -311,9 +308,13 @@ const RadialChart = ({keys, setSelectedCategory}) => {
//axis={<RadialAxis type="category" />}
}
// 0x0elliot: not cleaning this up. this seems clearly experimental. not worth my time.
// This is the start of a dashboard that can be used.
// What data do we fill in here? Idk
const Dashboard = (props) => {
const { globalUrl, isLoggedIn } = props;
//const alert = useAlert();
const [bigChartData, setBgChartData] = useState("data1");
@@ -397,7 +398,7 @@ const Dashboard = (props) => {
if (foundQuery !== null && foundQuery !== undefined) {
setSelectedUsecaseCategory(foundQuery)
const newitem = removeParam("selected", cursearch);
const newitem = removeParam("selected", cursearch);
navigate(curpath + newitem)
}
+3 -3
View File
@@ -186,9 +186,9 @@ const LoginDialog = (props) => {
if (responseJson.tutorials === undefined || responseJson.tutorials === null || !responseJson.tutorials.includes("welcome")) {
console.log("RUN Welcome!!")
setTimeout(() => {
navigate("/welcome?tab=2")
},200)
setTimeout(() => {
navigate("/welcome?tab=2")
},200)
// window.location.pathname = ""
return
}
+108 -111
View File
@@ -427,11 +427,15 @@ const chipStyle = {
color: "white",
};
export const collapseField = (field) => {
export const collapseField = (field, inputdata) => {
if (field === undefined || field === null) {
return true
}
if (field.namespace !== undefined && field.namespace !== null && field.namespace.length === 1) {
return false
}
if (field.name === "headers" || field.name === "cookies") {
return true
}
@@ -451,44 +455,44 @@ export const collapseField = (field) => {
}
export const validateJson = (showResult) => {
if (showResult === undefined || showResult === null) {
return {
valid: false,
result: "",
}
}
if (showResult === undefined || showResult === null) {
return {
valid: false,
result: "",
}
}
if (typeof showResult === 'string') {
showResult = showResult.split(" False").join(" false")
showResult = showResult.split(" True").join(" true")
if (typeof showResult === 'string') {
showResult = showResult.split(" False").join(" false")
showResult = showResult.split(" True").join(" true")
showResult.replaceAll("False,", "false,")
showResult.replaceAll("True,", "true,")
}
showResult.replaceAll("False,", "false,")
showResult.replaceAll("True,", "true,")
}
if (typeof showResult === "object" || typeof showResult === "array") {
return {
valid: true,
result: showResult,
}
}
if (typeof showResult === "object" || typeof showResult === "array") {
return {
valid: true,
result: showResult,
}
}
if (showResult[0] === "\"") {
return {
valid: false,
result: showResult,
}
}
if (showResult[0] === "\"") {
return {
valid: false,
result: showResult,
}
}
var jsonvalid = true
try {
if (!showResult.includes("{") && !showResult.includes("[")) {
jsonvalid = false
return {
valid: jsonvalid,
result: showResult,
};
return {
valid: jsonvalid,
result: showResult,
};
}
} catch (e) {
@@ -505,102 +509,94 @@ export const validateJson = (showResult) => {
var result = showResult;
try {
result = jsonvalid ? JSON.parse(showResult, { "storeAsString": true }) : showResult;
result = jsonvalid ? JSON.parse(showResult, {"storeAsString": true}) : showResult;
} catch (e) {
////console.log("Failed parsing JSON even though its valid: ", e)
jsonvalid = false;
}
if (jsonvalid === false) {
if (jsonvalid === false) {
if (typeof showResult === 'string') {
showResult = showResult.trim()
}
try {
var newstr = showResult.replaceAll("'", '"')
// Basic workarounds for issues with Python Dicts -> JSON
if (newstr.includes(": None")) {
newstr = newstr.replaceAll(": None", ': null')
}
if (newstr.includes("[\"{") && newstr.includes("}\"]")) {
newstr = newstr.replaceAll("[\"{", '[{')
newstr = newstr.replaceAll("}\"]", '}]')
}
if (newstr.includes("{\"[") && newstr.includes("]\"}")) {
newstr = newstr.replaceAll("{\"[", '[{')
newstr = newstr.replaceAll("]\"}", '}]')
}
result = JSON.parse(newstr)
jsonvalid = true
} catch (e) {
//console.log("Failed parsing JSON even though its valid (2): ", e)
jsonvalid = false
}
}
if (jsonvalid && typeof result === "number") {
jsonvalid = false
}
// This is where we start recursing
if (jsonvalid) {
// Check fields if they can be parsed too
try {
for (const [key, value] of Object.entries(result)) {
if (typeof value === "string") {
value = value.replaceAll(" ", "_")
if (typeof showResult === 'string') {
showResult = showResult.trim()
}
if (typeof value === "string" && (value.startsWith("{") || value.startsWith("["))) {
//console.log("CHECKING STRING: ", value)
try {
var newstr = showResult.replaceAll("'", '"')
const inside_result = validateJson(value)
if (inside_result.valid) {
//console.log("INSIDE RESULT: ", inside_result.result)
// Basic workarounds for issues with Python Dicts -> JSON
if (newstr.includes(": None")) {
newstr = newstr.replaceAll(": None", ': null')
}
if (typeof inside_result.result === "string") {
const newres = JSON.parse(inside_result.result)
if (newstr.includes("[\"{") && newstr.includes("}\"]")) {
newstr = newstr.replaceAll("[\"{", '[{')
newstr = newstr.replaceAll("}\"]", '}]')
}
result[key] = newres
} else {
result[key] = inside_result.result
}
}
} else {
if (newstr.includes("{\"[") && newstr.includes("]\"}")) {
newstr = newstr.replaceAll("{\"[", '[{')
newstr = newstr.replaceAll("]\"}", '}]')
}
// Usually only reaches here if raw array > dict > value
if (typeof showResult !== "array") {
for (const [subkey, subvalue] of Object.entries(value)) {
if (typeof subvalue === "string") {
subvalue = subvalue.replaceAll(" ", "_")
}
result = JSON.parse(newstr)
jsonvalid = true
} catch (e) {
if (typeof subvalue === "string" && (subvalue.startsWith("{") || subvalue.startsWith("["))) {
const inside_result = validateJson(subvalue)
if (inside_result.valid) {
if (typeof inside_result.result === "string") {
const newres = JSON.parse(inside_result.result)
result[key][subkey] = newres
} else {
result[key][subkey] = inside_result.result
}
}
}
//console.log("Failed parsing JSON even though its valid (2): ", e)
jsonvalid = false
}
}
}
}
}
}
} catch (e) {
//console.log("Failed parsing inside json subvalues: ", e)
}
}
if (jsonvalid && typeof result === "number") {
jsonvalid = false
}
// This is where we start recursing
if (jsonvalid) {
// Check fields if they can be parsed too
try {
for (const [key, value] of Object.entries(result)) {
if (typeof value === "string" && (value.startsWith("{") || value.startsWith("["))) {
//console.log("CHECKING STRING: ", value)
const inside_result = validateJson(value)
if (inside_result.valid) {
//console.log("INSIDE RESULT: ", inside_result.result)
if (typeof inside_result.result === "string") {
const newres = JSON.parse(inside_result.result)
result[key] = newres
} else {
result[key] = inside_result.result
}
}
} else {
// Usually only reaches here if raw array > dict > value
if (typeof showResult !== "array") {
for (const [subkey, subvalue] of Object.entries(value)) {
if (typeof subvalue === "string" && (subvalue.startsWith("{") || subvalue.startsWith("["))) {
const inside_result = validateJson(subvalue)
if (inside_result.valid) {
if (typeof inside_result.result === "string") {
const newres = JSON.parse(inside_result.result)
result[key][subkey] = newres
} else {
result[key][subkey] = inside_result.result
}
}
}
}
}
}
}
} catch (e) {
//console.log("Failed parsing inside json subvalues: ", e)
}
}
return {
valid: jsonvalid,
@@ -608,6 +604,7 @@ export const validateJson = (showResult) => {
};
};
//Custom hook for handling styling of the dropzone
const useDropzoneStyles = () => {
const { leftSideBarOpenByClick } = useContext(Context);