Merge branch '2.0.0' of github.com:Shuffle/Shuffle into 2.0.0

This commit is contained in:
Aditya
2024-12-05 01:45:38 +05:30
31 changed files with 2812 additions and 1834 deletions
+16 -14
View File
@@ -92,7 +92,7 @@ const RequestMethods = [
},
];
const ApiExplorer = memo(({ openapi, globalUrl, userdata, HandleApiExecution, selectedAppData, ConfigurationTab }) => {
const ApiExplorer = memo(({ openapi, globalUrl, userdata, HandleApiExecution, selectedAppData, ConfigurationTab, isLoggedIn, isLoaded }) => {
const [actions, setActions] = useState([]);
const [info, setInfo] = useState({});
const [serverurl, setServerUrl] = useState("");
@@ -1265,12 +1265,12 @@ const ApiExplorer = memo(({ openapi, globalUrl, userdata, HandleApiExecution, se
style={{
display: "flex",
flexDirection: 'row',
height: userdata?.support ? "auto" : "calc(100vh - 80px)",
height: (isLoggedIn && isLoaded) ? "auto" : "calc(100vh - 80px)",
}}
>
<ActionsList info={info} openapi={openapi} userdata={userdata} actions={actions} filteredActions={filteredActions} setFilteredActions={setFilteredActions} selectedActionIndex={selectedActionIndex} setSelectedActionIndex={setSelectedActionIndex} setExampleBody={setExampleBody }/>
<ActionsList info={info} openapi={openapi} isLoggedIn={isLoggedIn} isLoaded={isLoaded} userdata={userdata} actions={actions} filteredActions={filteredActions} setFilteredActions={setFilteredActions} selectedActionIndex={selectedActionIndex} setSelectedActionIndex={setSelectedActionIndex} setExampleBody={setExampleBody }/>
<ActionResponseAndRequest ConfigurationTab={ConfigurationTab} selectedAppData={selectedAppData} HandleApiExecution={HandleApiExecution} userdata={userdata} info={info} filteredActions={filteredActions} setFilteredActions={setFilteredActions} actions={actions} selectedActionIndex={selectedActionIndex} ExampleBody={ExampleBody} setExampleBody={setExampleBody} openapi={openapi} serverurl={serverurl} globalUrl={globalUrl} setSelectedActionIndex={setSelectedActionIndex}/>
<ActionResponseAndRequest isLoaded={isLoaded} isLoggedIn={isLoggedIn} ConfigurationTab={ConfigurationTab} selectedAppData={selectedAppData} HandleApiExecution={HandleApiExecution} userdata={userdata} info={info} filteredActions={filteredActions} setFilteredActions={setFilteredActions} actions={actions} selectedActionIndex={selectedActionIndex} ExampleBody={ExampleBody} setExampleBody={setExampleBody} openapi={openapi} serverurl={serverurl} globalUrl={globalUrl} setSelectedActionIndex={setSelectedActionIndex}/>
</div>
);
@@ -1279,7 +1279,7 @@ const ApiExplorer = memo(({ openapi, globalUrl, userdata, HandleApiExecution, se
export default ApiExplorer;
const ActionResponseAndRequest = memo(({ConfigurationTab, selectedAppData,actions, info, HandleApiExecution, userdata, filteredActions, setFilteredActions, serverurl, globalUrl, setSelectedActionIndex, ExampleBody, setExampleBody, selectedActionIndex}) => {
const ActionResponseAndRequest = memo(({ isLoggedIn, isLoaded, ConfigurationTab, selectedAppData,actions, info, HandleApiExecution, userdata, filteredActions, setFilteredActions, serverurl, globalUrl, setSelectedActionIndex, ExampleBody, setExampleBody, selectedActionIndex}) => {
const [apiResponse, setApiResponse] = useState({});
const [isLoading, setIsLoading] = useState(false);
const loadAction = 10;
@@ -1366,7 +1366,7 @@ const ActionResponseAndRequest = memo(({ConfigurationTab, selectedAppData,action
))}
</div>
<ActionResponse userdata={userdata} apiResponse={apiResponse} ExampleBody={ExampleBody}/>
<ActionResponse isLoaded={isLoaded} isLoggedIn={isLoggedIn} apiResponse={apiResponse} ExampleBody={ExampleBody}/>
</div>
)})
@@ -1381,6 +1381,8 @@ const ActionsList = memo(({
userdata,
info,
openapi,
isLoggedIn,
isLoaded
}) => {
const [searchQuery, setSearchQuery] = useState("");
@@ -1436,7 +1438,7 @@ const ActionsList = memo(({
}
};
return (
<div style={{ maxWidth: '350px', width: "25%", overflow: 'hidden', marginLeft: !userdata.support ? 5 : 0}}>
<div style={{ maxWidth: '350px', width: "25%", overflow: 'hidden', marginLeft: !(isLoggedIn || isLoaded) ? 5 : 0}}>
<div style={{ borderBottom: '1px solid #494949', paddingTop: 10, paddingBottom: 10}}>
<div>
{info?.title ? (
@@ -1487,7 +1489,7 @@ const ActionsList = memo(({
marginTop: 15,
backgroundColor: "#1a1a1a",
overflowY: "auto",
height: userdata?.support ? "calc(100vh - 190px)" : "calc(100vh - 260px)",
height: (isLoaded && isLoggedIn) ? "calc(100vh - 190px)" : "calc(100vh - 260px)",
paddingRight: 5,
}}
>
@@ -2776,7 +2778,7 @@ const Action = memo((
);
})
const ActionResponse = memo(({ apiResponse, ExampleBody, userdata }) => {
const ActionResponse = memo(({ apiResponse, ExampleBody, isLoggedIn, isLoaded }) => {
const [height, setHeight] = useState("14vh")
const [responseTabIndex, setResponseTabIndex] = useState(0)
const [oldResponse, setOldResponse] = useState(apiResponse)
@@ -2897,7 +2899,7 @@ const ActionResponse = memo(({ apiResponse, ExampleBody, userdata }) => {
}, []);
return (
<ApiResponseWrapper userdata={userdata}>
<ApiResponseWrapper isLoggedIn={isLoggedIn} isLoaded={isLoaded}>
<div
style={{
width: '100%',
@@ -3016,12 +3018,12 @@ const ResponseTabWrapper = memo(({ apiResponse }) => {
/>
)})
const PaddingWrapper = memo(({ userdata, children }) => {
const PaddingWrapper = memo(({ isLoggedIn, isLoaded, children }) => {
const { leftSideBarOpenByClick, windowWidth } = useContext(Context);
return (
<div
style={{
width: userdata?.support
width: (isLoggedIn && isLoaded)
? leftSideBarOpenByClick
? windowWidth >= 1920 ? "calc(100% - 630px)" : "calc(100% - 570px)"
: windowWidth >= 1920 ? "calc(100vw - 460px)": "calc(100% - 410px)"
@@ -3042,9 +3044,9 @@ const PaddingWrapper = memo(({ userdata, children }) => {
);
});
const ApiResponseWrapper = memo(({ children, userdata }) => {
const ApiResponseWrapper = memo(({ children, isLoaded, isLoggedIn }) => {
return (
<PaddingWrapper userdata={userdata}>
<PaddingWrapper isLoggedIn={isLoggedIn} isLoaded={isLoaded}>
{children}
</PaddingWrapper>
);
+202 -57
View File
@@ -1,4 +1,6 @@
import React, { useEffect, useState } from 'react';
import { useNavigate } from 'react-router';
import {
Dialog,
DialogTitle,
@@ -21,17 +23,22 @@ import LaunchIcon from '@mui/icons-material/Launch';
import CheckCircleIcon from '@mui/icons-material/CheckCircle';
import { CloudDownloadOutlined } from '@mui/icons-material';
import { findSpecificApp } from './AppFramework';
import theme from "../theme";
import YAML from 'yaml';
import { toast } from 'react-toastify';
import { Link } from 'react-router-dom';
const AppModal = ({ open, onClose, app, userdata, globalUrl }) => {
const AppModal = ({ open, onClose, app, globalUrl }) => {
const [frameworkData, setFrameworkData] = useState({})
const [userdata, setUserdata] = useState({})
const [usecases, setUsecases] = useState([])
const [workflows, setWorkflows] = useState([])
const [prevSubcase, setPrevSubcase] = useState({})
const [inputUsecase, setInputUsecase] = useState({})
const [latestUsecase, setLatestUsecase] = useState([])
const [foundAppUsecase, setFoundAppUsecase] = useState({})
const navigate = useNavigate();
const parseUsecase = (subcase) => {
const srcdata = findSpecificApp(frameworkData, subcase.type)
const dstdata = findSpecificApp(frameworkData, subcase.last)
@@ -48,6 +55,25 @@ const AppModal = ({ open, onClose, app, userdata, globalUrl }) => {
return subcase
}
useEffect(() => {
var baseurl = globalUrl;
fetch(baseurl + "/api/v1/me", {
credentials: "include",
headers: {
'Content-Type': 'application/json',
},
})
.then(response => response.json())
.then(responseJson => {
if (responseJson.success) {
setUserdata(responseJson)
}
})
.catch(error => {
console.log("Failed login check: ", error);
});
}, [app]);
const getFramework = () => {
fetch(globalUrl + "/api/v1/apps/frameworkConfiguration", {
@@ -266,6 +292,89 @@ const AppModal = ({ open, onClose, app, userdata, globalUrl }) => {
setFoundAppUsecase(foundSubcase);
}, [latestUsecase])
const downloadApp = (inputdata) => {
const id = inputdata.id;
toast("Downloading..");
fetch(globalUrl + "/api/v1/apps/" + id + "/config", {
method: "GET",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
credentials: "include",
})
.then((response) => {
if (response.status !== 200) {
window.location.pathname = "/apps";
}
return response.json();
})
.then((responseJson) => {
if (!responseJson.success) {
toast("Failed to download file");
} else {
console.log(responseJson);
const basedata = atob(responseJson.openapi);
console.log("BASE: ", basedata);
var inputdata = JSON.parse(basedata);
console.log("POST INPUT: ", inputdata);
inputdata = JSON.parse(inputdata.body);
const newpaths = {};
if (inputdata["paths"] !== undefined) {
Object.keys(inputdata["paths"]).forEach(function (key) {
newpaths[key.split("?")[0]] = inputdata.paths[key];
});
}
inputdata.paths = newpaths;
console.log("INPUT: ", inputdata);
var name = inputdata.info.title;
name = name.replace(/ /g, "_", -1);
name = name.toLowerCase();
delete inputdata.id;
delete inputdata.editing;
const data = YAML.stringify(inputdata);
var blob = new Blob([data], {
type: "application/octet-stream",
});
var url = URL.createObjectURL(blob);
var link = document.createElement("a");
link.setAttribute("href", url);
link.setAttribute("download", `${name}.yaml`);
var event = document.createEvent("MouseEvents");
event.initMouseEvent(
"click",
true,
true,
window,
1,
0,
0,
0,
0,
false,
false,
false,
false,
0,
null
);
link.dispatchEvent(event);
//link.parentNode.removeChild(link)
}
})
.catch((error) => {
console.log(error);
toast(error.toString());
});
};
const isCloud =
window.location.host === "localhost:3002" ||
window.location.host === "shuffler.io" || window.location.host === "localhost:3000"
@@ -280,7 +389,8 @@ const AppModal = ({ open, onClose, app, userdata, globalUrl }) => {
newAppname = newAppname?.replaceAll("_", " ");
}
var canEditApp = userdata.admin === "true" || userdata.id === app?.owner || app?.owner === "" || (userdata.admin === "true" && userdata.active_org.id === app?.reference_org) || !app?.generated
var canEditApp = userdata.admin === "true" || userdata?.id === app?.owner || app?.owner === "" || (userdata.admin === "true" && userdata.active_org.id === app?.reference_org) || !app?.generated
return (
@@ -294,7 +404,7 @@ const AppModal = ({ open, onClose, app, userdata, globalUrl }) => {
borderRadius: 2,
border: "1px solid #494949",
minWidth: '440px',
fontFamily: "Inter",
fontFamily: theme?.typography?.fontFamily,
backgroundColor: "#212121",
'& .MuiDialogContent-root': {
backgroundColor: "#212121",
@@ -303,10 +413,10 @@ const AppModal = ({ open, onClose, app, userdata, globalUrl }) => {
backgroundColor: "#212121",
},
'& .MuiTypography-root': {
fontFamily: 'Inter, sans-serif',
fontFamily: theme?.typography?.fontFamily,
},
'& .MuiButton-root': {
fontFamily: 'Inter, sans-serif',
fontFamily: theme?.typography?.fontFamily,
},
}
}}
@@ -320,7 +430,7 @@ const AppModal = ({ open, onClose, app, userdata, globalUrl }) => {
pt: 2,
pl: 3,
pr: 2,
fontFamily: "Inter"
fontFamily: theme?.typography?.fontFamily
}}
>
<Typography component="div" sx={{ fontWeight: 500, color: "#F1F1F1", fontSize: "22px" }}>
@@ -340,7 +450,7 @@ const AppModal = ({ open, onClose, app, userdata, globalUrl }) => {
<DialogContent sx={{ py: 3, px: 3 }}>
<Box sx={{ display: 'flex', alignItems: 'space-between', justifyContent: 'space-between' }}>
<div style={{ display: "flex", flexDirection: "row", gap: 10, fontFamily: "Inter" }}>
<div style={{ display: "flex", flexDirection: "row", gap: 10, fontFamily: theme?.typography?.fontFamily }}>
<img
alt={app?.name}
src={app?.large_image || app?.image_url}
@@ -364,11 +474,9 @@ const AppModal = ({ open, onClose, app, userdata, globalUrl }) => {
</Typography>
{
isCloud && (
<a
rel="noopener noreferrer"
href={"https://shuffler.io/apps/" + app?.id}
<Link
to={"/apps/" + (app?.id || app?.objectID)}
style={{ textDecoration: "none", color: "#f85a3e", marginTop: "-2px" }}
target="_blank"
>
<IconButton
style={{
@@ -378,7 +486,7 @@ const AppModal = ({ open, onClose, app, userdata, globalUrl }) => {
>
<OpenInNewIcon />
</IconButton>
</a>
</Link>
)
}
</div>
@@ -391,23 +499,33 @@ const AppModal = ({ open, onClose, app, userdata, globalUrl }) => {
</div>
</div>
<div style={{ display: "flex", flexDirection: "row", justifyContent: "center", alignItems: "center", gap: 10 }}>
<Button
variant="contained"
sx={{
bgcolor: '#494949',
'&:hover': { bgcolor: '#494949' },
textTransform: 'none',
borderRadius: 1,
minWidth: '45px',
width: '45px',
height: '40px',
padding: 2,
color: "#fff",
fontFamily: "Inter"
}}
>
<CloudDownloadOutlined />
</Button>
{app?.activated &&
app?.private_id !== undefined &&
app?.private_id?.length > 0 &&
app?.generated ? (
<Button
variant="contained"
sx={{
bgcolor: '#494949',
'&:hover': { bgcolor: '#494949' },
textTransform: 'none',
borderRadius: 1,
minWidth: '45px',
width: '45px',
height: '40px',
padding: 2,
color: "#fff",
fontFamily: theme?.typography?.fontFamily
}}
onClick={(event) => {
event.preventDefault();
event.stopPropagation();
downloadApp(app);
}}
>
<CloudDownloadOutlined />
</Button>) : null}
<Button
variant="contained"
sx={{
@@ -419,9 +537,21 @@ const AppModal = ({ open, onClose, app, userdata, globalUrl }) => {
px: 3,
height: '40px',
color: "#fff",
fontFamily: "Inter"
fontFamily: theme?.typography?.fontFamily
}}
startIcon={canEditApp ? <EditIcon /> :
(app?.generated && app?.activated && userdata?.id !== app?.owner && isCloud ?
<ForkRightIcon /> : null
)}
onClick={() => {
if (canEditApp) {
const editUrl = "/apps/edit/" + (app?.id || app?.objectID);
navigate(editUrl)
}else{
const forkUrl = "/apps/new?id=" + (app?.id || app?.objectID);
navigate(forkUrl)
}
}}
startIcon={canEditApp ? <EditIcon /> : <ForkRightIcon />}
>
{canEditApp ? "Edit" : "Fork"}
</Button>
@@ -431,17 +561,17 @@ const AppModal = ({ open, onClose, app, userdata, globalUrl }) => {
<div style={{
display: "flex",
justifyContent: "space-between",
fontFamily: "Inter",
fontFamily: theme?.typography?.fontFamily,
padding: "26px 0px"
}}>
<div style={{
textAlign: "start",
flex: 1,
}}>
<Typography
variant="h6"
<Typography
variant="h6"
sx={{
fontFamily: 'Inter, sans-serif',
fontFamily: theme?.typography?.fontFamily,
fontSize: '24px',
fontWeight: 600,
mb: 0.3,
@@ -450,11 +580,11 @@ const AppModal = ({ open, onClose, app, userdata, globalUrl }) => {
>
20
</Typography>
<Typography
variant="body2"
sx={{
<Typography
variant="body2"
sx={{
color: 'rgba(255, 255, 255, 0.7)',
fontFamily: 'Inter, sans-serif',
fontFamily: theme?.typography?.fontFamily,
fontSize: '14px'
}}
>
@@ -468,12 +598,12 @@ const AppModal = ({ open, onClose, app, userdata, globalUrl }) => {
paddingLeft: "10px",
height: "100%",
}}>
<Typography variant="h6"
sx={{
fontWeight: 600,
mb: 0.3,
color: '#fff'
}}>
<Typography variant="h6"
sx={{
fontWeight: 600,
mb: 0.3,
color: '#fff'
}}>
{Array.isArray(app?.actions) ? app.actions.length : app?.actions}
</Typography>
<Typography variant="body2" sx={{ color: 'rgba(255, 255, 255, 0.7)' }}>
@@ -486,7 +616,7 @@ const AppModal = ({ open, onClose, app, userdata, globalUrl }) => {
paddingLeft: "10px",
paddingTop: "5px"
}}>
<div style={{ display: 'flex', alignItems: 'center', gap: '4px', marginBottom: "5px", fontFamily: "Inter", fontSize: "14px", fontWeight: 600 }}>
<div style={{ display: 'flex', alignItems: 'center', gap: '4px', marginBottom: "5px", fontFamily: theme?.typography?.fontFamily, fontSize: "14px", fontWeight: 600, color: 'white' }}>
{
app?.collection ? (
<>
@@ -494,13 +624,25 @@ const AppModal = ({ open, onClose, app, userdata, globalUrl }) => {
<Typography variant="body1" sx={{
fontWeight: 500,
color: '#fff',
marginTop: "1px"
marginTop: "1px",
fontFamily: theme?.typography?.fontFamily,
fontSize: "16px"
}}>
app.collection
</Typography>
</>
) : "No collection yet"
) : (
<Typography sx={{
fontSize: "16px",
fontWeight: 500,
marginTop: "1px",
color: 'rgba(255, 255, 255, 0.7)',
fontFamily: theme?.typography?.fontFamily
}}>
No collection yet
</Typography>
)
}
</div>
@@ -517,7 +659,7 @@ const AppModal = ({ open, onClose, app, userdata, globalUrl }) => {
width: "100%"
}}>
<div style={{
fontFamily: "Inter",
fontFamily: theme?.typography?.fontFamily,
fontSize: "16px",
color: "#fff",
marginBottom: "16px",
@@ -544,7 +686,7 @@ const AppModal = ({ open, onClose, app, userdata, globalUrl }) => {
{
foundAppUsecase === undefined ? (
<Avatar sx={{ width: 32, height: 32, bgcolor: 'background.paper', border: 1, borderColor: 'divider' }}>
<Search sx={{ color: 'text.primary', zIndex: 10, fontSize: 18}} />
<Search sx={{ color: 'text.primary', zIndex: 10, fontSize: 18 }} />
</Avatar>
) : (
<Avatar
@@ -560,10 +702,10 @@ const AppModal = ({ open, onClose, app, userdata, globalUrl }) => {
/>
)
}
{
{
foundAppUsecase === undefined ? (
<Avatar sx={{ width: 32, height: 32, bgcolor: 'background.paper', border: 1, borderColor: 'divider' }}>
<AddIcon sx={{ color: 'text.primary', zIndex: 10, fontSize: 18}} />
<AddIcon sx={{ color: 'text.primary', zIndex: 10, fontSize: 18 }} />
</Avatar>
) : (
<Avatar
@@ -586,7 +728,7 @@ const AppModal = ({ open, onClose, app, userdata, globalUrl }) => {
</Box>
</div>
<div style={{ display: "flex", justifyContent: "center", fontFamily: "Inter" }}>
<div style={{ display: "flex", justifyContent: "center", fontFamily: theme?.typography?.fontFamily }}>
<Button
variant="contained"
sx={{
@@ -599,11 +741,14 @@ const AppModal = ({ open, onClose, app, userdata, globalUrl }) => {
fontSize: "14px",
letterSpacing: "0.5px",
color: "black",
fontFamily: "Inter",
fontFamily: theme?.typography?.fontFamily,
minWidth: '200px'
}}
onClick={() => {
navigate("/usecases2")
}}
>
Create a Usecase
Find a Usecase
</Button>
</div>
</DialogContent>
@@ -611,4 +756,4 @@ const AppModal = ({ open, onClose, app, userdata, globalUrl }) => {
);
};
export default AppModal;
export default AppModal;
+1 -1
View File
@@ -465,7 +465,7 @@ const AppSelection = props => {
style={{
fontSize: 16,
color: "rgba(158, 158, 158, 1)",
fontFamily: "Inter",
fontFamily: theme?.typography?.fontFamily,
}}
>
Your organization has no apps yet, select your starting apps here
+104 -75
View File
@@ -1,4 +1,4 @@
import React, { useState, useEffect } from "react";
import React, { useState, useEffect, useContext, memo, useMemo } from "react";
import ReactGA from 'react-ga4';
import theme from "../theme.jsx";
@@ -41,7 +41,8 @@ import {
Delete,
RestaurantRounded,
Cloud,
CheckCircle
CheckCircle,
Padding,
} from "@mui/icons-material";
//import { useAlert
@@ -49,8 +50,9 @@ import { typecost, typecost_single, } from "../views/HandlePaymentNew.jsx";
import BillingStats from "./BillingStats.jsx";
import { handlePayasyougo } from "../views/HandlePaymentNew.jsx"
import DeleteIcon from '@mui/icons-material/Delete';
import { Context } from "../context/ContextApi.jsx";
const Billing = (props) => {
const Billing = memo((props) => {
const { globalUrl, userdata, serverside, billingInfo, stripeKey, selectedOrganization, handleGetOrg, clickedFromOrgTab } = props;
//const alert = useAlert();
let navigate = useNavigate();
@@ -72,7 +74,7 @@ const Billing = (props) => {
const [alertThresholds, setAlertThresholds] = useState(selectedOrganization.Billing !== undefined && selectedOrganization.Billing.AlertThreshold !== undefined && selectedOrganization.Billing.AlertThreshold !== null ? selectedOrganization.Billing.AlertThreshold : [{ percentage: '', count: '', Email_send: false }]);
const [currentIndex, setCurrentIndex] = useState(0);
const [deleteAlertVerification, setDeleteAlertVerification] = useState(false);
useEffect(() => {
if (userdata.app_execution_limit !== undefined && userdata.app_execution_usage !== undefined) {
const percentage = (userdata.app_execution_usage / userdata.app_execution_limit) * 100;
@@ -81,18 +83,12 @@ const Billing = (props) => {
}
}, [userdata]);
const [BillingEmail, setBillingEmail] = useState(selectedOrganization?.Billing?.Email);
const [BillingEmail, setBillingEmail] = useState(selectedOrganization.Billing !== undefined && selectedOrganization.Billing.Email !== undefined && selectedOrganization.Billing.Email != null && selectedOrganization.Billing.Email.length > 0 ? selectedOrganization.Billing.Email : selectedOrganization.org);
useState(() => {
// Set the billing email
setBillingEmail(
selectedOrganization.Billing !== undefined &&
selectedOrganization.Billing.Email !== undefined &&
selectedOrganization.Billing.Email.length > 0
? selectedOrganization.Billing.Email
: selectedOrganization.org
);
useEffect(() => {
if (BillingEmail !== selectedOrganization?.Billing?.Email) {
setBillingEmail(selectedOrganization?.Billing?.Email);
}
// Set and sort the alert thresholds
const alertThresholds = selectedOrganization.Billing !== undefined &&
@@ -126,7 +122,6 @@ const Billing = (props) => {
];
const handleGetDeals = (orgId) => {
console.log("Get deals!");
if (orgId.length === 0) {
toast(
@@ -178,7 +173,7 @@ const Billing = (props) => {
width: 340,
height: 480,
// width: "100%",
backgroundColor: theme.palette.platformColor,
backgroundColor: theme.palette.backgroundColor,
borderRadius: theme.palette?.borderRadius * 2,
border: "1px solid rgba(255,255,255,0.3)",
marginRight: 10,
@@ -384,7 +379,7 @@ const Billing = (props) => {
}
if (hovered) {
newPaperstyle.backgroundColor = theme.palette.surfaceColor
newPaperstyle.backgroundColor = "#2b2b2b"
}
const handleClickOpen = () => {
@@ -412,7 +407,6 @@ const Billing = (props) => {
const HandleChangeBillingEmail = (orgId) => {
const email = newBillingEmail;
const emailPattern = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$/;
console.log("Pattern matches: ", emailPattern.test(email));
if (!emailPattern.test(email)) {
toast("Please enter a valid email address");
return;
@@ -445,7 +439,6 @@ const Billing = (props) => {
}
return response.json();
}).then((responseJson) => {
console.log("Got org:", responseJson);
if (responseJson.success === true) {
toast.success("Successfully updated billing email");
setBillingEmail(newBillingEmail);
@@ -460,7 +453,7 @@ const Billing = (props) => {
}
return (
<Paper
<div
style={newPaperstyle}
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
@@ -726,7 +719,7 @@ const Billing = (props) => {
</Typography>
<div style={{ display: 'flex', flexDirection: 'row' }}>
<Typography variant="body2" style={{ marginTop: !userdata.has_card_available ? 5 : 0 }}>
Billing email: {BillingEmail}
{BillingEmail?.length > 0 ? `Billing email: ${BillingEmail}` : null}
</Typography>
{userdata.has_card_available === true && (
<Button
@@ -817,11 +810,11 @@ const Billing = (props) => {
color="primary"
style={{
marginTop: !userdata.has_card_available ? 20 : 10,
borderRadius: 25,
borderRadius: 8,
height: 40,
fontSize: 16,
color: "white",
backgroundColor: userdata.has_card_available ? null : "#f86743",
color: userdata.has_card_available ? "#ff8544" : "#1a1a1a",
backgroundColor: userdata.has_card_available ? null : "#ff8544",
// backgroundImage: userdata.has_card_available ? null : "linear-gradient(to right, #f86a3e, #f34079)",
textTransform: "none",
@@ -850,11 +843,11 @@ const Billing = (props) => {
color="primary"
style={{
marginTop: 10,
borderRadius: 25,
borderRadius: 8,
height: 40,
fontSize: 16,
color: "white",
backgroundColor: "#f86743",
color: "#1a1a1a",
backgroundColor: "#ff8544",
textTransform: 'none'
}}
onClick={() => {
@@ -873,7 +866,6 @@ const Billing = (props) => {
: null}
{showSupport ?
<Button variant="outlined" color="primary" style={{ marginTop: 20, marginBottom: 10, }} onClick={() => {
console.log("Support click")
if (window.drift !== undefined) {
//window.drift.api.startInteraction({ interactionId: 340045 })
window.drift.api.startInteraction({ interactionId: 340043 })
@@ -884,7 +876,7 @@ const Billing = (props) => {
Get Support
</Button>
: null}
</Paper>
</div>
)
}
const ConsultationManagement = (props) => {
@@ -1035,12 +1027,12 @@ const Billing = (props) => {
})
return (
<Paper style={{
<div style={{
padding: 20,
// maxWidth: 400,
width: 340,
height: 480,
backgroundColor: hovered ? "#232427" : theme.palette.platformColor,
backgroundColor: hovered ? "#2b2b2b" : theme.palette.backgroundColor,
borderRadius: theme.palette?.borderRadius * 2,
border: "1px solid rgba(255,255,255,0.3)",
marginRight: 10,
@@ -1131,11 +1123,11 @@ const Billing = (props) => {
color="primary"
style={{
marginTop: userdata.support ? 0 : 10,
borderRadius: 25,
borderRadius: 8,
height: 40,
fontSize: 16,
color: "white",
backgroundColor: "#f86743",
color: "#1A1A1A",
backgroundColor: "#FF8544",
textTransform: 'none',
}}
onClick={() => {
@@ -1150,7 +1142,7 @@ const Billing = (props) => {
>
Buy
</Button>
<Dialog open={clickOnBuy} onClose={() => { setClickOnBuy(false) }}>
<Dialog open={clickOnBuy} onClose={() => { setClickOnBuy(false) }} PaperProps={{style: {backgroundColor: "rgb(26, 26, 26)"}}}>
<Typography variant="body1" style={{ marginTop: 10, textAlign: 'center', padding: 20, fontSize: 18 }}>
You will be taken to Stripe to book professional service hours. You can adjust the number of hours on the left side of the Stripe page.
</Typography>
@@ -1158,7 +1150,7 @@ const Billing = (props) => {
<Button
variant="contained"
color="primary"
style={{ display: 'block', marginLeft: 'auto', marginRight: 'auto', padding: '12px 24px', textTransform: 'none', fontSize: 16 }}
style={{ display: 'block', marginLeft: 'auto', marginRight: 'auto', padding: '12px 24px', textTransform: 'none', fontSize: 16, color: "#1a1a1a", backgroundColor: "#ff8544" }}
onClick={() => {
if (Cloud) {
ReactGA.event({
@@ -1186,10 +1178,10 @@ const Billing = (props) => {
color="primary"
style={{
marginTop: userdata.support ? 5 : 10,
borderRadius: 25,
borderRadius: 8,
height: 40,
fontSize: 16,
color: "white",
color: "#ff8544",
textTransform: 'none',
cursor: getProfessionalServices ? 'pointer' : 'not-allowed',
opacity: getProfessionalServices ? 1 : 0.6,
@@ -1259,7 +1251,7 @@ const Billing = (props) => {
</Button>
</DialogContent>
</Dialog>
</Paper >
</div >
)
}
@@ -1308,13 +1300,13 @@ const Billing = (props) => {
}
return (
<Paper
<div
style={{
padding: 20,
height: 480,
// maxWidth: 400,
width: 340,
backgroundColor: hovered ? "#232427" : theme.palette.platformColor,
backgroundColor: hovered ? "#2b2b2b" : theme.palette.backgroundColor,
borderRadius: theme.palette?.borderRadius * 2,
border: "1px solid rgba(255,255,255,0.3)",
marginRight: 10,
@@ -1370,11 +1362,12 @@ const Billing = (props) => {
color="primary"
style={{
marginTop: 10,
borderRadius: 25,
borderRadius: 8,
height: 40,
fontSize: 16,
color: "white",
textTransform: 'none'
color: "#1A1A1A",
textTransform: 'none',
backgroundColor: "#FF8544"
}}
onClick={() => {
if (Cloud) {
@@ -1397,10 +1390,12 @@ const Billing = (props) => {
color="primary"
style={{
marginTop: 10,
borderRadius: 25,
borderRadius: 8,
height: 40,
fontSize: 16,
textTransform: 'none'
textTransform: 'none',
color: "#FF8544",
backgroundColor: "#1a1a1a",
}}
onClick={() => {
if (Cloud) {
@@ -1425,6 +1420,7 @@ const Billing = (props) => {
style: {
width: 500,
margin: 0,
backgroundColor: "rgb(26, 26, 26)",
}
}}
>
@@ -1464,7 +1460,7 @@ const Billing = (props) => {
<Button
variant="contained"
color="primary"
style={{ marginTop: '24px', display: 'block', marginLeft: 'auto', marginRight: 'auto', padding: '12px 24px', textTransform: 'none' }}
style={{ marginTop: '24px', display: 'block', marginLeft: 'auto', marginRight: 'auto', padding: '12px 24px', textTransform: 'none', color: "#1a1a1a", backgroundColor: "#ff8544" }}
onClick={handlePrivateTraining}
>
Submit Request
@@ -1472,7 +1468,7 @@ const Billing = (props) => {
</DialogContent>
</Dialog>
</div>
</Paper>
</div>
)
}
@@ -1892,13 +1888,15 @@ const Billing = (props) => {
}
};
const isChildOrg = userdata.active_org.creator_org !== "" && userdata.active_org.creator_org !== undefined && userdata.active_org.creator_org !== null
const isChildOrg = userdata?.active_org?.creator_org !== "" && userdata?.active_org?.creator_org !== undefined && userdata?.active_org?.creator_org !== null
return (
<div style={{ width: clickedFromOrgTab ? 1030 : "auto", padding: 27, backgroundColor: '#212121', borderRadius: '16px', }}>
{addDealModal}
<Wrapper clickedFromOrgTab={clickedFromOrgTab}>
<div style={{ height: "100%", width: "100%"}}>
<div style={{ width: "100%",}}>
{addDealModal}
{clickedFromOrgTab ?
<h2 style={{ marginBottom: 8, marginTop: 0, color: "#ffffff" }}>Billing & Licensing</h2> :
<Typography style={{fontSize: 24, fontWeight: "bold", marginBottom: 8, marginTop: 0, color: "#ffffff" }}>Billing & Licensing</Typography> :
<Typography variant="h4" style={{ marginTop: 20, marginBottom: 10 }}>
Billing & Licensing
</Typography>}
@@ -1942,11 +1940,11 @@ const Billing = (props) => {
{isChildOrg ?
<Typography variant="h6" style={{ marginBottom: 50, }}>
Billing is handled by your parent organisation. Reach out to support@shuffler.io if you have questions about this.
Licensing is handled by your parent organisation. Reach out to support@shuffler.io if you have questions about this.
</Typography>
: null}
<div style={{ display: "flex", width: 1180, overflowX: 'auto', overflowY: 'hidden', scrollbarWidth: 'thin', scrollbarColor: '#494949 #2f2f2f', height: 580 }} >
<div style={{ display: "flex", width: clickedFromOrgTab ? 1030 : "auto", overflowX: 'auto', overflowY: 'hidden', scrollbarWidth: 'thin', scrollbarColor: '#494949 #2f2f2f', height: isChildOrg ? 0 : 580 }} >
{isCloud && billingInfo.subscription !== undefined && billingInfo.subscription !== null ? isChildOrg ? null :
<SubscriptionObject
index={0}
@@ -2258,10 +2256,10 @@ const Billing = (props) => {
) : null*/}
{!isChildOrg && isCloud && (
<div style={{ display: 'flex', flexDirection: 'column', marginTop: 10 }}>
<Typography style={{ marginBottom: 5 }} variant="h4">
<Typography style={{ marginBottom: 5, fontSize: 24, fontWeight: "bold" }}>
Professional Services
</Typography>
<Typography color="textSecondary">
<Typography color="textSecondary" style={{fontSize: 16,}}>
We offer priority support through consultations and training to help you make the most of our product. If you have any questions, please reach out to us at support@shuffler.io.
</Typography>
<div style={{ display: 'flex', flexDirection: 'row', marginTop: 5 }}>
@@ -2280,15 +2278,14 @@ const Billing = (props) => {
)}
<div style={{ marginTop: isCloud && 40, marginLeft: 10 }}>
<Typography
style={{ marginBottom: 5 }}
variant="h4"
style={{ marginBottom: 5, fontSize: 24, fontWeight: "bold" }}
>
Manage Billing
</Typography>
<Typography variant="body1" color="textSecondary" style={{ marginTop: 0, marginBottom: 10 }}>
<Typography color="textSecondary" style={{ marginTop: 10, marginBottom: 10, fontSize: 16, }}>
Manage your billing and licensing information below. When you reach the certain thresholds of your subscription limit, you will be notified by email.
</Typography>
<Typography variant="body1">Current Usage:</Typography>
<Typography style={{fontSize: 18, marginTop: 10}}>Current Usage:</Typography>
<LinearProgress
variant="determinate"
value={currentAppRunsInPercentage}
@@ -2300,15 +2297,15 @@ const Billing = (props) => {
marginBottom: 10,
}}
/>
<Typography variant="body1" color="textSecondary">
<Typography style={{marginTop: 10, fontSize: 16,}} color="textSecondary">
You have used <strong>{currentAppRunsInPercentage}%</strong> of total app execution limit or <strong>{userdata.app_execution_usage}</strong> app runs out of <strong>{userdata.app_execution_limit}</strong> app runs.
</Typography>
<div>
<Typography variant="body1" style={{ marginTop: 20 }}>
<Typography style={{ marginTop: 20, fontSize: 18 }}>
Set email alert thresholds for app runs
</Typography>
<Typography variant="body1" color="textSecondary" style={{ marginTop: 10 }}>
<Typography color="textSecondary" style={{ marginTop: 10, fontSize: 16 }}>
You will be notified by email when you reach the
{currentIndex !== -1
? " " + getSafeValue(alertThresholds[currentIndex].percentage) + '%' + " "
@@ -2320,8 +2317,8 @@ const Billing = (props) => {
: " " + 0 + " "}
app runs.
</Typography>
<Typography variant="body1" color="textSecondary" style={{ marginTop: 10 }}>
Please note: Once your app runs reach the set alert threshold, all admins in the organization will receive an email notification.
<Typography color="textSecondary" style={{ fontSize: 16 }}>
<span style={{fontWeight: 'bold'}}>Please note</span>: Once your app runs reach the set alert threshold, all admins in the organization will receive an email notification.
</Typography>
<div style={{ marginTop: 15 }}>
{alertThresholds.map((threshold, index) => (
@@ -2329,7 +2326,7 @@ const Billing = (props) => {
<TextField
style={{
marginTop: 10,
backgroundColor: theme.palette.inputColor,
backgroundColor: "#212121",
width: 250,
}}
InputProps={{
@@ -2354,7 +2351,7 @@ const Billing = (props) => {
<TextField
style={{
marginTop: 10,
backgroundColor: theme.palette.inputColor,
backgroundColor: "#212121",
width: 250,
marginLeft: 15,
}}
@@ -2421,11 +2418,11 @@ const Billing = (props) => {
Save
</Button>
</div>
</div>
<div style={{ marginTop: 40, display: 'flex', flexDirection: 'column' }}>
<Typography
style={{ marginTop: 10, marginLeft: 10, marginBottom: 5 }}
variant="h4"
style={{ marginTop: 10, marginLeft: 10, marginBottom: 5, fontSize: 24, fontWeight: "bold" }}
>
Utilization & Stats
</Typography>
@@ -2436,9 +2433,41 @@ const Billing = (props) => {
globalUrl={globalUrl}
selectedOrganization={selectedOrganization}
userdata={userdata}
/>
</div>
/>
</div>
</Wrapper>
)
}
})
export default Billing;
export default memo(Billing);
const PaddingWrapper = memo(({ clickedFromOrgTab, children }) => {
const wrapperStyle = useMemo(() => ({
width: clickedFromOrgTab
? "100%"
: "auto",
padding: "27px 10px 19px 27px",
backgroundColor: '#212121',
borderRadius: '16px',
height: '100%',
boxSizing: 'border-box',
borderLeft: '1px solid #494949',
overflow: 'hidden',
maxHeight: "1700px", overflowY: "auto",scrollbarColor: '#494949 transparent', scrollbarWidth: 'thin'
}), [clickedFromOrgTab]);
return (
<div style={wrapperStyle}>
{children}
</div>
);
});
const Wrapper = memo(({ children, clickedFromOrgTab }) => {
return (
<PaddingWrapper clickedFromOrgTab={clickedFromOrgTab}>
{children}
</PaddingWrapper>
);
});
+52 -11
View File
@@ -1,4 +1,4 @@
import React, { useState, useEffect } from 'react';
import React, { useState, useEffect, useContext, memo, useMemo } from 'react';
import theme from '../theme.jsx';
import classNames from "classnames";
@@ -37,6 +37,7 @@ import {
} from 'reaviz';
import { typecost, typecost_single, } from "../views/HandlePaymentNew.jsx";
import { Context } from '../context/ContextApi.jsx';
const LineChartWrapper = ({keys, inputname, height, width}) => {
const [hovered, setHovered] = useState("");
@@ -82,8 +83,8 @@ const AppStats = (defaultprops) => {
const [workflows, setWorkflows] = useState(inputWorkflows === undefined ? [] : inputWorkflows)
const [resultRows, setResultRows] = useState([])
const [resultLoading, setResultLoading] = useState(true)
const includedExecutions = selectedOrganization.sync_features.app_executions !== undefined ? selectedOrganization.sync_features.app_executions.limit : 0
const includedExecutions = selectedOrganization?.sync_features?.app_executions !== undefined ? selectedOrganization?.sync_features?.app_executions?.limit : 0
useEffect(() => {
if (workflows === undefined || workflows === null || workflows.length === 0) {
@@ -92,7 +93,6 @@ const AppStats = (defaultprops) => {
}, [])
const getWorkflowStats = async (workflow, startTime, endTime) => {
if (!userdata.support) {
return workflow
@@ -162,8 +162,6 @@ const AppStats = (defaultprops) => {
const loadWorkflowStats = (foundWorkflows, startTime, endTime) => {
if (!userdata.support) {
console.log("Not support")
return
}
@@ -488,8 +486,13 @@ const AppStats = (defaultprops) => {
setApprunCosts(appcostRuns)
}
const getStats = () => {
fetch(`${globalUrl}/api/v1/orgs/${selectedOrganization.id}/stats`, {
const getStats = (orgid) => {
if (orgid === undefined || orgid === null) {
return
}
fetch(`${globalUrl}/api/v1/orgs/${orgid}/stats`, {
method: "GET",
headers: {
"Content-Type": "application/json",
@@ -519,8 +522,10 @@ const AppStats = (defaultprops) => {
}
useEffect(() => {
getStats()
}, [])
if(selectedOrganization?.id?.length > 0) {
getStats(selectedOrganization.id)
}
}, [selectedOrganization])
const paperStyle = {
textAlign: "center",
@@ -639,7 +644,7 @@ const AppStats = (defaultprops) => {
const data = (
<div className="content" style={{width: "100%", margin: "auto", }}>
<Typography variant="body1" style={{margin: "auto", marginLeft: 10, marginBottom: 20, }} color="textSecondary">
<Typography style={{margin: "auto", marginLeft: 10, marginBottom: 20, fontSize: 16}} color="textSecondary">
All shown statistics are gathered from <a
href={`${globalUrl}/api/v1/orgs/${selectedOrganization.id}/stats`}
target="_blank"
@@ -722,6 +727,40 @@ const AppStats = (defaultprops) => {
</div>
: null}
{clickedFromOrgTab? (
<LocalizationProvider dateAdapter={AdapterDayjs} style={{ flex: 1 }}>
<div style={{ display: "flex", flexDirection: "column", gap: "10px" }}>
<div style={{ flex: 1, maxWidth: "200px", }}>
<DateTimePicker
sx={{
marginTop: 1,
marginLeft: 1,
}}
ampm={false}
label="Search from"
format="YYYY-MM-DD HH:mm:ss"
value={startTime}
onChange={handleStartTimeChange}
renderInput={(params) => <TextField {...params} />}
/>
</div>
<div style={{ flex: 1, maxWidth: "200px",}}>
<DateTimePicker
sx={{
marginTop: 1,
marginLeft: 1,
}}
ampm={false}
label="Search until"
format="YYYY-MM-DD HH:mm:ss"
value={endTime}
onChange={handleEndTimeChange}
renderInput={(params) => <TextField {...params} />}
/>
</div>
</div>
</LocalizationProvider>
):(
<LocalizationProvider dateAdapter={AdapterDayjs} style={{flex: 1, }}>
<div style={{display: "flex", flexDirection: "column", }}>
<DateTimePicker
@@ -754,6 +793,8 @@ const AppStats = (defaultprops) => {
/>
</div>
</LocalizationProvider>
)}
</div>
+53 -25
View File
@@ -1,4 +1,4 @@
import React, { useState, useEffect } from "react";
import React, { useState, useEffect, useContext } from "react";
import ReactGA from 'react-ga4';
import theme from "../theme.jsx";
import { ToastContainer, toast } from "react-toastify"
@@ -21,6 +21,7 @@ import {
red,
green,
} from "../views/AngularWorkflow.jsx"
import { Context } from "../context/ContextApi.jsx";
//import { useAlert
@@ -30,7 +31,8 @@ const Branding = (props) => {
const [publishingInfo, setPublishingInfo] = useState("");
const [publishRequirements, setPublishRequirements] = useState([])
const { leftSideBarOpenByClick } = useContext(Context)
const handleEditOrg = (joinStatus) => {
const data = {
"org_id": selectedOrganization.id,
@@ -115,48 +117,73 @@ const Branding = (props) => {
const leadinfo = selectedOrganization.lead_info === undefined || selectedOrganization.lead_info === null || selectedOrganization.lead_info === "" ? "" : JSON.stringify(selectedOrganization.lead_info)
const isPartner = leadinfo.includes("partner")
console.log("LEADINFO: ", leadinfo)
console.log("SELECTEDORGANIZATION: ", selectedOrganization)
return (
<div style={{ width: clickedFromOrgTab? 1030: "auto", padding: 27, height: "auto", backgroundColor: '#212121', borderRadius: '16px', }}>
<h2 style={{marginTop: clickedFromOrgTab ?0:null,}}>
<div style={{ width: clickedFromOrgTab? "100%": "auto", height: "100%", minHeight: 1100, boxSizing: 'border-box', transition: "width 0.3s ease", padding: "27px 10px 19px 27px", height: "auto", backgroundColor: '#212121', borderRadius: '16px', }}>
<div style={{height: 843, overflowY: "auto",}}>
<div style={{width: "100%", overflowX: 'hidden', }}>
<Typography style={{fontSize: 24, fontWeight: "bold", marginTop: clickedFromOrgTab ?0:null,}}>
Partner Status & Branding
</h2>
<Typography variant="body1" color="textSecondary" style={{ marginTop: 20, marginBottom: 10 }}>
</Typography>
<Typography variant="body1" color="textSecondary" style={{ marginTop: 10, marginBottom: 10, fontSize: 16 }}>
You can customize your organization's branding by uploading a logo, changing the color scheme and a lot more.
</Typography>
<Typography variant="body1" color="textSecondary" style={{ marginTop: 20, marginBottom: 10 }}>
<Typography variant="body1" color="textSecondary" style={{display: 'flex', marginTop: 20, marginBottom: 10 }}>
{isPublished ? <CheckCircleIcon style={{color: red, }} /> : <CheckCircleIcon style={{color: green, }} />}
<span style={{marginLeft: 10, color: isPublished ? red : green, }}>{isPublished ? "Not Published" : "Published"}</span>
<span style={{marginLeft: 10, color: isPublished ? red : green, fontSize: 16 }}>{isPublished ? "Not Published" : "Published"}</span>
</Typography>
<a href="https://shuffler.io/partners" target="_blank" style={{ textDecoration: "none", }}>
<Typography variant="body1" color="textSecondary" style={{ marginTop: 20, marginBottom: 10 }}>
<Typography variant="body1" color="textSecondary" style={{display: 'flex', marginTop: 20, marginBottom: 10 }}>
{!isPartner ? <CheckCircleIcon style={{color: red, }} /> : <CheckCircleIcon style={{color: green, }} />}
<Tooltip title="Official Partner Program (manual verification)" placement="top" arrow>
<span style={{marginLeft: 10, color: !isPartner ? red : green, }}>{!isPartner? "Not Officially Partnered" : "Officially Partnered"}</span>
<span style={{marginLeft: 10, color: !isPartner ? red : green, fontSize: 16}}>{!isPartner? "Not Officially Partnered" : "Officially Partnered"}</span>
</Tooltip>
</Typography>
</a>
<a href={`/partners/${selectedOrganization.creator_id}/edit`} target="_blank">
<Button disabled={isPublished} variant="contained" style={{ marginTop: 20, marginBottom: 10, }} onClick={() => {
}}>
Modify Public Partner Details
</Button>
</a>
{!isPublished ? (
<a
href={`/partners/${selectedOrganization.creator_id}/edit`}
target="_blank"
style={{ textDecoration: "none" }} // Optional: remove underline
>
<Button
variant="contained"
style={{
marginTop: 20,
marginBottom: 10,
textTransform: 'none',
fontSize: 16
}}
>
Modify Public Partner Details
</Button>
</a>
) : (
<Button
disabled
variant="contained"
style={{
marginTop: 20,
marginBottom: 10,
textTransform: 'none',
fontSize: 16
}}
>
Modify Public Partner Details
</Button>
)}
<Divider style={{marginTop: 50, marginBottom: 50, }} />
<h2>
<Typography style={{fontSize: 24, fontWeight: "bold"}}>
Partner Program
</h2>
<div style={{ display: "flex", width: 900, }}>
</Typography>
<div style={{ display: "flex", width: 900, marginTop: 10}}>
<div>
<span>
<Typography variant="body1" color="textSecondary">
<Typography variant="body1" color="textSecondary" style={{fontSize: 16}}>
By changing publishing settings, you agree to our <a href="/docs/terms_of_service" target="_blank" style={{ textDecoration: "none", color: "#f86a3e"}}>Terms of Service</a>, and acknowledge that your organization's non-sensitive data will be added as a <a target="_blank" style={{ textDecoration: "none", color: "#f86a3e"}} href="https://shuffler.io/creators">creator account</a>. None of your existing workflows, apps, or other stored data will be published. Any admin in your organization can manage the creator configuration. Becoming a creator organization IS reversible.<div/>Support: <a href="mailto:support@shuffler.io"target="_blank" style={{ textDecoration: "none", color: "#f86a3e"}}>support@shuffler.io</a>
</Typography>
{selectedOrganization.creator_id == "" ?
@@ -167,9 +194,8 @@ const Branding = (props) => {
null
}
<Button
style={{ height: 40, marginTop: 10, width: 300, }}
style={{ height: 40, marginTop: 10, width: 300, textTransform: 'none', fontSize: 18, backgroundColor: "#ff8544", color: "#1a1a1a" }}
variant={selectedOrganization.creator_id == "" ? "contained" : "outlined"}
color={selectedOrganization.creator_id == "" ? "primary" : "secondary"}
disabled={!isOrganizationReady()}
@@ -195,6 +221,8 @@ const Branding = (props) => {
</span>
</div>
</div>
</div>
</div>
</div>
)
}
+186 -77
View File
@@ -1,4 +1,4 @@
import React, { useState, useEffect } from "react";
import React, { useState, useEffect, useContext, memo } from "react";
import theme from "../theme.jsx";
import { toast } from 'react-toastify';
import ReactJson from "react-json-view-ssr";
@@ -19,6 +19,7 @@ import {
Dialog,
DialogTitle,
DialogActions,
Skeleton,
} from "@mui/material";
import {
@@ -47,6 +48,7 @@ import {
VisibilityOff as VisibilityOffIcon,
} from "@mui/icons-material";
import { validateJson, } from "../views/Workflows.jsx";
import { Context } from "../context/ContextApi.jsx";
const scrollStyle1 = {
height: 100,
@@ -65,7 +67,7 @@ const scrollStyle2 = {
}
const CacheView = (props) => {
const CacheView = memo((props) => {
const { globalUrl, userdata, serverside, orgId, isSelectedDataStore } = props;
const [orgCache, setOrgCache] = React.useState("");
const [listCache, setListCache] = React.useState([]);
@@ -78,11 +80,13 @@ const CacheView = (props) => {
const [cacheCursor, setCacheCursor] = React.useState("");
const [dataValue, setDataValue] = React.useState({});
const [editCache, setEditCache] = React.useState(false);
const [cachedLoaded, setCachedLoaded] = React.useState(false);
const [show, setShow] = useState({});
useEffect(() => {
listOrgCache(orgId);
}, []);
if(orgId?.length >0){
listOrgCache(orgId);
}
}, [orgId]);
const listOrgCache = (orgId) => {
fetch(globalUrl + `/api/v1/orgs/${orgId}/list_cache`, {
@@ -104,6 +108,7 @@ const CacheView = (props) => {
.then((responseJson) => {
if (responseJson.success === true) {
setListCache(responseJson.keys);
setCachedLoaded(true);
}
if (responseJson.cursor !== undefined && responseJson.cursor !== null && responseJson.cursor !== "") {
@@ -232,6 +237,34 @@ const CacheView = (props) => {
}
}
const handleReactJsonClipboard = (copy) => {
const elementName = "copy_element_shuffle";
let copyText = document.getElementById(elementName);
if (copyText) {
if (copy.namespace && copy.name && copy.src) {
copy = copy.src;
}
const clipboard = navigator.clipboard;
if (!clipboard) {
toast("Can only copy over HTTPS (port 3443)");
return;
}
let stringified = JSON.stringify(copy);
if (stringified.startsWith('"') && stringified.endsWith('"')) {
stringified = stringified.slice(1, -1);
}
navigator.clipboard.writeText(stringified);
toast("Copied value to clipboard, NOT json path.");
} else {
console.log("Failed to copy from " + elementName + ": ", copyText);
}
};
const modalView = (
// console.log("key:", dataValue.key),
//console.log("value:",dataValue.value),
@@ -241,11 +274,23 @@ const CacheView = (props) => {
setModalOpen(false);
}}
PaperProps={{
style: {
backgroundColor: theme.palette.surfaceColor,
color: "white",
sx: {
borderRadius: theme?.palette?.DialogStyle?.borderRadius,
border: theme?.palette?.DialogStyle?.border,
minWidth: "800px",
minHeight: "320px",
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,
},
'& .MuiDialogActions-root': {
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
},
},
}}
>
@@ -254,7 +299,7 @@ const CacheView = (props) => {
{ editCache ? "Edit Cache" : "Add Cache" }
</span>
</DialogTitle>
<div style={{ paddingLeft: "30px", paddingRight: '30px' }}>
<div style={{ paddingLeft: "30px", paddingRight: '30px', backgroundColor: "#212121", }}>
Key
<TextField
color="primary"
@@ -278,7 +323,7 @@ const CacheView = (props) => {
onChange={(e) => setKey(e.target.value)}
/>
</div>
<div style={{ paddingLeft: 30, paddingRight: 30 }}>
<div style={{ paddingLeft: 30, paddingRight: 30, backgroundColor: "#212121" }}>
<div style={{display: "flex", }}>
<Typography style={{marginTop: 25, marginBottom: 0, flex: 20, }}>
Value - ({isValidJson.valid === true ? "Valid" : "Invalid"} JSON)
@@ -320,7 +365,7 @@ const CacheView = (props) => {
</div>
<DialogActions style={{ paddingLeft: "30px", paddingRight: '30px' }}>
<Button
style={{ borderRadius: "0px" }}
style={{ borderRadius: "2px", fontSize: 16, color: "#ff8544", textTransform:"none" }}
onClick={() => {
setModalOpen(false)
setValue("")
@@ -332,7 +377,7 @@ const CacheView = (props) => {
</Button>
<Button
variant="contained"
style={{ borderRadius: "0px" }}
style={{ borderRadius: "2px", backgroundColor: "#ff8544",color: "#1a1a1a", textTransform:"none" }}
onClick={() => {
{editCache ? editOrgCache(orgId) : addOrgCache(orgId)}
@@ -348,10 +393,11 @@ const CacheView = (props) => {
);
return (
<div style={{paddingBottom: isSelectedDataStore?null:250, width: isSelectedDataStore?1030:null, padding:isSelectedDataStore?27:null, height: isSelectedDataStore?"auto":null, color: isSelectedDataStore?'#ffffff':null, backgroundColor: isSelectedDataStore?'#212121':null, borderRadius: isSelectedDataStore?'16px':null, }}>
<div style={{paddingBottom: isSelectedDataStore?null:250, minHeight: 1000, boxSizing: "border-box", width: isSelectedDataStore? "100%" :null, transition: "width 0.3s ease", padding:isSelectedDataStore?"27px 10px 27px 27px":null, height: isSelectedDataStore?"100%":null, color: isSelectedDataStore?'#ffffff':null, backgroundColor: isSelectedDataStore?'#212121':null, borderTopRightRadius: isSelectedDataStore?'8px':null, borderBottomRightRadius: isSelectedDataStore?'8px':null, borderLeft: "1px solid #494949" }}>
{modalView}
<div style={{ marginTop: isSelectedDataStore?null:20, marginBottom: 20 }}>
<div style={{height: "100%", maxHeight: 1700, overflowY: "auto", scrollbarColor: '#494949 transparent', scrollbarWidth: 'thin'}}>
<div style={{ height: "100%", width: "calc(100% - 20px)", scrollbarColor: '#494949 transparent', scrollbarWidth: 'thin' }}>
<div style={{ marginTop: isSelectedDataStore?null:20, marginBottom: 20 }}>
<h2 style={{ display: isSelectedDataStore?null: "inline" }}>Shuffle Datastore</h2>
<span style={{ marginLeft: isSelectedDataStore?null:25, color:isSelectedDataStore?"#9E9E9E":null}}>
Datastore is a permanent key-value database for storing data that can be used cross-workflow. <br/>You can store anything from lists of IPs to complex configurations.&nbsp;
@@ -366,7 +412,7 @@ const CacheView = (props) => {
</span>
</div>
<Button
style={{backgroundColor: isSelectedDataStore?'rgba(255, 132, 68, 0.2)':null, boxShadow: isSelectedDataStore ? "none":null,textTransform: isSelectedDataStore ? 'capitalize':null, color:isSelectedDataStore?"#FF8444":null, borderRadius:isSelectedDataStore?200:null, width:isSelectedDataStore?162:null, height:isSelectedDataStore?40:null}}
style={{backgroundColor: isSelectedDataStore? "#ff8544":null, fontSize: 16, boxShadow: isSelectedDataStore ? "none":null,textTransform: isSelectedDataStore ? 'capitalize':null, color:isSelectedDataStore?"#1a1a1a":null, borderRadius:isSelectedDataStore?8:null, width:isSelectedDataStore?162:null, height:isSelectedDataStore?40:null}}
variant="contained"
color="primary"
onClick={() =>{
@@ -379,7 +425,7 @@ const CacheView = (props) => {
Add Cache
</Button>
<Button
style={{ marginLeft: 5, marginRight: 15, backgroundColor: isSelectedDataStore?"#2F2F2F":null, boxShadow: isSelectedDataStore ? "none":null,textTransform: isSelectedDataStore ? 'capitalize':null,borderRadius:isSelectedDataStore?200:null, width:isSelectedDataStore?81:null, height:isSelectedDataStore?40:null, }}
style={{ marginLeft: 5, marginRight: 15, backgroundColor: isSelectedDataStore?"#2F2F2F":null, boxShadow: isSelectedDataStore ? "none":null,textTransform: isSelectedDataStore ? 'capitalize':null,borderRadius:isSelectedDataStore?8:null, width:isSelectedDataStore?81:null, height:isSelectedDataStore?40:null, }}
variant="contained"
color="primary"
onClick={() => listOrgCache(orgId)}
@@ -392,28 +438,79 @@ const CacheView = (props) => {
marginBottom: 20,
}}
/>}
<List style={{borderRadius: isSelectedDataStore?8:null, border:isSelectedDataStore?"1px solid #494949":null, marginTop:isSelectedDataStore?24:null}}>
<ListItem style={{width: isSelectedDataStore?"100%":null, borderBottom:isSelectedDataStore?"1px solid #494949":null}}>
<ListItemText
primary="Key"
style={{ minWidth: isSelectedDataStore?200:250, maxWidth: isSelectedDataStore?200:250, }}
/>
<ListItemText
primary="Value"
style={{ minWidth: isSelectedDataStore?300:400, maxWidth: isSelectedDataStore?300:400, overflowX: "auto", overflowY: "hidden", }}
/>
<ListItemText
primary="Actions"
style={{ minWidth: 150, maxWidth: 150, marginLeft: isSelectedDataStore?80:null,}}
/>
<ListItemText
style={{textAlign:isSelectedDataStore?"center":null}}
primary="Updated"
/>
<div
style={{
borderRadius: 8,
marginTop: 24,
border: "1px solid #494949",
width: "100%",
overflowX: "auto",
paddingBottom: 0,
}}
>
<List
style={{
borderRadius: 8,
paddingBottom: 0,
tableLayout: "auto",
display: "table",
width: '100%',
minWidth: 800,
overflowX: "auto",
}}>
<ListItem style={{width: isSelectedDataStore?"100%":null, borderBottom:isSelectedDataStore?"1px solid #494949":null, display: "table-row"}}>
{["Key", "Value", "Actions", "Updated"].map((header, index) => (
<ListItemText
key={index}
primary={header}
style={{
display: "table-cell",
padding: "0px 8px 8px 8px",
whiteSpace: "nowrap",
overflow: "hidden",
textOverflow: "ellipsis",
borderBottom: "1px solid #494949"
}}
/>
))}
</ListItem>
{listCache === undefined || listCache === null
? null
: listCache.map((data, index) => {
{cachedLoaded === false
? [...Array(6)].map((_, rowIndex) => (
<ListItem
key={rowIndex}
style={{
display: "table-row",
backgroundColor: "#212121",
}}
>
{Array(4)
.fill()
.map((_, colIndex) => (
<ListItemText
key={colIndex}
style={{
display: "table-cell",
padding: "8px",
}}
>
<Skeleton
variant="text"
animation="wave"
sx={{
backgroundColor: "#1a1a1a",
height: "20px",
borderRadius: "4px",
}}
/>
</ListItemText>
))}
</ListItem>
))
: listCache?.length === 0 ? (
<Typography style={{ textAlign: "center", marginTop: 20, marginBottom: 20, minWidth: 1000, }}>
No Keys Found
</Typography>
): listCache?.map((data, index) => {
var bgColor = isSelectedDataStore? "#212121":"#27292d";
if (index % 2 === 0) {
bgColor = isSelectedDataStore? "#1A1A1A":"#1f2023";
@@ -421,49 +518,61 @@ const CacheView = (props) => {
const validate = validateJson(data.value);
return (
<ListItem key={index} style={{ backgroundColor: bgColor, maxHeight: 300, overflow: "auto", }}>
<ListItem key={index} style={{display:'table-row', backgroundColor: bgColor, maxHeight: 300, overflow: "auto", borderBottomLeftRadius: listCache?.length - 1 === index ? 8 : 0, borderBottomRightRadius: listCache?.length - 1 === index ? 8 : 0}}>
<ListItemText
style={{
maxWidth: 200,
minWidth: 200,
display: "table-cell",
overflow: "hidden",
padding: 8,
verticalAlign: "middle",
}}
primary={data.key}
/>
<ListItemText
style={{
minWidth: 300,
maxWidth: 300,
// height:200,
overflowX: "hidden",
display: "table-cell",
overflowY: "auto",
overflowX: "auto",
border: "1px solid rgba(255,255,255,0.7)",
borderRadius: 6,
backgroundColor: "#151515",
maxHeight: 300,
verticalAlign: "middle",
}}
primary={validate.valid ?
<ReactJson
src={validate.result}
theme={theme.palette.jsonTheme}
style={theme.palette.reactJsonStyle}
collapsed={true}
enableClipboard={(copy) => {
//handleReactJsonClipboard(copy);
}}
displayDataTypes={false}
onSelect={(select) => {
//HandleJsonCopy(showResult, select, data.action.label);
//console.log("SELECTED!: ", select);
}}
name={"value"}
/>
primary={validate.valid ?
<ReactJson
src={validate.result}
theme={theme.palette.jsonTheme}
style={{
padding: 5,
maxHeight: 300,
overflowY: "auto",
}}
collapsed={true}
enableClipboard={(copy) => {
// handleReactJsonClipboard(copy);
}}
collapseStringsAfterLength={theme.palette.jsonCollapseStringsAfterLength}
iconStyle={theme.palette.jsonIconStyle}
displayDataTypes={false}
onSelect={(select) => {
// HandleJsonCopy(showResult, select, data.action.label);
console.log("SELECTED!: ", select);
}}
name={"value"}
/>
:
data.value
}
/>
<ListItemText
style={{
maxWidth: 200,
minWidth: 200,
marginLeft: 50,
display: "table-cell",
verticalAlign: "middle",
padding: 8
}}
primary=<span style={{ display: "inline" }}>
primary={(
<span style={{ display: "inline" }}>
<Tooltip
title="Edit item"
style={{}}
@@ -482,9 +591,7 @@ const CacheView = (props) => {
setModalOpen(true)
}}
>
<EditIcon
style={{ color: "white" }}
/>
<img src="/icons/editIcon.svg" alt="edit" />
</IconButton>
</span>
</Tooltip>
@@ -508,7 +615,6 @@ const CacheView = (props) => {
</Tooltip>
<Tooltip
title={"Delete item"}
style={{ marginLeft: 25, }}
aria-label={"Delete"}
>
<span>
@@ -519,18 +625,18 @@ const CacheView = (props) => {
//deleteFile(orgId);
}}
>
<DeleteIcon
style={{ color: "white" }}
/>
<img src="/icons/deleteIcon.svg" alt="delete" />
</IconButton>
</span>
</Tooltip>
</span>
)}
/>
<ListItemText
style={{
maxWidth: 225,
minWidth: 225,
display: "table-cell",
verticalAlign: "middle",
padding: 8
}}
primary={new Date(data.edited * 1000).toISOString()}
/>
@@ -538,8 +644,11 @@ const CacheView = (props) => {
);
})}
</List>
</div>
</div>
</div>
</div>
);
}
export default CacheView;
});
export default memo(CacheView);
+2 -2
View File
@@ -1149,7 +1149,7 @@ const EditWorkflow = (props) => {
</div>
: null}
{/*!isEditing ? <>
{!isEditing ? <>
<div style={{ marginTop: 20, }}>
<FormControlLabel
control={<Checkbox />}
@@ -1164,7 +1164,7 @@ const EditWorkflow = (props) => {
/>
</div>
</> : null*/}
</> : null}
<Tooltip color="primary" title={"Add more details"} placement="top">
<Button
+398 -330
View File
@@ -1,4 +1,4 @@
import React, { useState, useEffect } from "react";
import React, { useState, useEffect, useContext, memo } from "react";
import { toast } from 'react-toastify';
import {
@@ -21,6 +21,7 @@ import {
DialogContent,
DialogActions,
Typography,
Skeleton,
} from "@mui/material";
import {
@@ -39,11 +40,13 @@ import {
import Dropzone from "../components/Dropzone.jsx";
import ShuffleCodeEditor from "../components/ShuffleCodeEditor1.jsx";
import theme from "../theme.jsx";
import { Context } from "../context/ContextApi.jsx";
const Files = (props) => {
const Files = memo((props) => {
const { globalUrl, userdata, serverside, selectedOrganization, isCloud,isSelectedFiles } = props;
const [files, setFiles] = React.useState([]);
const [showLoader, setShowLoader] = useState(true)
const [selectedCategory, setSelectedCategory] = React.useState("default");
const [openFileId, setOpenFileId] = React.useState(false);
const [fileCategories, setFileCategories] = React.useState([]);
@@ -58,16 +61,13 @@ const Files = (props) => {
const [downloadBranch, setDownloadBranch] = React.useState("main");
const [downloadFolder, setDownloadFolder] = React.useState("translation_standards");
const [contentLoading, setContentLoading] = React.useState(false)
//const alert = useAlert();
const allowedFileTypes = ["txt", "py", "yaml", "yml","json", "html", "js", "csv", "log", "eml", "msg", "md", "xml", "sh", "bat", "ps1", "psm1", "psd1", "ps1xml", "pssc", "psc1", "response"]
var upload = "";
const handleKeyDown = (event) => {
if (event.key === 'Enter') {
console.log('do validate')
console.log("new namespace name->",event.target.value);
fileCategories.push(event.target.value);
setSelectedCategory(event.target.value);
setRenderTextBox(false);
@@ -136,6 +136,7 @@ const Files = (props) => {
.then((responseJson) => {
if (responseJson.files !== undefined && responseJson.files !== null) {
setFiles(responseJson.files);
setShowLoader(false)
} else if (responseJson.list !== undefined && responseJson.list !== null) {
// Set the "namespace" field in all items
if (namespace !== undefined && namespace !== null) {
@@ -147,8 +148,10 @@ const Files = (props) => {
}
setFiles(responseJson.list);
setShowLoader(false)
} else {
setFiles([]);
setShowLoader(false)
}
if (namespace === undefined || namespace === null || namespace === "default") {
@@ -244,15 +247,27 @@ const Files = (props) => {
const fileDownloadModal = loadFileModalOpen ?
<Dialog
open={loadFileModalOpen}
onClose={() => {}}
PaperProps={{
style: {
backgroundColor: theme.palette.surfaceColor,
color: "white",
minWidth: "800px",
minHeight: "320px",
},
}}
sx: {
borderRadius: theme?.palette?.DialogStyle?.borderRadius,
border: theme?.palette?.DialogStyle?.border,
fontFamily: theme?.typography?.fontFamily,
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
zIndex: 1000,
minWidth: "800px",
minHeight: "320px",
overflow: "hidden",
'& .MuiDialogContent-root': {
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
},
'& .MuiDialogTitle-root': {
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
},
'& .MuiDialogActions-root': {
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
},
}
}}
>
<DialogTitle>
<div style={{ color: "rgba(255,255,255,0.9)" }}>
@@ -364,7 +379,7 @@ const Files = (props) => {
</DialogContent>
<DialogActions>
<Button
style={{ borderRadius: "0px" }}
style={{ borderRadius: "2px", textTransform: 'none', fontSize:16, color: "#ff8544" }}
onClick={() => setLoadFileModalOpen(false)}
color="primary"
>
@@ -372,7 +387,7 @@ const Files = (props) => {
</Button>
<Button
variant="contained"
style={{ borderRadius: "0px" }}
style={{ borderRadius: "2px", textTransform: 'none', fontSize:16, color: "#1a1a1a", backgroundColor: "#ff8544" }}
disabled={downloadUrl.length === 0 || !downloadUrl.includes("http")}
onClick={() => {
handleGithubValidation();
@@ -637,24 +652,18 @@ const Files = (props) => {
return (
<Dropzone
style={{
maxWidth: window.innerWidth > 1366 ? 1366 : 1200,
margin: "auto",
padding: isSelectedFiles ? null : 20,
width: '100%',
height: "100%",
}}
onDrop={uploadFile}
>
<div style={{position: "relative", width: isSelectedFiles? 1030: null, padding:isSelectedFiles?27:null, height: isSelectedFiles?"auto":null, color: isSelectedFiles?'#ffffff':null, backgroundColor: isSelectedFiles?'#212121':null, borderRadius: isSelectedFiles?'16px':null,}}>
<div style={{width: "100%", minHeight: 1100, boxSizing: 'border-box', padding: "27px 10px 19px 27px", height:"100%", backgroundColor: '#212121',borderTopRightRadius: '8px', borderBottomRightRadius: 8, borderLeft: "1px solid #494949", }}>
<Tooltip color="primary" title={"Import files to Shuffle from Git"} placement="top">
<IconButton
color="secondary"
style={{position: "absolute", right: 0, top: isSelectedFiles?null:0, left: isSelectedFiles? 990:null }}
variant="text"
onClick={() => setLoadFileModalOpen(true)}
>
<CloudDownloadIcon />
</IconButton>
</Tooltip>
<div style={{height: "100%", maxHeight: 1700,overflowY: 'auto', scrollbarColor: '#494949 transparent', scrollbarWidth: 'thin'}}>
<div style={{ height: "100%", width: "calc(100% - 20px)", scrollbarColor: '#494949 transparent', scrollbarWidth: 'thin' }}>
<DownloadFileIcon setLoadFileModalOpen={setLoadFileModalOpen} isSelectedFiles={isSelectedFiles} />
{fileDownloadModal}
@@ -681,9 +690,9 @@ const Files = (props) => {
onClick={() => {
upload.click();
}}
style={{backgroundColor: isSelectedFiles?'rgba(255, 132, 68, 0.2)':null, color:isSelectedFiles?"#FF8444":null, borderRadius:isSelectedFiles?200:null, width:isSelectedFiles?162:null, height:isSelectedFiles?40:null, boxShadow: isSelectedFiles?'none':null,}}
style={{backgroundColor: isSelectedFiles?'#ff8544':null, color:isSelectedFiles?"#212121":null, textTransform: 'none',fontSize: 16, borderRadius:isSelectedFiles?8:null, width:isSelectedFiles?143:null, height:isSelectedFiles?35:null, boxShadow: isSelectedFiles?'none':null,}}
>
<PublishIcon /> Upload files
Upload files
</Button>
{/* <FileCategoryInput
isSet={renderTextBox} /> */}
@@ -702,7 +711,7 @@ const Files = (props) => {
}}
/>
<Button
style={{ marginLeft: 5, marginRight: 15, backgroundColor:isSelectedFiles?"#2F2F2F":null,borderRadius:isSelectedFiles?200:null, width:isSelectedFiles?81:null, height:isSelectedFiles?40:null, boxShadow: isSelectedFiles?'none':null, }}
style={{ marginLeft: 16, marginRight: 15, backgroundColor:isSelectedFiles?"#2F2F2F":null,borderRadius:isSelectedFiles?8:null, width:isSelectedFiles?81:null, height:isSelectedFiles?35:null, boxShadow: isSelectedFiles?'none':null, }}
variant="contained"
color="primary"
onClick={() => getFiles(selectedCategory)}
@@ -710,21 +719,23 @@ const Files = (props) => {
<CachedIcon />
</Button>
{/* <div style={{height: 35, width: 1, color: "#494949"}}></div> */}
{fileCategories !== undefined &&
fileCategories !== null &&
fileCategories.length > 1 ? (
<FormControl style={{ minWidth: 150, maxWidth: 150 }}>
<InputLabel id="input-namespace-label">File Category</InputLabel>
<Select
labelId="input-namespace-select-label"
id="input-namespace-select-id"
style={{
color: "white",
minWidth: 150,
maxWidth: 150,
minWidth: 122,
maxWidth: 122,
height: 35,
float: "right",
position: 'relative',
top: 8
}}
value={selectedCategory}
onChange={(event) => {
@@ -759,7 +770,7 @@ const Files = (props) => {
</Select>
</FormControl>
) : null}
<div style={{display: "inline-flex", position:"relative"}}>
<div style={{display: "inline-flex", position:"relative", top: 8}}>
{renderTextBox ?
<Tooltip title={"Close"} style={{}} aria-label={""}>
@@ -777,13 +788,14 @@ const Files = (props) => {
:
<Tooltip title={"Add new file category"} style={{}} aria-label={""}>
<Button
style={{ marginLeft: 5, marginRight: 15 }}
style={{ marginLeft: 5, marginRight: 15, width: 169, height: 35, backgroundColor: "#494949", textTransform: 'none', fontSize: 16, color: "#f1f1f1" }}
color="primary"
onClick={() => {
setRenderTextBox(true);
}}
>
<AddIcon/>
File Category
</Button>
</Tooltip>
}
@@ -824,323 +836,379 @@ const Files = (props) => {
backgroundColor: theme.palette.inputColor,
}}
/>}
<List style={{borderRadius: isSelectedFiles?8:null, border:isSelectedFiles?"1px solid #494949":null, marginTop:isSelectedFiles?24:null}}>
<ListItem style={{width:isSelectedFiles?"100%":null, borderBottom:isSelectedFiles?"1px solid #494949":null}}>
{/*
<ListItemText
primary="Updated"
style={{ maxWidth: 185, minWidth: 185 }}
/>
*/}
<ListItemText
primary="Name"
style={{
maxWidth: 250,
minWidth: 250,
overflow: "hidden",
marginLeft: 10,
<div
style={{
borderRadius: 8,
marginTop: 24,
border: "1px solid #494949",
width: "100%",
overflowX: "auto",
paddingBottom: 0,
}}
>
<List
style={{
width: '100%',
tableLayout: "auto",
display: "table",
minWidth: 800,
overflowX: "auto"
}}
>
<ListItem style={{width:isSelectedFiles?"100%":null, borderBottom:isSelectedFiles?"1px solid #494949":null, display: 'table-row'}}>
{["Name", "Workflow", "Md5", "Status", "Filesize", "Actions"].map((header, index) => (
<ListItemText
key={index}
primary={header}
style={{
display: "table-cell",
padding: "0px 8px 8px 8px",
whiteSpace: "nowrap",
textOverflow: "ellipsis",
borderBottom: "1px solid #494949",
verticalAlign: "middle"
}}
primaryTypographyProps={{
style: {
paddingLeft: index === 3 ? 80 : 10,
}
}}
/>
<ListItemText
primary="Workflow"
style={{ maxWidth: 100, minWidth: 100, overflow: "hidden" }}
/>
<ListItemText
primary="Md5"
style={{ minWidth: 300, maxWidth: 300, overflow: "hidden" }}
/>
<ListItemText
primary="Status"
style={{ minWidth: 75, maxWidth: 75, marginLeft: 10 }}
/>
<ListItemText
primary="Filesize"
style={{ minWidth: 125, maxWidth: 125 }}
/>
<ListItemText primary="Actions" />
/>
))}
</ListItem>
{files === undefined || files === null || files.length === 0 ? null :
files.map((file, index) => {
if (file.namespace === "") {
file.namespace = "default";
}
if (file.namespace !== selectedCategory) {
return null;
}
var bgColor = isSelectedFiles ? "#212121":"#27292d";
if (index % 2 === 0) {
bgColor = isSelectedFiles ? "#1A1A1A":"#1f2023";
}
const filenamesplit = file.filename.split(".")
const iseditable = file.filesize < 2000000 && file.status === "active" && allowedFileTypes.includes(filenamesplit[filenamesplit.length-1])
return (
<ListItem
key={index}
style={{
backgroundColor: bgColor,
maxHeight: 100,
overflow: "hidden",
}}
>
{/*
<ListItemText
{showLoader ?
[...Array(6)].map((_, rowIndex) => (
<ListItem
key={rowIndex}
style={{
display: "table-row",
backgroundColor: "#212121",
}}
>
{Array(6)
.fill()
.map((_, colIndex) => (
<ListItemText
key={colIndex}
style={{
display: "table-cell",
padding: "8px",
}}
>
<Skeleton
variant="text"
animation="wave"
sx={{
backgroundColor: "#1a1a1a",
height: "20px",
borderRadius: "4px",
}}
/>
</ListItemText>
))}
</ListItem>
)):
files.length === 0 ? (
<div style={{textAlign: "center"}}>
<Typography style={{padding: 25, fontSize: 18, textAlign: 'center'}}>
No files found
</Typography>
</div>
):(
files?.map((file, index) => {
if (file.namespace === "") {
file.namespace = "default";
}
if (file.namespace !== selectedCategory) {
return null;
}
var bgColor = isSelectedFiles ? "#212121":"#27292d";
if (index % 2 === 0) {
bgColor = isSelectedFiles ? "#1A1A1A":"#1f2023";
}
const filenamesplit = file.filename.split(".")
const iseditable = file.filesize < 2000000 && file.status === "active" && allowedFileTypes.includes(filenamesplit[filenamesplit.length-1])
return (
<ListItem
key={index}
style={{
maxWidth: isSelectedFiles ? 170:225,
minWidth: isSelectedFiles ? 170:225,
display: 'table-row',
backgroundColor: bgColor,
maxHeight: 100,
overflow: "hidden",
borderBottomLeftRadius: files?.length - 1 === index ? 8 : 0,
borderBottomRightRadius: files?.length - 1 === index ? 8 : 0,
}}
primary={new Date(file.updated_at * 1000).toISOString()}
/>
*/}
<ListItemText
style={{
maxWidth: 250,
minWidth: 250,
overflow: "hidden",
marginLeft: 10,
}}
primary={file.filename}
/>
<ListItemText
primary={
file.workflow_id === "global" || file.workflow_id === "" || file.workflow_id === null || file.workflow_id === undefined ?
<IconButton
disabled={file.workflow_id === "global"}
>
<OpenInNewIcon
style={{
color:
file.workflow_id !== "global"
? "white"
: "grey",
}}
/>
</IconButton>
: (
>
{/*
<ListItemText
style={{
maxWidth: isSelectedFiles ? 170:225,
minWidth: isSelectedFiles ? 170:225,
overflow: "hidden",
}}
primary={new Date(file.updated_at * 1000).toISOString()}
/>
*/}
<ListItemText
primaryTypographyProps={{
style: {
display: 'table-cell',
maxWidth: "170px",
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
overflow: 'hidden',
padding: 8
},
}}
primary={file.filename}
/>
<ListItemText
primary={
file.workflow_id === "global" || file.workflow_id === "" || file.workflow_id === null || file.workflow_id === undefined ?
<IconButton
disabled={file.workflow_id === "global"}
style={{marginLeft: 10}}
>
<OpenInNewIcon
style={{
color:
file.workflow_id !== "global"
? "#FF8444"
: "grey",
}}
/>
</IconButton>
: (
<Tooltip
title={"Go to workflow"}
style={{}}
aria-label={"Download"}
>
<span>
<a
rel="noopener noreferrer"
style={{
textDecoration: "none",
color: "#f85a3e",
}}
href={`/workflows/${file.workflow_id}`}
target="_blank"
>
<IconButton
disabled={file.workflow_id === "global"}
style={{marginLeft: 10}}
>
<OpenInNewIcon
style={{
color:
file.workflow_id !== "global"
? "#FF8444"
: "grey",
}}
/>
</IconButton>
</a>
</span>
</Tooltip>
)
}
style={{
display: 'table-cell',
overflow: "hidden",
}}
/>
<ListItemText
primary={(
<Tooltip title={file.md5_sum}>
{file.md5_sum}
</Tooltip>
)}
primaryTypographyProps={{
style:{
display: 'table-cell',
marginLeft:isSelectedFiles? 15:null,
overflow: "hidden",
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
maxWidth: 200,
}
}}
/>
<ListItemText
primary={file.status}
style={{
display: 'table-cell',
overflow: "hidden",
textAlign:isSelectedFiles?"left":null,
color: file.status === "active" ? "#2BC07E" : "#FD4C62"
}}
/>
<ListItemText
primary={file.filesize}
style={{
display: 'table-cell',
overflow: "hidden",
textAlign:'center'
}}
/>
<ListItemText
primary=<span style={{ display:"inline"}}>
<Tooltip
title={"Go to workflow"}
title={`Edit File (${allowedFileTypes.join(", ")}). Max size 2MB`}
style={{}}
aria-label={"Edit"}
>
<span>
<IconButton
disabled={!iseditable}
style = {{padding: "6px", }}
onClick={() => {
setOpenEditor(true)
setOpenFileId(file.id)
readFileData(file)
}}
>
<img src="/icons/editIcon.svg" alt="edit icon"
style={{color: iseditable ? "white" : "grey",}}
/>
</IconButton>
</span>
</Tooltip>
{/*
<Tooltip
title={"Public URL"}
style={{}}
>
<span>
<IconButton
style = {{padding: "6px"}}
disabled={file.status !== "active"}
onClick={() => {
// Open the file, without downloading it
window.open(`${globalUrl}/api/v1/files/${file.id}/content?type=text&authorization=${file.public_authorization}`, "_blank noreferrer noopener")
}}
>
<LinkIcon
style={{
color:
file.status === "active"
? "white"
: "grey",
}}
/>
</IconButton>
</span>
</Tooltip>
*/}
<Tooltip
title={"Download file"}
style={{}}
aria-label={"Download"}
>
<span>
<a
rel="noopener noreferrer"
style={{
textDecoration: "none",
color: "#f85a3e",
<IconButton
style = {{padding: "6px"}}
disabled={file.status !== "active"}
onClick={() => {
downloadFile(file);
}}
href={`/workflows/${file.workflow_id}`}
target="_blank"
>
<IconButton
disabled={file.workflow_id === "global"}
>
<OpenInNewIcon
style={{
color:
file.workflow_id !== "global"
? "white"
: "grey",
}}
/>
</IconButton>
</a>
<img src="/icons/downloadIcon.svg" alt="download icon"
style={{
color:
file.status === "active"
? "white"
: "grey",
}}
/>
</IconButton>
</span>
</Tooltip>
)
}
style={{
minWidth: 100,
maxWidth: 100,
overflow: "hidden",
textAlign: isSelectedFiles?"center":null
}}
/>
<ListItemText
primary={file.md5_sum}
style={{
minWidth: 300,
maxWidth: 300,
marginLeft:isSelectedFiles? 15:null,
overflow: isSelectedFiles?"auto":"hidden",
}}
/>
<ListItemText
primary={file.status}
style={{
minWidth: 75,
maxWidth: 75,
overflow: "hidden",
textAlign:isSelectedFiles?"center":null,
marginLeft: 10,
}}
/>
<ListItemText
primary={file.filesize}
style={{
minWidth: isSelectedFiles?80:125,
maxWidth: isSelectedFiles?80:125,
marginLeft: isSelectedFiles?15:null,
overflow: "hidden",
}}
/>
<ListItemText
primary=<span style={{ display:"inline"}}>
<Tooltip
title={`Edit File (${allowedFileTypes.join(", ")}). Max size 2MB`}
style={{}}
aria-label={"Edit"}
>
<span>
<IconButton
disabled={!iseditable}
style = {{padding: "6px"}}
onClick={() => {
setOpenEditor(true)
setOpenFileId(file.id)
readFileData(file)
}}
>
<EditIcon
style={{color: iseditable ? "white" : "grey",}}
/>
</IconButton>
</span>
</Tooltip>
{/*
<Tooltip
title={"Public URL"}
style={{}}
>
<span>
<IconButton
style = {{padding: "6px"}}
disabled={file.status !== "active"}
onClick={() => {
// Open the file, without downloading it
window.open(`${globalUrl}/api/v1/files/${file.id}/content?type=text&authorization=${file.public_authorization}`, "_blank noreferrer noopener")
}}
>
<LinkIcon
style={{
color:
file.status === "active"
? "white"
: "grey",
}}
/>
</IconButton>
</span>
</Tooltip>
*/}
<Tooltip
title={"Download file"}
style={{}}
aria-label={"Download"}
>
<span>
<IconButton
style = {{padding: "6px"}}
disabled={file.status !== "active"}
onClick={() => {
downloadFile(file);
}}
>
<CloudDownloadIcon
style={{
color:
file.status === "active"
? "white"
: "grey",
}}
/>
</IconButton>
</span>
</Tooltip>
<Tooltip
title={"Copy file ID"}
style={{}}
aria-label={"copy"}
>
<IconButton
style = {{padding: "6px"}}
onClick={() => {
const elementName = "copy_element_shuffle";
var copyText =
document.getElementById(elementName);
if (
copyText !== null &&
copyText !== undefined
) {
const clipboard = navigator.clipboard;
if (clipboard === undefined) {
toast(
"Can only copy over HTTPS (port 3443)"
);
return;
}
navigator.clipboard.writeText(file.id);
copyText.select();
copyText.setSelectionRange(
0,
99999
); /* For mobile devices */
/* Copy the text inside the text field */
document.execCommand("copy");
toast(file.id + " copied to clipboard");
}
}}
<Tooltip
title={"Copy file ID"}
style={{}}
aria-label={"copy"}
>
<FileCopyIcon style={{ color: "white" }} />
</IconButton>
</Tooltip>
<Tooltip
title={"Delete file"}
style={{marginLeft: isSelectedFiles?5:15, }}
aria-label={"Delete"}
>
<span>
<IconButton
disabled={file.status !== "active"}
style = {{padding: "6px"}}
onClick={() => {
deleteFile(file)
console.log("file is : ", file)
navigator.clipboard.writeText(file.id);
document.execCommand("copy");
toast(file.id + " copied to clipboard");
}}
>
<DeleteIcon
style={{
color:
file.status === "active"
? "white"
: "grey",
}}
/>
<img src="/icons/copyIcon.svg" alt="copy icon" style={{ color: "white" }} />
</IconButton>
</span>
</Tooltip>
</span>
style={{
minWidth: 250,
maxWidth: 250,
// overflow: "hidden",
}}
/>
</ListItem>
);
})
</Tooltip>
<Tooltip
title={"Delete file"}
style={{marginLeft: isSelectedFiles?5:15, }}
aria-label={"Delete"}
>
<span>
<IconButton
disabled={file.status !== "active"}
style = {{padding: "6px"}}
onClick={() => {
deleteFile(file)
}}
>
<img src="/icons/deleteIcon.svg" alt="delete icon"
style={{
color:
file.status === "active"
? "white"
: "grey",
}}
/>
</IconButton>
</span>
</Tooltip>
</span>
style={{
display: 'table-cell',
textAlign:'center'
// overflow: "hidden",
}}
/>
</ListItem>
);
})
)
}
</List>
</div>
</div>
</div>
</div>
</Dropzone>
)
}
})
export default Files;
export default memo(Files);
const DownloadFileIcon = memo(({ setLoadFileModalOpen, isSelectedFiles, }) => {
const { leftSideBarOpenByClick } = useContext(Context)
return(
<Tooltip color="primary" title={"Import files to Shuffle from Git"} placement="top">
<IconButton
color="secondary"
style={{position: "absolute", right: 0, top: isSelectedFiles?null:0, left: isSelectedFiles? leftSideBarOpenByClick ? "90%": "85%":null, transition: "left 0.3s ease" }}
variant="text"
onClick={() => setLoadFileModalOpen(true)}
>
<CloudDownloadIcon />
</IconButton>
</Tooltip>
)
})
+246 -89
View File
@@ -7,6 +7,7 @@ import {
Add as AddIcon,
BorderColor,
Close as CloseIcon,
ConstructionOutlined,
} from "@mui/icons-material";
import SearchBox from "./SearchData.jsx";
import {
@@ -85,7 +86,21 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
return orgOptions.find((option) => option.name === selectedOrg);
}, [selectedOrg, orgOptions]);
//With this code it is opening search bar on google chrome search bar as well which is not required
useEffect(() => {
const handleKeyDown = (event) => {
if ((event.ctrlKey || event.metaKey) && event.key === "k") {
event.preventDefault();
setSearchBarModalOpen((prev)=> !prev);
}
};
window.addEventListener("keydown", handleKeyDown);
return () => {
window.removeEventListener("keydown", handleKeyDown);
};
}, [setSearchBarModalOpen]);
const CustomPopper = (props) => {
return (
@@ -141,7 +156,7 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
>
{props.children}
</Box>
<Link to="/admin2" style={hrefStyle}>
<Link to="/admin?tab=tenants" style={hrefStyle}>
<Box
sx={{
width: "100%",
@@ -446,7 +461,7 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
},
}}
>
<Link to="/admin2" style={hrefStyle}>
<Link to="/admin" style={hrefStyle}>
<MenuItem
onClick={(event) => {
handleClose();
@@ -466,7 +481,7 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
</Link>
<Divider style={{ marginTop: 10, marginBottom: 10, }} />
<Link to="/admin2?admin_tab=priorities" style={hrefStyle}>
<Link to="/admin?admin_tab=notifications" style={hrefStyle}>
<MenuItem
onClick={(event) => {
handleClose();
@@ -478,7 +493,7 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
})
</MenuItem>
</Link>
<Link to="/usecases2" style={hrefStyle}>
<Link to="/usecases" style={hrefStyle}>
<MenuItem
onClick={(event) => {
handleClose();
@@ -641,7 +656,7 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
};
const getRegionTag = (region_url) => {
let regiontag = "eu";
let regiontag = "UK";
if (
region_url !== undefined &&
region_url !== null &&
@@ -653,15 +668,25 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
regiontag = namesplit[namesplit.length - 1];
if (regiontag === "california") {
regiontag = "us";
regiontag = "US";
} else if (regiontag === "frankfurt") {
regiontag = "fr";
regiontag = "EU";
} else if (regiontag === "ca"){
regiontag = "CA";
}
}
}
return regiontag;
};
useEffect(() => {
if(activeOrgName !== userdata?.active_org?.name){
setActiveOrgName(userdata?.active_org?.name || "Select Organization");
}
}, [userdata]);
const CheckOrgStates = useCallback(() => {
setOrgOptions(
userdata?.orgs?.map((org) => ({
@@ -723,6 +748,20 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
</Dialog>
);
const getRegionFlag = (region_url) => {
var region = "gb";
const regionMapping = {
"US": "us",
"EU": "eu",
"CA": "ca",
"UK": "gb"
};
region = regionMapping[region_url] || "gb";
return `https://flagcdn.com/48x36/${region}.png`;
};
return (
<div
style={{
@@ -736,7 +775,8 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
boxShadow: "0px 4px 12px rgba(0, 0, 0, 0.2)" ,
resize: 'both',
height: "calc(100vh - 32px)",
zoom: 0.8,
height: "calc((100vh - 32px)*1.2)",
}}
>
{modalView}
@@ -759,7 +799,7 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
<Box
sx={{
display: "flex",
alignItems: "center",
// alignItems: "center",
justifyContent: "center",
position: "relative",
right: !expandLeftNav && 8,
@@ -814,46 +854,62 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
{expandLeftNav ? (
<Fade in={expandLeftNav} timeout={500}>
<TextField
id="sidebar-search"
placeholder= "Search"
sx={{
width: "100%",
maxWidth: 228,
"& .MuiInputBase-root": {
height: 35,
padding: 0,
},
"& .MuiOutlinedInput-root": {
cursor: "pointer",
width: 228,
"& fieldset": {
borderColor: "#494949",
},
"&:hover fieldset": {
borderColor: "#ffffff",
display: "block"
},
"&.Mui-focused fieldset": {
borderColor: "#A9A9A9",
},
},
"& input": {
padding: "8px 14px",
fontSize: "14px",
color: "#C8C8C8",
},
backgroundColor: "transparent",
}}
variant="outlined"
InputProps={{
startAdornment: (
<SearchIcon style={{ color: "#CDCDCD", width: 24, height: 24, marginLeft: 16}} />
),
disableUnderline: true,
}}
onClick={()=>{setSearchBarModalOpen(true)}}
onChange={()=> {setSearchBarModalOpen(true)}}
/>
id="sidebar-search"
placeholder="Search"
sx={{
width: "100%",
maxWidth: 228,
"& .MuiInputBase-root": {
height: 35,
padding: 0,
},
"& .MuiOutlinedInput-root": {
cursor: "pointer",
width: 228,
"& fieldset": {
borderColor: "#494949",
},
"&:hover fieldset": {
borderColor: "#ffffff",
display: "block",
},
"&.Mui-focused fieldset": {
borderColor: "#A9A9A9",
},
},
"& input": {
padding: "8px 14px",
fontSize: "14px",
color: "#C8C8C8",
},
backgroundColor: "transparent",
}}
variant="outlined"
InputProps={{
startAdornment: (
<SearchIcon style={{ color: "#CDCDCD", width: 24, height: 24, marginLeft: 16 }} />
),
endAdornment: (
<span
style={{
color: "#C8C8C8",
fontSize: "12px",
marginRight: 16,
whiteSpace: "nowrap",
}}
>
<kbd>Ctrl</kbd>/<kbd>Cmd</kbd>+<kbd>K</kbd>
</span>
),
disableUnderline: true,
}}
onClick={() => {
setSearchBarModalOpen(true);
}}
onChange={() => {
setSearchBarModalOpen(true);
}}
/>
</Fade>
):(
<>
@@ -891,10 +947,16 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
setCurrentOpenTab("automate");
localStorage.setItem("lastTabOpenByUser", "automate");
}}
sx={{
variant="text"
style={{
...ButtonStyle,
backgroundColor: ((currentOpenTab === "automate" && currentPath.includes("/dashboards/automate"))|| (!expandLeftNav && (currentPath === "/workflows" || currentPath === "/usecases2" || currentPath.includes("/search"))))? "#2f2f2f": "transparent",
"&:hover": { backgroundColor: "#2f2f2f" },
}}
onMouseOver={(event)=>{
event.currentTarget.style.backgroundColor = "#2f2f2f";
}}
onMouseOut={(event)=>{
event.currentTarget.style.backgroundColor = ((currentOpenTab === "automate" && currentPath.includes("/dashboards/automate"))|| (!expandLeftNav && (currentPath === "/workflows" || currentPath === "/usecases2" || currentPath.includes("/search"))))? "#2f2f2f": "transparent";
}}
>
<img
@@ -920,8 +982,15 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
setOpenautomateTab((prev) => !prev);
setOpenSecurityTab(false);
}}
sx={{
marginLeft: 0.625,
style={{
color: "#FFFFFF",
marginLeft: 0.625,
}}
onMouseOver={(event)=>{
event.currentTarget.style.backgroundColor = "#2f2f2f";
}}
onMouseOut={(event)=>{
event.currentTarget.style.backgroundColor = "transparent";
}}
>
{openautomatetab ? (
@@ -952,23 +1021,28 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
flexDirection: "column",
paddingLeft: 16,
gap: 4,
marginTop: expandLeftNav ? 16 : 0,
}}
>
<Link to="/usecases2" style={hrefStyle}>
<Link to="/usecases" style={hrefStyle}>
<Button
onClick={(event) => {
setCurrentOpenTab("usecases");
localStorage.setItem("lastTabOpenByUser", "usecases");
}}
sx={{
style={{
width: "100%",
height: 35,
color: "#C8C8C8",
justifyContent: "flex-start",
textTransform: "none",
backgroundColor: currentOpenTab === "usecases" && expandLeftNav && currentPath.includes("/usecases2")? "#2f2f2f": "transparent",
"&:hover": { backgroundColor: "#2f2f2f" },
backgroundColor: currentOpenTab === "usecases" && expandLeftNav && currentPath.includes("/usecases")? "#2f2f2f": "transparent",
marginLeft: 16
}}
onMouseOver={(event)=>{
event.currentTarget.style.backgroundColor = "#2f2f2f";
}}
onMouseOut={(event)=>{
event.currentTarget.style.backgroundColor = currentOpenTab === "usecases" && expandLeftNav && currentPath.includes("/usecases")? "#2f2f2f": "transparent";
}}
disableRipple={expandLeftNav ? false : true}
>
@@ -985,22 +1059,28 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
</span>
</Button>
</Link>
<Link to="/workflows2" style={hrefStyle}>
<Link to="/workflows" style={hrefStyle}>
<Button
onClick={(event) => {
setCurrentOpenTab("workflows");
localStorage.setItem("lastTabOpenByUser", "workflows");
}}
sx={{
style={{
width: "100%",
height: 35,
color: "#C8C8C8",
justifyContent: "flex-start",
textTransform: "none",
backgroundColor: currentOpenTab === "workflows" && currentPath === "/workflows" && expandLeftNav? "#2f2f2f": "transparent",
"&:hover": { backgroundColor: "#2f2f2f" },
marginLeft: 16
}}
disableRipple={expandLeftNav ? false : true}
onMouseOver={(event)=>{
event.currentTarget.style.backgroundColor = "#2f2f2f";
}}
onMouseOut={(event)=>{
event.currentTarget.style.backgroundColor = currentOpenTab === "workflows" && currentPath === "/workflows" && expandLeftNav? "#2f2f2f": "transparent";
}}
>
<span style={{display: expandLeftNav ? "inline" : "none", opacity: expandLeftNav ? 1 : 0, transition: "opacity 0.3s ease", position: 'relative', left: !expandLeftNav ? 10: 0, marginRight: 10, fontSize: 16 }}></span>{" "}
<span
@@ -1015,20 +1095,26 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
</span>
</Button>
</Link>
<Link to="/apps2" style={hrefStyle}>
<Link to="/apps" style={hrefStyle}>
<Button
onClick={(event) => {
setCurrentOpenTab("apps");
localStorage.setItem("lastTabOpenByUser", "apps");
}}
sx={{
style={{
width: "100%",
height: 35,
color: "#C8C8C8",
justifyContent: "flex-start",
textTransform: "none",
backgroundColor: currentOpenTab === "apps" && expandLeftNav && currentPath.includes("/search") ? "#2f2f2f": "transparent",
"&:hover": { backgroundColor: "#2f2f2f" },
backgroundColor: currentOpenTab === "apps" && expandLeftNav && currentPath.includes("/apps2") ? "#2f2f2f": "transparent",
marginLeft: 16
}}
onMouseOver={(event)=>{
event.currentTarget.style.backgroundColor = "#2f2f2f";
}}
onMouseOut={(event)=>{
event.currentTarget.style.backgroundColor = currentOpenTab === "apps" && expandLeftNav && currentPath.includes("/search") ? "#2f2f2f": "transparent";
}}
disableRipple={expandLeftNav ? false : true}
>
@@ -1038,7 +1124,7 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
display: expandLeftNav ? "inline" : "none",
opacity: expandLeftNav ? 1 : 0,
transition: "opacity 0.3s ease",
color: currentOpenTab === "apps" && currentPath.includes("/search") ? "#F1F1F1" : "#C8C8C8",
color: currentOpenTab === "apps" && currentPath.includes("/apps2") ? "#F1F1F1" : "#C8C8C8",
}}
>
Apps
@@ -1051,7 +1137,7 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
sx={{
display: "flex",
flexDirection: "row",
marginTop: 2.5,
marginTop: 1
}}
>
<span style={{ display: "inline-block", width: "100%" }}>
@@ -1072,7 +1158,7 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
setCurrentOpenTab("security");
localStorage.setItem("lastTabOpenByUser", "security");
}}
sx={{
style={{
...ButtonStyle,
backgroundColor:
((currentOpenTab === "security" && currentPath.includes("/security")) ||
@@ -1080,11 +1166,19 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
(currentPath.includes("detections") || currentPath.includes("response"))))
? "#2f2f2f"
: "transparent",
"&:hover": {
backgroundColor: userdata?.support ? "#2f2f2f" : "transparent",
},
cursor: userdata?.support ? "pointer" : "not-allowed",
}}
onMouseOver={(event)=>{
event.currentTarget.style.backgroundColor = "#2f2f2f";
}}
onMouseOut={(event)=>{
event.currentTarget.style.backgroundColor = ((currentOpenTab === "security" && currentPath.includes("/security")) ||
(!expandLeftNav &&
(currentPath.includes("detections") || currentPath.includes("response"))))
? "#2f2f2f"
: "transparent";
}}
disabled={!userdata?.support}
>
<ShieldOutlinedIcon
style={{
@@ -1105,19 +1199,25 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
: "#6F6F6F",
}}
>
Security
Content
</span>
</Button>
</Link>
</span>
<IconButton
disabled={true}
onClick={() => {
setOpenSecurityTab((prev) => !prev);
setOpenautomateTab(false);
}}
sx={{
style={{
marginLeft: 0.625,
color: "#FFFFFF",
}}
onMouseOver={(event)=>{
event.currentTarget.style.backgroundColor = "#2f2f2f";
}}
onMouseOut={(event)=>{
event.currentTarget.style.backgroundColor = "transparent";
}}
>
{openSecurityTab ? (
@@ -1144,14 +1244,12 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
display: "flex",
flexDirection: "column",
paddingLeft: 16,
gap: 4,
marginTop: expandLeftNav ? 16 : 0,
}}
disableRipple={expandLeftNav ? false : true}
>
<span style={{ display: "inline-block", width: "100%" }}>
<Link
to={userdata?.support ? "/detections" : "#"}
to={"/forms"}
style={{
...hrefStyle,
pointerEvents: userdata?.support ? "auto" : "none",
@@ -1196,14 +1294,14 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
: "#6F6F6F",
}}
>
Detection
Forms
</span>
</Button>
</Link>
</span>
<span style={{ display: "inline-block", width: "100%" }}>
<span style={{ display: "inline-block", width: "100%" }}>
<Link
to={userdata?.support ? "/response" : "#"}
to={"/admin?tab=datastore"}
style={{
...hrefStyle,
pointerEvents: userdata?.support ? "auto" : "none",
@@ -1230,7 +1328,6 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
},
cursor: userdata?.support ? "pointer" : "not-allowed",
}}
disabled={!userdata?.support}
>
<span style={{ position: "relative", left: !expandLeftNav ? 10 : 0, marginRight: 10, fontSize: 16 }}>
@@ -1248,26 +1345,81 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
: "#6F6F6F",
}}
>
Response
Datastore
</span>
</Button>
</Link>
</span>
<span style={{ display: "inline-block", width: "100%" }}>
<Link
to={"/admin?tab=files"}
style={{
...hrefStyle,
pointerEvents: userdata?.support ? "auto" : "none",
}}
>
<Button
onClick={(event) => {
if (!userdata?.support) return;
setCurrentOpenTab("response");
localStorage.setItem("lastTabOpenByUser", "response");
}}
sx={{
width: "100%",
height: 35,
color: userdata?.support ? "#C8C8C8" : "#6F6F6F",
justifyContent: "flex-start",
textTransform: "none",
backgroundColor:
currentOpenTab === "response" && currentPath.includes("/response")
? "#2f2f2f"
: "transparent",
"&:hover": {
backgroundColor: userdata?.support ? "#2f2f2f" : "transparent",
},
cursor: userdata?.support ? "pointer" : "not-allowed",
}}
>
<span style={{ position: "relative", left: !expandLeftNav ? 10 : 0, marginRight: 10, fontSize: 16 }}>
</span>
<span
style={{
display: expandLeftNav ? "inline" : "none",
opacity: expandLeftNav ? 1 : 0,
transition: "opacity 0.3s ease",
color:
userdata?.support && currentOpenTab === "response" && currentPath.includes("/response")
? "#F1F1F1"
: userdata?.support
? "#C8C8C8"
: "#6F6F6F",
}}
>
Files
</span>
</Button>
</Link>
</span>
</Box>
</Collapse>
<Link to="/docs" style={hrefStyle}>
<Button
onClick={(event) => {
setCurrentOpenTab("docs");
localStorage.setItem("lastTabOpenByUser", "docs");
}}
sx={{
style={{
...ButtonStyle,
marginTop: 2,
marginTop: 8,
marginTop: 8,
backgroundColor: currentOpenTab === "docs" && currentPath.includes("/docs") ? "#2f2f2f": "transparent",
"&:hover": { backgroundColor: "#2f2f2f" },
}}
onMouseOver={(event)=>{
event.currentTarget.style.backgroundColor = "#2f2f2f";
}}
onMouseOut={(event)=>{
event.currentTarget.style.backgroundColor = currentOpenTab === "docs" && currentPath.includes("/docs") ? "#2f2f2f": "transparent";
}}
>
<img
@@ -1385,15 +1537,20 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
>
{
isCloud ? (
<span
<span
style={{
color: "#bbb",
fontSize: "16px",
marginRight: 12,
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
<img src={getRegionFlag(option.region_url)} alt={option.region_url} style={{ width: 24, height: 24, marginRight: 12, borderRadius: "50%" }} />
{option.region_url}
</span>) : null
</span>
) : null
}
<img
src={option.image ? option.image : "/images/no_image.png"}
+1 -1
View File
@@ -127,7 +127,7 @@ const MFASetup = ({ isLoaded, globalUrl, setCookie }) => {
};
return (
<div style={{ margin: "50px auto", width: "500px" }}>
<div style={{ paddingTop: 50, margin: "0px auto", width: "500px" }}>
<Paper elevation={3} style={{ padding: "30px", backgroundColor: "#212121" }}>
<Typography variant="h5" style={{ color: "white", marginBottom: 10, textAlign: "center" }}>
Multi-Factor Authentication Setup
+24 -2
View File
@@ -110,8 +110,30 @@ const OrgHeader = (props) => {
orgDescription,
selectedOrganization.id,
selectedOrganization.image,
{},
{},
{
app_download_repo: selectedOrganization?.defaults?.app_download_repo,
app_download_branch: selectedOrganization?.defaults?.app_download_branch,
workflow_download_repo: selectedOrganization?.defaults?.workflow_download_repo,
workflow_download_branch: selectedOrganization?.defaults?.workflow_download_branch,
notification_workflow: selectedOrganization?.defaults?.notification_workflow,
documentation_reference: selectedOrganization?.defaults?.documentation_reference,
workflow_upload_repo: selectedOrganization?.defaults?.workflow_upload_repo,
workflow_upload_branch: selectedOrganization?.defaults?.workflow_upload_branch,
workflow_upload_username: selectedOrganization?.defaults?.workflow_upload_username,
workflow_upload_token: selectedOrganization?.defaults?.workflow_upload_token,
newsletter: selectedOrganization?.defaults?.newsletter,
weekly_recommendations: selectedOrganization?.defaults?.weekly_recommendations,
},
{
sso_entrypoint: selectedOrganization?.sso_config?.sso_entrypoint,
sso_certificate: selectedOrganization?.sso_config?.sso_certificate,
client_id: selectedOrganization?.sso_config?.client_id,
client_secret: selectedOrganization?.sso_config?.client_secret,
openid_authorization: selectedOrganization?.sso_config?.openid_authorization,
openid_token: selectedOrganization?.sso_config?.openid_token,
SSORequired: selectedOrganization?.sso_config?.SSORequired,
auto_provision: selectedOrganization?.sso_config?.auto_provision,
},
[],
)
}
@@ -227,7 +227,7 @@ const OrgHeaderexpanded = (props) => {
defaults,
sso_config
) => {
console.log("defatult in handleEditOrg", defaults)
const data = {
name: name,
description: description,
@@ -309,6 +309,7 @@ const OrgHeaderexpanded = (props) => {
openid_authorization: openidAuthorization,
openid_token: openidToken,
SSORequired: SSORequired,
auto_provision: selectedOrganization?.sso_config?.auto_provision,
}
)
}
@@ -485,6 +486,7 @@ const OrgHeaderexpanded = (props) => {
value={data}
onClick={(e) => {
var parsedinput = { target: { value: data } }
console.log("Parsed input: ", parsedinput)
handleWorkflowSelectionUpdate(parsedinput)
}}
>
@@ -753,7 +755,7 @@ const OrgHeaderexpanded = (props) => {
{SSORequired ? "Required" : "Optional"}
</div>
</div>
<div
{/* <div
style={{
display: "flex",
flexDirection: "column",
@@ -800,7 +802,7 @@ const OrgHeaderexpanded = (props) => {
</Button>
</span>
</Tooltip>
</div>
</div> */}
<div></div>
<Grid item xs={12} style={{}}>
<Typography variant="h6" style={{ textAlign: "center" }}>
+34 -31
View File
@@ -53,6 +53,7 @@ import {
import {
HelpOutline as HelpOutlineIcon,
OpenInFull as OpenInFullIcon,
Description as DescriptionIcon,
GetApp as GetAppIcon,
Search as SearchIcon,
@@ -2090,7 +2091,7 @@ const ParsedAction = (props) => {
{data?.validation?.valid === true ?
<Tooltip title="Authentication has been validated" placement="top">
<Chip
style={{marginLeft: 0, padding: 0, marginRight: 10, cursor: "pointer", borderColor: green, }}
style={{marginLeft: 0, padding: 0, marginRight: 10, cursor: "pointer", borderColor: green, maxHeight: 25, }}
label={"Valid"}
variant="outlined"
color="secondary"
@@ -2099,7 +2100,7 @@ const ParsedAction = (props) => {
: null }
{data?.last_modified === true ?
<Chip
style={{marginLeft: 0, padding: 0, marginRight: 10, cursor: "pointer",}}
style={{marginLeft: 0, padding: 0, marginRight: 10, cursor: "pointer", maxHeight: 25, }}
label={"Latest"}
variant="outlined"
color="secondary"
@@ -2139,7 +2140,7 @@ const ParsedAction = (props) => {
>
<IconButton
color="primary"
variant="outlined"
variant="outlined"
style={{}}
onClick={() => {
setAuthenticationModalOpen(true);
@@ -3452,36 +3453,10 @@ const ParsedAction = (props) => {
disableUnderline: true,
endAdornment: hideExtraTypes ? null : (
<InputAdornment position="end">
<ButtonGroup orientation={multiline ? "vertical" : "horizontal"}>
<Tooltip title="Expand window" placement="top">
<AspectRatioIcon
style={{ cursor: "pointer", margin: multiline ? 5 : 0 ,}}
onClick={(event) => {
event.preventDefault()
setFieldCount(count)
setExpansionModalOpen(true)
setActiveDialog("codeeditor")
//setcodedata(data.value)
var parsedvalue = data.value
if (parsedvalue === undefined || parsedvalue === null) {
parsedvalue = ""
}
setEditorData({
"name": data.name,
"value": parsedvalue,
"field_number": count,
"actionlist": actionlist,
"field_id": clickedFieldId,
"example": selectedActionParameters[count].example,
})
}}
/>
</Tooltip>
<ButtonGroup color="secondary" orientation={multiline ? "vertical" : "horizontal"}>
<Tooltip title="Autocomplete text" placement="bottom">
<AddCircleOutlineIcon
style={{ cursor: "pointer", margin: multiline ? 5 : 0, }}
style={{ color: "rgba(255,255,255,0.7)", cursor: "pointer", margin: multiline ? 5 : 0, }}
onClick={(event) => {
event.preventDefault()
@@ -4355,6 +4330,34 @@ const ParsedAction = (props) => {
{tmpitem} <span style={{color: theme.palette.main}}>{selectedActionParameters[count].required || selectedActionParameters[count].configuration ? "*" : ""}</span>
</div>
<Tooltip title="Expand editor window" placement="top">
<OpenInFullIcon
style={{ color: "rgba(255,255,255,0.7)", cursor: "pointer", margin: multiline ? 5 : 0, height: 20, width: 20, }}
onClick={(event) => {
event.preventDefault()
setFieldCount(count)
setExpansionModalOpen(true)
setActiveDialog("codeeditor")
//setcodedata(data.value)
var parsedvalue = data.value
if (parsedvalue === undefined || parsedvalue === null) {
parsedvalue = ""
}
setEditorData({
"name": data.name,
"value": parsedvalue,
"field_number": count,
"actionlist": actionlist,
"field_id": clickedFieldId,
"example": selectedActionParameters[count].example,
})
}}
/>
</Tooltip>
</div>
{datafield}
{/*shufflecode*/}
+241 -200
View File
@@ -1,4 +1,4 @@
import React, { useState, useEffect } from "react";
import React, { useState, useEffect, useContext, memo } from "react";
import { toast } from "react-toastify";
import theme from "../theme.jsx";
@@ -13,22 +13,25 @@ import {
Card,
Chip,
Switch,
Skeleton,
} from "@mui/material";
import { Context } from "../context/ContextApi.jsx";
import { useNavigate, Link } from "react-router-dom";
import Priority from "../components/Priority.jsx";
import { constrainMatrix } from "reaviz";
//import { useAlert
const Priorities = (props) => {
const Priorities = memo((props) => {
const { globalUrl, userdata,clickedFromOrgTab, serverside, billingInfo, stripeKey, checkLogin, setAdminTab, setCurTab, notifications, setNotifications, } = props;
const [showDismissed, setShowDismissed] = React.useState(false);
const [showRead, setShowRead] = React.useState(false);
const [appFramework, setAppFramework] = React.useState({});
const [selectedWorkflow, setSelectedWorkflow] = React.useState("NO HIGHLIGHT");
const [selectedExecutionId, setSelectedExecutionId] = React.useState("NO HIGHLIGHT");
const [highlightKMS, setHighlightKMS] = React.useState(false)
let navigate = useNavigate();
useEffect(() => {
getFramework()
@@ -216,188 +219,16 @@ const Priorities = (props) => {
const notificationWidth = "100%"
const imagesize = 22
const boxColor = "#86c142"
const NotificationItem = (props) => {
const {data} = props
var image = "";
var orgName = "";
var orgId = "";
var highlighted = selectedExecutionId === "" && selectedWorkflow === "" ? false : data.reference_url === undefined || data.reference_url === null || data.reference_url.length === 0 ? false : data.reference_url.includes(selectedExecutionId) || data.reference_url.includes(selectedWorkflow)
if (!highlighted && highlightKMS) {
if (data.title !== undefined && data.title !== null && data.title.toLowerCase().includes("kms")) {
highlighted = true
} else if (data.description !== undefined && data.description !== null && data.description.toLowerCase().includes("kms")) {
highlighted = true
}
}
if (userdata.orgs !== undefined) {
const foundOrg = userdata.orgs.find((org) => org.id === data["org_id"]);
if (foundOrg !== undefined && foundOrg !== null) {
//position: "absolute", bottom: 5, right: -5,
const imageStyle = {
width: imagesize,
height: imagesize,
pointerEvents: "none",
marginLeft:
data.creator_org !== undefined && data.creator_org.length > 0
? 20
: 0,
borderRadius: 10,
border:
foundOrg.id === userdata.active_org.id
? `3px solid ${boxColor}`
: null,
cursor: "pointer",
marginRight: 10,
};
image =
foundOrg.image === "" ? (
<img
alt={foundOrg.name}
src={theme.palette.defaultImage}
style={imageStyle}
/>
) : (
<img
alt={foundOrg.name}
src={foundOrg.image}
style={imageStyle}
onClick={() => {}}
/>
);
orgName = foundOrg.name;
orgId = foundOrg.id;
}
}
return (
<Paper
style={{
backgroundColor: theme.palette.platformColor,
width: clickedFromOrgTab ? null :notificationWidth,
padding: 30,
borderBottom: "1px solid rgba(255,255,255,0.4)",
marginBottom: 20,
border: highlighted ? "2px solid #f85a3e" : null,
borderRadius: theme.palette?.borderRadius,
}}
>
<div style={{display: "flex", }}>
{data.amount === 1 && data.read === false ?
<Chip
label={"First seen"}
variant="contained"
color="primary"
style={{marginRight: 15, height: 25, }}
/>
: null}
{data.ignored === true ?
<Chip
label={"Disabled"}
variant="outlined"
color="primary"
style={{marginRight: 15, height: 25, }}
/>
: null}
{data.read === false ?
<Chip
label={"Unread"}
variant="outlined"
color="primary"
style={{marginRight: 15, height: 25, }}
/>
:
<Chip
label={"Read"}
variant="outlined"
color="secondary"
style={{marginRight: 15, height: 25, }}
/>
}
<Typography variant="body1" color="textPrimary">
{data.title}
</Typography >
</div>
{data.image !== undefined && data.image !== null && data.image.length > 0 ?
<img alt={data.title} src={data.image} style={{height: 100, width: 100, }} />
:
null
}
<Typography variant="body2" color="textSecondary" style={{marginTop: 10, maxHeight: 200, overflowX: "hidden", overflowY: "auto", }}>
{data.description}
</Typography >
<div style={{ display: "flex" }}>
<ButtonGroup>
<Button
color="secondary"
variant="outlined"
style={{ marginTop: 15 }}
disabled={data.reference_url === undefined || data.reference_url === null || data.reference_url.length === 0}
onClick={() => {
window.open(data.reference_url, "_blank")
}}
>
Explore
</Button>
{data.read === false ? (
<Button
color="secondary"
variant="outlined"
style={{ marginTop: 15 }}
onClick={() => {
dismissNotification(data.id);
}}
>
Dismiss
</Button>
) : null}
<Tooltip title="Disabling a notification makes it so similar notifications to this one will NOT be re-opened. It will NOT forward notifications to your notification workflow, but WILL still keep counting." placement="top">
<Button
color="secondary"
variant={data.ignored === true ? "contained" : "outlined"}
style={{ marginTop: 15, }}
onClick={() => {
if (data.ignored === true) {
dismissNotification(data.id, false)
} else {
dismissNotification(data.id, true)
}
}}
>
{data.ignored === true ? "Re-enable" : "Disable"}
</Button>
</Tooltip>
</ButtonGroup>
<Typography variant="body2" color="textSecondary" style={{marginLeft: 20, marginTop: 20, }}>
<b>First seen</b>: {new Date(data.created_at * 1000).toISOString().slice(0, 19)}
</Typography >
<Typography variant="body2" color="textSecondary" style={{marginLeft: 20, marginTop: 20, }}>
<b>Last seen</b>: {new Date(data.updated_at * 1000).toISOString().slice(0, 19)}
</Typography >
<Typography variant="body2" color="textSecondary" style={{marginLeft: 20, marginTop: 20, }}>
<b>Times seen</b>: {data.amount}
</Typography >
</div>
</Paper>
);
}
return (
<div style={{width: clickedFromOrgTab ? 1030:1000, padding: clickedFromOrgTab ? 27:null, height: clickedFromOrgTab ? "auto":null, backgroundColor: clickedFromOrgTab ? '#212121':null, borderRadius: clickedFromOrgTab ? '16px':null, }}>
<h2 style={{ display: clickedFromOrgTab?null:"inline", marginBottom: clickedFromOrgTab? 8:null, marginTop: clickedFromOrgTab?40:null, color: clickedFromOrgTab?"#ffffff":null }}>Notifications ({
<div style={{width: "100%", height: "100%", boxSizing: 'border-box', transition: 'width 0.3s ease', padding: clickedFromOrgTab ? "27px 10px 19px 27px":null, height: clickedFromOrgTab ? "auto":null, minHeight: 843, backgroundColor: clickedFromOrgTab ? '#212121':null, borderRadius: clickedFromOrgTab ? '16px':null, }}>
<div style={{ maxHeight: 1700, overflowY: "auto", width: '100%', scrollbarColor: '#494949 transparent', scrollbarWidth: 'thin'}}>
<div style={{maxWidth: "calc(100% - 20px)"}}>
<Typography style={{ fontSize: 24, fontWeight: 'bold', display: clickedFromOrgTab?null:"inline", marginBottom: clickedFromOrgTab? 8:null, color: clickedFromOrgTab?"#ffffff":null }}>Notifications ({
notifications?.filter((notification) => showRead === true || notification.read === false).length
})</h2>
})</Typography>
<span style={{ marginLeft: clickedFromOrgTab?null:25, color: clickedFromOrgTab?"#9E9E9E":null, }}>
<span style={{ fontSize: 16, marginLeft: clickedFromOrgTab?null:25, color: clickedFromOrgTab?"#9E9E9E":null, }}>
Notifications help you find potential problems with your workflows and apps.&nbsp;
<a
target="_blank"
@@ -430,24 +261,11 @@ const Priorities = (props) => {
</Button>
) : null}
</div>
{notifications === null || notifications === undefined || notifications.length === 0 ? null :
<div>
{notifications.map((notification, index) => {
if (showRead === false && notification.read === true) {
return null
}
return (
<NotificationItem data={notification} key={index} />
)
})}
</div>
}
<NotificationComponent notifications={notifications} showRead={showRead} selectedExecutionId={selectedExecutionId} selectedWorkflow={selectedWorkflow} highlightKMS={highlightKMS} userdata={userdata} imagesize={imagesize} boxColor={boxColor} clickedFromOrgTab={clickedFromOrgTab} notificationWidth={notificationWidth} dismissNotification={dismissNotification}/>
{clickedFromOrgTab? null : <Divider style={{marginTop: 50, marginBottom: 50, }} />}
<h2 style={{ display: clickedFromOrgTab ? null:"inline", marginBottom: clickedFromOrgTab ? 8:null, marginTop: clickedFromOrgTab ?0:null, color: clickedFromOrgTab ? "#ffffff" : null }}>Suggestions</h2>
<span style={{ color: clickedFromOrgTab ?"#9E9E9E":null,marginLeft: clickedFromOrgTab ?null:25 }}>
<h2 style={{ display: clickedFromOrgTab ? null:"inline", marginBottom: clickedFromOrgTab ? 8:null, marginTop: clickedFromOrgTab ? 30 :null, color: clickedFromOrgTab ? "#ffffff" : null }}>Suggestions</h2>
<span style={{ fontSize: 16, color: clickedFromOrgTab ?"#9E9E9E":null,marginLeft: clickedFromOrgTab ?null:25 }}>
Suggestions are tasks identified by Shuffle to help you discover ways to protect your and customers' company. <br/>These range from simple configurations in Shuffle to Usecases you may have missed.&nbsp;
<a
target="_blank"
@@ -489,9 +307,232 @@ const Priorities = (props) => {
)
})
}
</div>
</div>
</div>
)
}
})
export default Priorities;
const NotificationItem = memo((props) => {
const {data, selectedExecutionId, selectedWorkflow, highlightKMS, userdata, imagesize, boxColor, clickedFromOrgTab, notificationWidth, dismissNotification} = props
var image = "";
var orgName = "";
var orgId = "";
var highlighted = selectedExecutionId === "" && selectedWorkflow === "" ? false : data.reference_url === undefined || data.reference_url === null || data.reference_url.length === 0 ? false : data.reference_url.includes(selectedExecutionId) || data.reference_url.includes(selectedWorkflow)
if (!highlighted && highlightKMS) {
if (data.title !== undefined && data.title !== null && data.title.toLowerCase().includes("kms")) {
highlighted = true
} else if (data.description !== undefined && data.description !== null && data.description.toLowerCase().includes("kms")) {
highlighted = true
}
}
if (userdata.orgs !== undefined) {
const foundOrg = userdata.orgs.find((org) => org.id === data["org_id"]);
if (foundOrg !== undefined && foundOrg !== null) {
//position: "absolute", bottom: 5, right: -5,
const imageStyle = {
width: imagesize,
height: imagesize,
pointerEvents: "none",
marginLeft:
data.creator_org !== undefined && data.creator_org.length > 0
? 20
: 0,
borderRadius: 10,
border:
foundOrg.id === userdata.active_org.id
? `3px solid ${boxColor}`
: null,
cursor: "pointer",
marginRight: 10,
};
image =
foundOrg.image === "" ? (
<img
alt={foundOrg.name}
src={theme.palette.defaultImage}
style={imageStyle}
/>
) : (
<img
alt={foundOrg.name}
src={foundOrg.image}
style={imageStyle}
onClick={() => {}}
/>
);
orgName = foundOrg.name;
orgId = foundOrg.id;
}
}
return (
<Paper
style={{
backgroundColor: theme.palette.inputColor.backgroundColor,
width: clickedFromOrgTab ? null :notificationWidth,
padding: 30,
borderBottom: "1px solid rgba(255,255,255,0.4)",
marginBottom: 20,
border: highlighted ? "2px solid #f85a3e" : null,
borderRadius: theme.palette?.borderRadius,
}}
>
<div style={{display: "flex", }}>
{data.amount === 1 && data.read === false ?
<Chip
label={"First seen"}
variant="contained"
color="primary"
style={{marginRight: 15, height: 25, }}
/>
: null}
{data.ignored === true ?
<Chip
label={"Disabled"}
variant="outlined"
color="primary"
style={{marginRight: 15, height: 25, }}
/>
: null}
{data.read === false ?
<Chip
label={"Unread"}
variant="outlined"
color="primary"
style={{marginRight: 15, height: 25, }}
/>
:
<Chip
label={"Read"}
variant="outlined"
color="secondary"
style={{marginRight: 15, height: 25, }}
/>
}
<Typography variant="body1" color="textPrimary" style={{ wordWrap: "break-word", overflow: "hidden", textOverflow: "ellipsis" }}>
{data.title}
</Typography >
</div>
{data.image !== undefined && data.image !== null && data.image.length > 0 ?
<img alt={data.title} src={data.image} style={{height: 100, width: 100, }} />
:
null
}
<Typography variant="body2" color="textSecondary" style={{ marginTop: 10, maxHeight: 200, overflowX: "hidden", overflowY: "auto", wordWrap: "break-word" }}>
{data.description}
</Typography >
<div style={{ display: "flex" }}>
<ButtonGroup>
<Button
color="secondary"
variant="outlined"
style={{ marginTop: 15 }}
disabled={data.reference_url === undefined || data.reference_url === null || data.reference_url.length === 0}
onClick={() => {
window.open(data.reference_url, "_blank")
}}
>
Explore
</Button>
{data.read === false ? (
<Button
color="secondary"
variant="outlined"
style={{ marginTop: 15 }}
onClick={() => {
dismissNotification(data.id);
}}
>
Dismiss
</Button>
) : null}
<Tooltip title="Disabling a notification makes it so similar notifications to this one will NOT be re-opened. It will NOT forward notifications to your notification workflow, but WILL still keep counting." placement="top">
<Button
color="secondary"
variant={data.ignored === true ? "contained" : "outlined"}
style={{ marginTop: 15, }}
onClick={() => {
if (data.ignored === true) {
dismissNotification(data.id, false)
} else {
dismissNotification(data.id, true)
}
}}
>
{data.ignored === true ? "Re-enable" : "Disable"}
</Button>
</Tooltip>
</ButtonGroup>
<Typography variant="body2" color="textSecondary" style={{ marginLeft: 20, marginTop: 20, wordWrap: "break-word", overflow: "hidden", textOverflow: "ellipsis" }}>
<b>First seen</b>: {new Date(data.created_at * 1000).toISOString().slice(0, 19)}
</Typography >
<Typography variant="body2" color="textSecondary" style={{ marginLeft: 20, marginTop: 20, wordWrap: "break-word", overflow: "hidden", textOverflow: "ellipsis" }}>
<b>Last seen</b>: {new Date(data.updated_at * 1000).toISOString().slice(0, 19)}
</Typography >
<Typography variant="body2" color="textSecondary" style={{ marginLeft: 20, marginTop: 20, wordWrap: "break-word", overflow: "hidden", textOverflow: "ellipsis" }}>
<b>Times seen</b>: {data.amount}
</Typography >
</div>
</Paper>
);
})
const NotificationComponent = memo(({notifications, showRead, selectedExecutionId, selectedWorkflow, highlightKMS, userdata, imagesize, boxColor, clickedFromOrgTab, notificationWidth, dismissNotification}) => {
return(
<div>
{notifications === null || notifications === undefined || notifications?.length === 0 ? (
null
) :
<div>
{notifications?.map((notification, index) => {
if (showRead === false && notification.read === true) {
return null
}
return (
<NotificationItem data={notification} key={index} selectedExecutionId={selectedExecutionId} selectedWorkflow={selectedWorkflow} highlightKMS={highlightKMS} userdata={userdata} imagesize={imagesize} boxColor={boxColor} clickedFromOrgTab={clickedFromOrgTab} notificationWidth={notificationWidth} dismissNotification={dismissNotification} />
)
})}
</div>
}
</div>
)
})
// const PaddingWrapper = memo(({children, clickedFromOrgTab}) => {
// const { leftSideBarOpenByClick } = useContext(Context)
// return(
// <div style={{width: leftSideBarOpenByClick ? 950 : 1030,transition: 'width 0.3s ease', padding: clickedFromOrgTab ? "27px 10px 19px 27px":null, height: clickedFromOrgTab ? "auto":null, minHeight: 843, backgroundColor: clickedFromOrgTab ? '#212121':null, borderRadius: clickedFromOrgTab ? '16px':null, }}>
// {children}
// </div>
// )
// })
// const Wrapper = memo(({children, clickedFromOrgTab}) => {
// return(
// <PaddingWrapper clickedFromOrgTab={clickedFromOrgTab}>
// {children}
// </PaddingWrapper>
// )
// })
@@ -1084,6 +1084,8 @@ const CodeEditor = (props) => {
maxHeight: isMobile ? "100%" : 700,
border: theme.palette.defaultBorder,
padding: isMobile ? "25px 10px 25px 10px" : 25,
zoom: 0.8,
backgroundColor: "black",
},
}}
>
+1 -1
View File
@@ -382,7 +382,7 @@ const data = [
css: {
"background-color": "#f85a3e",
"border-color": "#f85a3e",
"border-width": "12px",
"border-width": "7px",
"transition-property": "border-width",
"transition-duration": "0.25s",
label: "data(label)",
+6
View File
@@ -50,6 +50,12 @@ const theme = createTheme(adaptV4Theme({
borderRadius: 5,
height: 40,
},
DialogStyle: {
backgroundColor: "#212121",
borderRadius: 2,
boxShadow: "0px 0px 10px 0px rgba(0,0,0,0.75)",
border: "1px solid #494949",
},
innerTextfieldStyle: {
height: 40,
fontSize: 16,
+109 -14
View File
@@ -4,13 +4,13 @@ import { toast } from "react-toastify";
const Admin2 = (props) => {
// Destructure props if needed
const { userdata, globalUrl, isCloud, serverside, checkLogin, notifications, setNotifications, stripeKey } = props;
const { userdata, globalUrl, serverside, checkLogin, notifications, setNotifications, stripeKey, isLoaded, isLoggedIn} = props;
const [selectedTab, setSelectedTab] = useState('editdetails');
const [selectedStatus, setSelectedStatus] = React.useState([]);
const [selectedOrganization, setSelectedOrganization] = useState({});
const [organizationFeatures, setOrganizationFeatures] = useState({});
const [orgRequest, setOrgRequest] = React.useState(true);
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
const handleGetOrg = (orgId) => {
if (
serverside !== true &&
@@ -51,8 +51,11 @@ const Admin2 = (props) => {
.then((responseJson) => {
if (responseJson["success"] === false) {
toast(
"Failed getting your org. If this persists, please contact support.",
"Failed getting your org. If this persists, please contact support. Redirecting to workflows...",
);
setTimeout(() => {
window.location.href = "/workflows";
}, 3000);
} else {
if (
responseJson.sync_features === undefined ||
@@ -151,6 +154,77 @@ const Admin2 = (props) => {
});
};
const urlSearchParams = new URLSearchParams(window.location.search);
const params = Object.fromEntries(urlSearchParams.entries());
const foundOrgID = params["org_id"]
useEffect(() => {
if(foundOrgID !== null && foundOrgID !== undefined && userdata?.support && foundOrgID?.length > 0) {
handleClickChangeOrg(foundOrgID)
}
}, [foundOrgID]);
const handleClickChangeOrg = (orgId) => {
// Don't really care about the logout
//name: org.name,
//orgId = "asd"
const data = {
org_id: orgId,
};
localStorage.setItem("globalUrl", "");
localStorage.setItem("getting_started_sidebar", "open");
fetch(`${globalUrl}/api/v1/orgs/${orgId}/change`, {
mode: "cors",
credentials: "include",
crossDomain: true,
method: "POST",
body: JSON.stringify(data),
withCredentials: true,
headers: {
"Content-Type": "application/json; charset=utf-8",
},
})
.then(function (response) {
if (response.status !== 200) {
console.log("Error in response");
} else {
localStorage.removeItem("apps")
localStorage.removeItem("workflows")
localStorage.removeItem("userinfo")
}
return response.json();
})
.then(function (responseJson) {
if (responseJson.success === true) {
if (responseJson.region_url !== undefined && responseJson.region_url !== null && responseJson.region_url.length > 0) {
localStorage.setItem("globalUrl", responseJson.region_url)
//globalUrl = responseJson.region_url
}
setTimeout(() => {
window.location.reload()
}, 3000);
toast("Successfully changed active organization - refreshing!");
} else {
if (responseJson.reason !== undefined && responseJson.reason !== null) {
if (!responseJson.reason.includes("already")) {
toast("Failed changing org: " + responseJson.reason);
}
} else {
toast("Failed changing org")
}
}
})
.catch((error) => {
console.log("error changing: ", error);
//removeCookie("session_token", {path: "/"})
});
};
const handleEditOrg = (
name,
description,
@@ -159,6 +233,7 @@ const Admin2 = (props) => {
defaults,
sso_config,
lead_info,
{ mfa_required } = {}
) => {
const data = {
name: name,
@@ -168,6 +243,7 @@ const Admin2 = (props) => {
defaults: defaults,
sso_config: sso_config,
lead_info: lead_info,
mfa_required: mfa_required !== undefined ? mfa_required : selectedOrganization?.mfa_required,
};
const url = globalUrl + `/api/v1/orgs/${selectedOrganization.id}`;
@@ -202,21 +278,40 @@ const Admin2 = (props) => {
});
};
// useEffect(() => {
// handleGetOrg();
// }, []);
const handleStatusChange = (event) => {
const { value } = event.target;
setSelectedStatus(value);
handleEditOrg(
"",
"",
selectedOrganization?.name,
selectedOrganization?.description,
selectedOrganization.id,
"",
{},
{},
selectedOrganization?.image,
{
app_download_repo: selectedOrganization?.defaults?.app_download_repo,
app_download_branch: selectedOrganization?.defaults?.app_download_branch,
workflow_download_repo: selectedOrganization?.defaults?.workflow_download_repo,
workflow_download_branch: selectedOrganization?.defaults?.workflow_download_branch,
notification_workflow: selectedOrganization?.defaults?.notification_workflow,
documentation_reference: selectedOrganization?.defaults?.documentation_reference,
workflow_upload_repo: selectedOrganization?.defaults?.workflow_upload_repo,
workflow_upload_branch: selectedOrganization?.defaults?.workflow_upload_branch,
workflow_upload_username: selectedOrganization?.defaults?.workflow_upload_username,
workflow_upload_token: selectedOrganization?.defaults?.workflow_upload_token,
newsletter: !selectedOrganization?.defaults?.newsletter,
weekly_recommendations: selectedOrganization?.defaults?.weekly_recommendations,
},
{
sso_entrypoint: selectedOrganization?.sso_config?.sso_entrypoint,
sso_certificate: selectedOrganization?.sso_config?.sso_certificate,
client_id: selectedOrganization?.sso_config?.client_id,
client_secret: selectedOrganization?.sso_config?.client_secret,
openid_authorization: selectedOrganization?.sso_config?.openid_authorization,
openid_token: selectedOrganization?.sso_config?.openid_token,
SSORequired: selectedOrganization?.sso_config?.SSORequired,
auto_provision: selectedOrganization?.sso_config?.auto_provision,
},
value.length === 0 ? ["none"] : value,
);
};
@@ -228,14 +323,14 @@ const Admin2 = (props) => {
orgRequest
) {
const orgId = userdata.active_org.id
console.log("orgID", orgId)
setOrgRequest(false);
handleGetOrg(orgId);
}
return (
<div style={{ display: 'flex', justifyContent: 'center', paddingTop: 30 }}>
<AdminNavBar userdata={userdata} 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} serverside={serverside} />
<div style={{ display: 'flex', justifyContent: 'center', paddingTop: 29, zoom: 0.8, }}>
<AdminNavBar userdata={userdata} isLoaded={isLoaded} 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} serverside={serverside} />
</div>
);
};
+112 -67
View File
@@ -422,7 +422,7 @@ const AngularWorkflow = (defaultprops) => {
const [subworkflow, setSubworkflow] = React.useState({});
const [subworkflowStartnode, setSubworkflowStartnode] = React.useState("");
const [leftViewOpen, setLeftViewOpen] = React.useState(isMobile ? false : true);
const [leftBarSize, setLeftBarSize] = React.useState(isMobile ? 0 : 255)
const [leftBarSize, setLeftBarSize] = React.useState(isMobile ? 0 : 235)
const [creatorProfile, setCreatorProfile] = React.useState({});
const [usecases, setUsecases] = React.useState([]);
const [files, setFiles] = React.useState({
@@ -3813,7 +3813,7 @@ const releaseToConnectLabel = "Release to Connect"
});
}
cy.fit(null, 100);
cy.fit(null, 400);
cy.on("add", "node", (e) => onNodeAdded(e));
cy.on("add", "edge", (e) => onEdgeAdded(e));
} else {
@@ -4052,7 +4052,7 @@ const releaseToConnectLabel = "Release to Connect"
.then(() => {
console.log("DONE: ", workflow_id);
getWorkflow(workflow_id.value, nodedata);
cy.fit(null, 50);
cy.fit(null, 300);
});
}
};
@@ -4180,7 +4180,7 @@ const releaseToConnectLabel = "Release to Connect"
if (nodedata.app_name === "Webhook" || nodedata.app_name === "Schedule" || nodedata.app_name === "Gmail" || nodedata.app_name === "Office365") {
if (!found) {
console.log("Find amount of executions for the specific nodetype: ", nodedata.app_name, "Executions: ", workflowExecutions)
//console.log("Find amount of executions for the specific nodetype: ", nodedata.app_name, "Executions: ", workflowExecutions)
// Find how many executions it has
var executions = 0
const matchingExecutions = workflowExecutions.filter((execution => execution.execution_source === nodedata.app_name.toLowerCase()))
@@ -6121,7 +6121,7 @@ const releaseToConnectLabel = "Release to Connect"
event.target.remove()
//console.log("Found branch already!")
toast.error("Triggers can have exactly one target node")
toast.error("Triggers can point to exactly one start node")
return
@@ -7782,7 +7782,7 @@ const releaseToConnectLabel = "Release to Connect"
const item = typeIds[idkey]
if (item.data.id === nodedata.id) {
//console.log("items: ", item.data.id, nodedata.id)
parsedStyle["border-width"] = "12px"
parsedStyle["border-width"] = "7px"
break
}
}
@@ -8391,7 +8391,7 @@ const releaseToConnectLabel = "Release to Connect"
// Reset view for cytoscape
if (cy !== undefined && cy !== null) {
cy.add(insertedNodes);
cy.fit(null, 200);
cy.fit(null, 400);
} else {
setElements(insertedNodes);
}
@@ -8772,7 +8772,7 @@ const releaseToConnectLabel = "Release to Connect"
}
// preview: true,
cy.fit(null, 200);
cy.fit(null, 400)
cy.on("boxselect", "node", (e) => {
if (e.target.data("isButton") || e.target.data("isDescriptor") || e.target.data("isSuggestion")) {
@@ -9380,7 +9380,9 @@ const releaseToConnectLabel = "Release to Connect"
onChange={handleSetTab}
aria-label="Left sidebar tab"
orientation={isMobile ? "vertical" : "horizontal"}
style={{}}
style={{
display: "flex",
}}
>
<Tab
value={0}
@@ -9421,7 +9423,7 @@ const releaseToConnectLabel = "Release to Connect"
<Grid item>
<FavoriteBorderIcon style={iconStyle} />
</Grid>
{isMobile ? null : <Grid item>Variables</Grid>}
{isMobile ? null : <Grid item>Vars</Grid>}
</Grid>
}
style={tabStyle}
@@ -9776,11 +9778,13 @@ const releaseToConnectLabel = "Release to Connect"
const barHeight = bodyHeight - appBarSize - 50;
const appScrollStyle = {
overflow: "scroll",
maxHeight: isMobile ? bodyHeight - appBarSize * 4 : barHeight-300,
minHeight: isMobile ? bodyHeight - appBarSize * 4 : barHeight-300,
maxHeight: isMobile ? bodyHeight - appBarSize * 4 : barHeight-70,
minHeight: isMobile ? bodyHeight - appBarSize * 4 : barHeight-70,
marginTop: 1,
overflowY: "auto",
overflowX: "hidden",
zoom: 0.9,
}
const handleAppDrag = (e, app) => {
@@ -10568,12 +10572,12 @@ const releaseToConnectLabel = "Release to Connect"
var viewedApps = []
return (
<div style={appViewStyle}>
<div style={{ flex: "1" }}>
<div style={{ flex: "1", zoom: 0.9, }}>
<TextField
style={{
backgroundColor: theme.palette.inputColor,
borderRadius: theme.palette?.borderRadius,
marginTop: 5,
marginTop: 10,
marginRight: 10,
height: 40,
}}
@@ -11193,14 +11197,12 @@ const releaseToConnectLabel = "Release to Connect"
overflowAnchor: "none",
};
const minSize = 370
const minSize = 370
var rightsidebarStyle = {
position: "fixed",
top: appBarSize - 35,
right: 25,
height: "90vh",
width: isMobile ? "100%" : minSize,
minWidth: minSize,
maxWidth: 600,
maxHeight: "100vh",
border: "1px solid rgb(91, 96, 100)",
@@ -11210,6 +11212,9 @@ const releaseToConnectLabel = "Release to Connect"
overflow: "auto",
overflowAnchor: "none",
minWidth: minSize,
width: isMobile ? "100%" : minSize,
zoom: 0.9,
};
const setTriggerFolderWrapperMulti = (event) => {
@@ -16153,13 +16158,15 @@ const releaseToConnectLabel = "Release to Connect"
position: "absolute",
top: isMobile ? 30 : 25,
left: isMobile ? 20 : leftSideBarOpenByClick ? leftBarSize + 275 : leftBarSize+100,
left: isMobile ? 20 : leftSideBarOpenByClick ? leftBarSize + 275 : leftBarSize+135,
transition: "left 0.3s ease",
maxWidth: 500,
zoom: 0.9,
}
const TopCytoscapeBar = (props) => {
const [hovered, setHovered] = useState(false)
if (workflow.public === true) {
return null
}
@@ -16174,12 +16181,30 @@ const releaseToConnectLabel = "Release to Connect"
<div style={topBarStyle}>
<div style={{
margin: "0px 10px 0px 35px",
maxWidth: 500,
}}>
<Typography variant="h6" style={{
margin: 0,
}}>
{workflow.name}
cursor: "pointer",
display: "flex",
borderRadius: theme.palette.borderRadius,
border: hovered ? "1px solid rgba(255,255,255,0.3)" : "1px solid transparent",
paddingRight: 10,
paddingLeft: 10,
position: "relative",
}}
onMouseEnter={() => {
setHovered(true)
}}
onMouseLeave={() => {
setHovered(false)
}}
onClick={() => {
setEditWorkflowModalOpen(true)
setLastSaved(false)
}}
>
<EditIcon style={{position: "absolute", top: 7, height: 20, width: 20, }} />
<span style={{marginLeft: 30, }}>{workflow.name}</span>
</Typography>
{workflowAsCode && (
<Tooltip title="Switch to Code View">
@@ -16204,7 +16229,7 @@ const releaseToConnectLabel = "Release to Connect"
{!distributedFromParent ?
isCorrectOrg ? null :
<Typography variant="body1">
<Typography variant="body2">
<b>Warning</b>: Change <span
style={{color: "#FF8544", cursor: "pointer", pointerEvents: "auto", }}
onClick={() => {
@@ -16275,7 +16300,7 @@ const releaseToConnectLabel = "Release to Connect"
>Active Organization</span> to edit this Workflow.
</Typography>
:
<Typography variant="body1">
<Typography variant="body2" color="textSecondary">
Warning: This workflow is controlled by your parent org and may not be editable.
</Typography>
}
@@ -16285,7 +16310,7 @@ const releaseToConnectLabel = "Release to Connect"
<InputLabel
id="suborg-changer"
style={{ color: "white" }}
style={{ color: "rgba(255,255,255,0.7)", }}
>
Select an Org
</InputLabel>
@@ -16294,10 +16319,15 @@ const releaseToConnectLabel = "Release to Connect"
pointerEvents: "auto",
backgroundColor: theme.palette.inputColor,
color: "white",
height: 50,
maxWidth: 250,
minWidth: 250,
borderRadius: theme.palette?.borderRadius,
height: 40,
}}
InputProps={{
style: {
height: 40,
}
}}
labelId="suborg-changer"
value={workflow.org_id}
@@ -16470,47 +16500,16 @@ const releaseToConnectLabel = "Release to Connect"
</Select>
</FormControl>
}
</div>
<div style={{display: "flex", marginLeft: 10, maxWidth: 250, pointerEvents: "auto", }}>
{parentWorkflows.slice(0,5).map((wf, index) => {
return (
<a href={`/workflows/${wf.id}`} target="_blank" rel="noopener noreferrer" key={index}>
<Tooltip arrow placement="left" title={
<span style={{}}>
{wf.image !== undefined && wf.image !== null && wf.image.length > 0 ?
<img
src={wf.image}
alt={wf.name}
style={{ backgroundColor: theme.palette.surfaceColor, maxHeight: 200, minHeigth: 200, borderRadius: theme.palette?.borderRadius, }}
/>
: null}
<Typography>
Parent workflow: '{wf.name}'
</Typography>
</span>
} placement="bottom">
<span onClick={() => {
console.log("Click: ", wf)
}}>
<img src={theme.palette.defaultImage} style={{height: 25, width: 25, cursor: "pointer", border: 15, marginRight: 5, marginTop: 5, filter: "grayscale(90%)", }} />
</span>
</Tooltip>
</a>
)
})}
</div>
</div>
{showEnvironment === true && environments.length > 1 && selectedActionEnvironment !== undefined && selectedActionEnvironment !== null && selectedActionEnvironment.Name !== undefined && selectedActionEnvironment.Name !== null ?
<FormControl fullWidth style={{marginTop: 15, marginleft: 10, pointerEvents: "auto", maxWidth: 250, }}>
<FormControl fullWidth style={{marginTop: 15, pointerEvents: "auto", maxWidth: 250, }}>
<InputLabel
id="execution_location"
style={{ color: "white" }}
style={{ color: "rgba(255,255,255,0.7)", marginLeft: 40, }}
>
Execution Location
Location
</InputLabel>
<Select
labelId="execution_location"
@@ -16523,6 +16522,11 @@ const releaseToConnectLabel = "Release to Connect"
style: {
},
}}
InputProps={{
style: {
height: 40,
}
}}
onChange={(e) => {
setLastSaved(false)
const env = environments.find((a) => a.Name === e.target.value);
@@ -16539,13 +16543,14 @@ const releaseToConnectLabel = "Release to Connect"
}}
style={{
pointerEvents: "auto",
backgroundColor: theme.palette.inputColor,
color: "white",
height: 50,
maxWidth: 250,
minWidth: 250,
borderRadius: theme.palette?.borderRadius,
marginLeft: 10,
marginLeft: 35,
backgroundColor: theme.palette.inputColor,
height: 40,
}}
>
{environments.map((data, index) => {
@@ -16603,6 +16608,43 @@ const releaseToConnectLabel = "Release to Connect"
</FormControl>
: null}
{parentWorkflows === undefined || parentWorkflows === null || parentWorkflows.length === 0 ? null :
<div style={{display: "flex", marginLeft: 40, maxWidth: 250, pointerEvents: "auto", marginTop: 5, }}>
<Typography variant="body2" color="textSecondary" style={{marginRight: 5, marginTop: 5, }}>
<b>Parent Workflows:</b>
</Typography>
{parentWorkflows.slice(0,5).map((wf, index) => {
return (
<a href={`/workflows/${wf.id}`} target="_blank" rel="noopener noreferrer" key={index}>
<Tooltip arrow placement="left" title={
<span style={{}}>
{wf.image !== undefined && wf.image !== null && wf.image.length > 0 ?
<img
src={wf.image}
alt={wf.name}
style={{ backgroundColor: theme.palette.surfaceColor, maxHeight: 200, minHeigth: 200, borderRadius: theme.palette?.borderRadius, }}
/>
: null}
<Typography>
Parent workflow: '{wf.name}'
</Typography>
</span>
} placement="bottom">
<span onClick={() => {
console.log("Click: ", wf)
}}>
<img src={theme.palette.defaultImage} style={{height: 25, width: 25, cursor: "pointer", border: 15, marginRight: 5, marginTop: 5, filter: "grayscale(90%)", }} />
</span>
</Tooltip>
</a>
)
})}
</div>
}
</div>
);
};
@@ -17210,7 +17252,7 @@ const releaseToConnectLabel = "Release to Connect"
<Tooltip color="primary" title="Stop execution" placement="top">
<span>
<Button
style={{ height: boxSize, width: boxSize }}
style={{ height: boxSize, width: boxSize+5 }}
color="secondary"
variant="contained"
onClick={() => {
@@ -17228,7 +17270,7 @@ const releaseToConnectLabel = "Release to Connect"
workflow.public
|| executionRequestStarted
}
style={{ height: boxSize, width: boxSize, backgroundColor: green, }}
style={{ height: boxSize, width: boxSize+5, backgroundColor: green, }}
color="primary"
variant="contained"
onClick={() => {
@@ -17265,7 +17307,7 @@ const releaseToConnectLabel = "Release to Connect"
{executionButton}
<Tooltip
color="secondary"
title={`Show previous runs (${workflowExecutions.length}) (Ctrl + ')`}
title={`Show previous workflow runs (${workflowExecutions.length}) (Ctrl + ')`}
placement="top-start"
>
<Button
@@ -17492,6 +17534,8 @@ const releaseToConnectLabel = "Release to Connect"
workflow.configuration.exit_on_error !== undefined ? (
<WorkflowMenu />
) : null}
{/*
<Tooltip
color="secondary"
title="Edit workflow details"
@@ -17514,6 +17558,7 @@ const releaseToConnectLabel = "Release to Connect"
</Button>
</span>
</Tooltip>
*/}
<Tooltip
color="secondary"
+7 -4
View File
@@ -1,3 +1,4 @@
import React, { memo, useCallback } from "react";
import { useState, useEffect, useContext, Suspense } from "react";
import { useNavigate, Link, useLocation } from "react-router-dom";
@@ -57,7 +58,7 @@ const ApiExplorer = React.lazy(() => import("../components/ApiExplorer.jsx"));
const ApiExplorerWrapper = (props) => {
const { globalUrl, serverside, userdata, isLoggedIn} = props;
const { globalUrl, serverside, userdata, isLoggedIn, isLoaded} = props;
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"
const location = useLocation();
const navigate = useNavigate();
@@ -1754,7 +1755,7 @@ const ApiExplorerWrapper = (props) => {
)});
return (
<Wrapper userdata={userdata}>
<Wrapper isLoggedIn={isLoggedIn} isLoaded={isLoaded}>
<Suspense fallback={skeletonLoader}>
{authenticationModal}
<ApiExplorer
@@ -1765,6 +1766,8 @@ const ApiExplorerWrapper = (props) => {
HandleApiExecution={HandleApiExecution}
selectedAppData={selectedAppData}
ConfigurationTab={ConfigurationTab}
isLoggedIn={isLoggedIn}
isLoaded={isLoaded}
/>
</Suspense>
</Wrapper>
@@ -1774,13 +1777,13 @@ const ApiExplorerWrapper = (props) => {
export default ApiExplorerWrapper;
const Wrapper = ({children, userdata})=>{
const Wrapper = ({children, isLoaded,isLoggedIn})=>{
const { leftSideBarOpenByClick } = useContext(Context);
return(
<div className="api-explorer-wrapper" style={{ paddingLeft: userdata?.support ? leftSideBarOpenByClick ? 280 : 100 : 0, transition: 'padding-left 0.3s ease' }}>
<div className="api-explorer-wrapper" style={{ paddingLeft: (isLoggedIn && isLoaded) ? leftSideBarOpenByClick ? 280 : 100 : 0, transition: 'padding-left 0.3s ease' }}>
{children}
</div>
)
+3 -5
View File
@@ -3239,15 +3239,13 @@ const AppsWrapper = memo(({ appView, modalView, userdata, publishModal, generate
));
const SidebarAdjustWrapper = memo(({ userdata, children }) => {
const SidebarAdjustWrapper = memo(({ children }) => {
const {leftSideBarOpenByClick } = useContext(Context)
const marginLeft = userdata?.support
? leftSideBarOpenByClick ? 250 : 80
: 0;
return (
<div style={{ marginLeft, transition: 'margin-left 0.3s ease' }}>
<div style={{ marginLeft: leftSideBarOpenByClick ? 250 : 80, transition: 'margin-left 0.3s ease' }}>
{children}
</div>
);
File diff suppressed because it is too large Load Diff
+2 -1
View File
@@ -274,7 +274,8 @@ const CodeWorkflow = (defaultprops) => {
display: 'flex',
flexDirection: 'column',
height: '100vh', // Set to full viewport height
backgroundColor: '#252526'
backgroundColor: '#252526',
marginLeft: '100px',
}}>
{/* IDE-like toolbar */}
<Toolbar
+16 -4
View File
@@ -337,11 +337,23 @@ const Dashboard = (props) => {
const [, setUpdate] = useState(0);
let navigate = useNavigate();
const isCloud =
window.location.host === "localhost:3002" ||
window.location.host === "shuffler.io";
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
const path = window.location.pathname
if (path !== undefined && path !== null) {
if (path.includes("/automate")) {
navigate("/usecases")
}
if (path.includes("/security")) {
navigate("/forms")
}
}
useEffect(() => {
// Look for the path. If it is automation, go to usecases
// Handles redirect
const widgetnames = ["app_executions_cloud"]
for (let widgetkey in widgetnames) {
const widgetName = widgetnames[widgetkey]
@@ -859,7 +871,7 @@ const Dashboard = (props) => {
);
const dataWrapper = (
<div style={{ maxWidth: 1366, margin: "auto" }}>{data}</div>
<div style={{ maxWidth: 1366, margin: "auto", paddingTop: 10, }}>{data}</div>
);
return dataWrapper;
+8 -8
View File
@@ -251,7 +251,7 @@ export const CodeHandler = (props) => {
}
const Docs = (defaultprops) => {
const { globalUrl, selectedDoc, serverside, serverMobile, userdata } = defaultprops;
const { globalUrl, selectedDoc, serverside, serverMobile, isLoggedIn, isLoaded } = defaultprops;
let navigate = useNavigate();
// Quickfix for react router 5 -> 6
const params = useParams();
@@ -1266,7 +1266,7 @@ const Docs = (defaultprops) => {
// Padding and zIndex etc set because of footer in cloud.
const loadedCheck = (
<DocsWrapper userdata={userdata}>
<DocsWrapper isLoggedIn={isLoggedIn} isLoaded={isLoaded}>
<DocsContent postDataBrowser={postDataBrowser} postDataMobile={postDataMobile}/>
</DocsWrapper>
);
@@ -1285,18 +1285,18 @@ const DocsContent = memo(({postDataBrowser, postDataMobile}) => {
</div>
)})
const DocsWrapper = memo(({userdata, children })=>{
const DocsWrapper = memo(({isLoggedIn, isLoaded, children })=>{
const { leftSideBarOpenByClick, windowWidth } = useContext(Context);
return (
<div style={{
minHeight: 1000, zIndex: 1,
maxWidth: Math.min(leftSideBarOpenByClick ? windowWidth - 300 : windowWidth - 200, 1920),
minWidth: isMobile ? null : leftSideBarOpenByClick ? 800 : 900, margin: "auto",
position: leftSideBarOpenByClick ? "relative" : "static",
left: leftSideBarOpenByClick ? 120 : !leftSideBarOpenByClick ? 80 : 0,
marginLeft: windowWidth < 1920 ? leftSideBarOpenByClick ? 160 : !leftSideBarOpenByClick ? 80 : 0 : "auto", width: "100%",
maxWidth: Math.min(!(isLoggedIn && isLoaded) ? 1920 : leftSideBarOpenByClick ? windowWidth - 300 : windowWidth - 200, 1920),
minWidth: isMobile ? null : (isLoggedIn && isLoaded) ? leftSideBarOpenByClick ? 800 : 900 : null, margin: "auto",
position: (isLoggedIn && isLoaded) && leftSideBarOpenByClick ? "relative" : "static",
left: (isLoggedIn && isLoaded) && leftSideBarOpenByClick ? 120 : (isLoggedIn && isLoaded) && !leftSideBarOpenByClick ? 80 : 0,
marginLeft: windowWidth < 1920 ? leftSideBarOpenByClick && (isLoggedIn && isLoaded) ? 160 : (isLoggedIn && isLoaded) && !leftSideBarOpenByClick ? 80 : 0 : "auto", width: "100%",
transition: "left 0.3s ease-in-out, min-width 0.3s ease-in-out, max-width 0.3s ease-in-out, position 0.3s ease-in-out, margin 0.3s ease-in-out, margin-left 0.3s ease"
}}>
{children}
+14 -7
View File
@@ -108,7 +108,6 @@ const RunWorkflow = (defaultprops) => {
const boxStyle = {
color: "white",
padding: "25px 50px 50px 50px",
backgroundColor: theme.palette.surfaceColor,
borderRadius: 25,
minHeight: 500,
}
@@ -1116,7 +1115,7 @@ const RunWorkflow = (defaultprops) => {
const ExplorerUi = () => {
return (
<div style={{paddingTop: 50, marginTop: 50, width: 250, itemAlign: "center", textAlign: "center", margin: "auto", }}>
<div style={{paddingTop: 50, marginTop: 50, width: 350, itemAlign: "center", textAlign: "center", margin: "auto", }}>
{forms !== undefined && forms !== null && forms.length > 0 ?
<div>
@@ -1126,9 +1125,14 @@ const RunWorkflow = (defaultprops) => {
<FormList />
</div>
:
<Typography variant="h6" style={{marginTop: 100, marginBottom: 20, }}>
No Form Found
</Typography>
<div>
<Typography variant="h4" style={{marginTop: 125, marginBottom: 25, }}>
No Forms Found
</Typography>
<Typography variant="body1" color="textSecondary">
<b>Every</b> Workflow is a form, and can be accessed by going to /forms/{`{workflow_id}`}. You can control the form by editing the workflow details in the "Forms" section.
</Typography>
</div>
}
</div>
)
@@ -1474,7 +1478,7 @@ const RunWorkflow = (defaultprops) => {
{workflowQuestion !== "" ? null :
<Typography variant="body2" color="textSecondary" align="center" style={{marginTop: 10, }} >
Forms are in late Beta. Form submission data includes your Organization's unique ID while logged in, or a unique identifier for your browser otherwise. Your input will be automatically sanitized.
Form submission data includes your Organization's unique ID while logged in, or a unique identifier for your browser otherwise. Your input will be automatically sanitized.
</Typography>
}
</div>
@@ -1482,7 +1486,7 @@ const RunWorkflow = (defaultprops) => {
// const isCorrectOrg = userdata.active_org.id === undefined || userdata.active_org.id === null || workflow.org_id === null || workflow.org_id === undefined || workflow.org_id.length === 0 || userdata.active_org.id === workflow.org_id
const loadedCheck = isLoaded ?
<div style={{marginTop: 30, }}>
<div style={{paddingTop: 60, }}>
{editWorkflowModalOpen === true ?
<EditWorkflow
saveWorkflow={saveWorkflow}
@@ -1570,6 +1574,7 @@ const RunWorkflow = (defaultprops) => {
<div style={{position: "fixed", top: 10, right: 20, }}>
<Button
disabled={workflow.id === undefined || workflow.id === null}
variant={"outlined"}
color={"secondary"}
style={{marginRight: 10, }}
@@ -1582,6 +1587,7 @@ const RunWorkflow = (defaultprops) => {
</Button>
<Button
disabled={workflow.id === undefined || workflow.id === null}
variant={workflow.sharing === "form" ? "outlined" : "contained"}
color={"secondary"}
style={{marginRight: 10, }}
@@ -1603,6 +1609,7 @@ const RunWorkflow = (defaultprops) => {
</Button>
<Button
disabled={workflow.id === undefined || workflow.id === null}
variant={"contained"}
color={"primary"}
style={{}}
+1 -1
View File
@@ -91,7 +91,7 @@ const Search = (props) => {
userdata={userdata}
/>, [curTab]);
const MemoizedDiscordChat = useMemo(() => <DiscordChat isMobile={isMobile} />)
const MemoizedDiscordChat = useMemo(() => <DiscordChat isMobile={isMobile} />, [curTab])
const useStyles = makeStyles({
hideIndicator: {
+2 -2
View File
@@ -1220,7 +1220,7 @@ const Usecases2 = (props) => {
) : null
const data =
<div className="content" style={{width: isMobile ? "100%": leftSideBarOpenByClick ? 1200: 1000, margin: "auto", paddingBottom: 200, textAlign: "center", paddingLeft: leftSideBarOpenByClick ? 200 : 0, transition: "padding-left 0.3s ease, width 0.3s ease"}}>
<div className="content" style={{width: isMobile ? "100%": leftSideBarOpenByClick ? 1000: 1200, margin: "auto", paddingBottom: 200, textAlign: "center", paddingLeft: leftSideBarOpenByClick ? 200 : 0, transition: "padding-left 0.3s ease, width 0.3s ease"}}>
<UsecaseListComponent
userdata={userdata}
@@ -1241,7 +1241,7 @@ const Usecases2 = (props) => {
const dataWrapper =
<Fade in={true} timeout={1250}>
<div style={{ maxWidth: 1366, margin: "auto", }}>
<div style={{ maxWidth: 1366, margin: "auto", zoom: 0.8, }}>
{data}
</div>
</Fade>
File diff suppressed because it is too large Load Diff
+1 -3
View File
@@ -2932,9 +2932,7 @@ func checkTenzirNode() error {
return nil
}
log.Printf("[DEBUG] Failed to verify Tenzir node on %s: %s", url, err)
return fmt.Errorf("Tenzir node is not available")
return fmt.Errorf("Tenzir node is not available due to: %s", err)
}
func createPipeline(command, identifier string) (string, error) {