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>
);
+172 -27
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,6 +499,11 @@ const AppModal = ({ open, onClose, app, userdata, globalUrl }) => {
</div>
</div>
<div style={{ display: "flex", flexDirection: "row", justifyContent: "center", alignItems: "center", gap: 10 }}>
{app?.activated &&
app?.private_id !== undefined &&
app?.private_id?.length > 0 &&
app?.generated ? (
<Button
variant="contained"
sx={{
@@ -403,11 +516,16 @@ const AppModal = ({ open, onClose, app, userdata, globalUrl }) => {
height: '40px',
padding: 2,
color: "#fff",
fontFamily: "Inter"
fontFamily: theme?.typography?.fontFamily
}}
onClick={(event) => {
event.preventDefault();
event.stopPropagation();
downloadApp(app);
}}
>
<CloudDownloadOutlined />
</Button>
</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,7 +561,7 @@ 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={{
@@ -441,7 +571,7 @@ const AppModal = ({ open, onClose, app, userdata, globalUrl }) => {
<Typography
variant="h6"
sx={{
fontFamily: 'Inter, sans-serif',
fontFamily: theme?.typography?.fontFamily,
fontSize: '24px',
fontWeight: 600,
mb: 0.3,
@@ -454,7 +584,7 @@ const AppModal = ({ open, onClose, app, userdata, globalUrl }) => {
variant="body2"
sx={{
color: 'rgba(255, 255, 255, 0.7)',
fontFamily: 'Inter, sans-serif',
fontFamily: theme?.typography?.fontFamily,
fontSize: '14px'
}}
>
@@ -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",
@@ -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>
+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
+99 -70
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();
@@ -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', }}>
<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>
@@ -2438,7 +2435,39 @@ const Billing = (props) => {
userdata={userdata}
/>
</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>
);
});
+51 -10
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("");
@@ -83,7 +84,7 @@ const AppStats = (defaultprops) => {
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>
+49 -21
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,6 +31,7 @@ const Branding = (props) => {
const [publishingInfo, setPublishingInfo] = useState("");
const [publishRequirements, setPublishRequirements] = useState([])
const { leftSideBarOpenByClick } = useContext(Context)
const handleEditOrg = (joinStatus) => {
const data = {
@@ -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={() => {
}}>
{!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()}
@@ -196,6 +222,8 @@ const Branding = (props) => {
</div>
</div>
</div>
</div>
</div>
)
}
+168 -59
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(() => {
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,9 +393,10 @@ 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={{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}}>
@@ -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}}>
<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
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"
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,35 +518,46 @@ 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}
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);
console.log("SELECTED!: ", select);
}}
name={"value"}
/>
@@ -459,11 +567,12 @@ const CacheView = (props) => {
/>
<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()}
/>
@@ -539,7 +645,10 @@ 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
+189 -121
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,7 +61,6 @@ 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 = "";
@@ -66,8 +68,6 @@ const Files = (props) => {
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,14 +247,26 @@ const Files = (props) => {
const fileDownloadModal = loadFileModalOpen ?
<Dialog
open={loadFileModalOpen}
onClose={() => {}}
PaperProps={{
style: {
backgroundColor: theme.palette.surfaceColor,
color: "white",
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>
@@ -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,44 +836,86 @@ 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"
<div
style={{
maxWidth: 250,
minWidth: 250,
overflow: "hidden",
marginLeft: 10,
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) => {
{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";
}
@@ -877,14 +931,16 @@ const Files = (props) => {
const filenamesplit = file.filename.split(".")
const iseditable = file.filesize < 2000000 && file.status === "active" && allowedFileTypes.includes(filenamesplit[filenamesplit.length-1])
return (
<ListItem
key={index}
style={{
display: 'table-row',
backgroundColor: bgColor,
maxHeight: 100,
overflow: "hidden",
borderBottomLeftRadius: files?.length - 1 === index ? 8 : 0,
borderBottomRightRadius: files?.length - 1 === index ? 8 : 0,
}}
>
{/*
@@ -898,12 +954,17 @@ const Files = (props) => {
/>
*/}
<ListItemText
style={{
maxWidth: 250,
minWidth: 250,
overflow: "hidden",
marginLeft: 10,
primaryTypographyProps={{
style: {
display: 'table-cell',
maxWidth: "170px",
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
overflow: 'hidden',
padding: 8
},
}}
primary={file.filename}
/>
<ListItemText
@@ -911,12 +972,13 @@ const Files = (props) => {
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"
? "white"
? "#FF8444"
: "grey",
}}
/>
@@ -939,12 +1001,13 @@ const Files = (props) => {
>
<IconButton
disabled={file.workflow_id === "global"}
style={{marginLeft: 10}}
>
<OpenInNewIcon
style={{
color:
file.workflow_id !== "global"
? "white"
? "#FF8444"
: "grey",
}}
/>
@@ -955,38 +1018,42 @@ const Files = (props) => {
)
}
style={{
minWidth: 100,
maxWidth: 100,
display: 'table-cell',
overflow: "hidden",
textAlign: isSelectedFiles?"center":null
}}
/>
<ListItemText
primary={file.md5_sum}
style={{
minWidth: 300,
maxWidth: 300,
primary={(
<Tooltip title={file.md5_sum}>
{file.md5_sum}
</Tooltip>
)}
primaryTypographyProps={{
style:{
display: 'table-cell',
marginLeft:isSelectedFiles? 15:null,
overflow: isSelectedFiles?"auto":"hidden",
overflow: "hidden",
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
maxWidth: 200,
}
}}
/>
<ListItemText
primary={file.status}
style={{
minWidth: 75,
maxWidth: 75,
display: 'table-cell',
overflow: "hidden",
textAlign:isSelectedFiles?"center":null,
marginLeft: 10,
textAlign:isSelectedFiles?"left":null,
color: file.status === "active" ? "#2BC07E" : "#FD4C62"
}}
/>
<ListItemText
primary={file.filesize}
style={{
minWidth: isSelectedFiles?80:125,
maxWidth: isSelectedFiles?80:125,
marginLeft: isSelectedFiles?15:null,
display: 'table-cell',
overflow: "hidden",
textAlign:'center'
}}
/>
<ListItemText
@@ -999,14 +1066,14 @@ const Files = (props) => {
<span>
<IconButton
disabled={!iseditable}
style = {{padding: "6px"}}
style = {{padding: "6px", }}
onClick={() => {
setOpenEditor(true)
setOpenFileId(file.id)
readFileData(file)
}}
>
<EditIcon
<img src="/icons/editIcon.svg" alt="edit icon"
style={{color: iseditable ? "white" : "grey",}}
/>
</IconButton>
@@ -1051,7 +1118,7 @@ const Files = (props) => {
downloadFile(file);
}}
>
<CloudDownloadIcon
<img src="/icons/downloadIcon.svg" alt="download icon"
style={{
color:
file.status === "active"
@@ -1070,36 +1137,14 @@ const Files = (props) => {
<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;
}
console.log("file is : ", file)
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");
}
}}
>
<FileCopyIcon style={{ color: "white" }} />
<img src="/icons/copyIcon.svg" alt="copy icon" style={{ color: "white" }} />
</IconButton>
</Tooltip>
<Tooltip
@@ -1115,7 +1160,7 @@ const Files = (props) => {
deleteFile(file)
}}
>
<DeleteIcon
<img src="/icons/deleteIcon.svg" alt="delete icon"
style={{
color:
file.status === "active"
@@ -1128,19 +1173,42 @@ const Files = (props) => {
</Tooltip>
</span>
style={{
minWidth: 250,
maxWidth: 250,
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>
)
})
+206 -49
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,
@@ -831,7 +871,7 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
},
"&:hover fieldset": {
borderColor: "#ffffff",
display: "block"
display: "block",
},
"&.Mui-focused fieldset": {
borderColor: "#A9A9A9",
@@ -849,10 +889,26 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
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)}}
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,9 +982,16 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
setOpenautomateTab((prev) => !prev);
setOpenSecurityTab(false);
}}
sx={{
style={{
color: "#FFFFFF",
marginLeft: 0.625,
}}
onMouseOver={(event)=>{
event.currentTarget.style.backgroundColor = "#2f2f2f";
}}
onMouseOut={(event)=>{
event.currentTarget.style.backgroundColor = "transparent";
}}
>
{openautomatetab ? (
<ExpandLessIcon
@@ -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%" }}>
<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
@@ -1390,10 +1542,15 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
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" }}>
+33 -30
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"
@@ -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*/}
+143 -102
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,18 +13,21 @@ 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)
@@ -216,8 +219,105 @@ const Priorities = (props) => {
const notificationWidth = "100%"
const imagesize = 22
const boxColor = "#86c142"
const NotificationItem = (props) => {
const {data} = props
return (
<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
})</Typography>
<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"
rel="noopener noreferrer"
href="/docs/organizations#notifications"
style={{ textDecoration: clickedFromOrgTab?null:"none", color: clickedFromOrgTab?"#FF8444":"#f85a3e" }}
>
Learn more
</a>
</span>
<div/>
<div style={{display: "flex", marginTop: 10, marginBottom: 10, }}>
<Switch
checked={showRead}
onChange={() => {
setShowRead(!showRead);
}}
/><span style={{marginTop: 5, }}>&nbsp; Show read </span>
{notifications !== undefined && notifications !== null && notifications.length > 1 ? (
<Button
color="primary"
variant="outlined"
disabled={notifications.filter((data) => !data.read).length === 0}
onClick={() => {
clearNotifications()
}}
style={{marginLeft: 50, }}
>
Mark all as read
</Button>
) : null}
</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 ? 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"
rel="noopener noreferrer"
href="/docs/organizations#priorities"
style={{ textDecoration: clickedFromOrgTab ?null:"none", color: clickedFromOrgTab ?"#FF8444":"#f85a3e" }}
>
Learn more
</a>
</span>
<div style={{marginTop: 10, }}/>
<Switch
checked={showDismissed}
onChange={() => {
setShowDismissed(!showDismissed);
}}
/>&nbsp; Show dismissed
{userdata.priorities === null || userdata.priorities === undefined || userdata.priorities.length === 0 ?
<Typography variant="h4">
No Suggestions found
</Typography>
:
userdata.priorities.map((priority, index) => {
if (showDismissed === false && priority.active === false) {
return null
}
return (
<Priority
key={index}
globalUrl={globalUrl}
priority={priority}
checkLogin={checkLogin}
clickedFromOrgTab={true}
setAdminTab={setAdminTab}
setCurTab={setCurTab}
appFramework={appFramework}
/>
)
})
}
</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 = "";
@@ -280,7 +380,7 @@ const Priorities = (props) => {
return (
<Paper
style={{
backgroundColor: theme.palette.platformColor,
backgroundColor: theme.palette.inputColor.backgroundColor,
width: clickedFromOrgTab ? null :notificationWidth,
padding: 30,
borderBottom: "1px solid rgba(255,255,255,0.4)",
@@ -321,7 +421,7 @@ const Priorities = (props) => {
style={{marginRight: 15, height: 25, }}
/>
}
<Typography variant="body1" color="textPrimary">
<Typography variant="body1" color="textPrimary" style={{ wordWrap: "break-word", overflow: "hidden", textOverflow: "ellipsis" }}>
{data.title}
</Typography >
</div>
@@ -331,7 +431,7 @@ const Priorities = (props) => {
:
null
}
<Typography variant="body2" color="textSecondary" style={{marginTop: 10, maxHeight: 200, overflowX: "hidden", overflowY: "auto", }}>
<Typography variant="body2" color="textSecondary" style={{ marginTop: 10, maxHeight: 200, overflowX: "hidden", overflowY: "auto", wordWrap: "break-word" }}>
{data.description}
</Typography >
<div style={{ display: "flex" }}>
@@ -377,121 +477,62 @@ const Priorities = (props) => {
</Tooltip>
</ButtonGroup>
<Typography variant="body2" color="textSecondary" style={{marginLeft: 20, marginTop: 20, }}>
<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, }}>
<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, }}>
<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 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 ({
notifications?.filter((notification) => showRead === true || notification.read === false).length
})</h2>
<span style={{ marginLeft: clickedFromOrgTab?null:25, color: clickedFromOrgTab?"#9E9E9E":null, }}>
Notifications help you find potential problems with your workflows and apps.&nbsp;
<a
target="_blank"
rel="noopener noreferrer"
href="/docs/organizations#notifications"
style={{ textDecoration: clickedFromOrgTab?null:"none", color: clickedFromOrgTab?"#FF8444":"#f85a3e" }}
>
Learn more
</a>
</span>
<div/>
<div style={{display: "flex", marginTop: 10, marginBottom: 10, }}>
<Switch
checked={showRead}
onChange={() => {
setShowRead(!showRead);
}}
/><span style={{marginTop: 5, }}>&nbsp; Show read </span>
{notifications !== undefined && notifications !== null && notifications.length > 1 ? (
<Button
color="primary"
variant="outlined"
disabled={notifications.filter((data) => !data.read).length === 0}
onClick={() => {
clearNotifications()
}}
style={{marginLeft: 50, }}
>
Mark all as read
</Button>
) : null}
</div>
{notifications === null || notifications === undefined || notifications.length === 0 ? null :
<div>
{notifications.map((notification, index) => {
{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} />
<NotificationItem data={notification} key={index} selectedExecutionId={selectedExecutionId} selectedWorkflow={selectedWorkflow} highlightKMS={highlightKMS} userdata={userdata} imagesize={imagesize} boxColor={boxColor} clickedFromOrgTab={clickedFromOrgTab} notificationWidth={notificationWidth} dismissNotification={dismissNotification} />
)
})}
</div>
}
{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 }}>
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"
rel="noopener noreferrer"
href="/docs/organizations#priorities"
style={{ textDecoration: clickedFromOrgTab ?null:"none", color: clickedFromOrgTab ?"#FF8444":"#f85a3e" }}
>
Learn more
</a>
</span>
<div style={{marginTop: 10, }}/>
<Switch
checked={showDismissed}
onChange={() => {
setShowDismissed(!showDismissed);
}}
/>&nbsp; Show dismissed
{userdata.priorities === null || userdata.priorities === undefined || userdata.priorities.length === 0 ?
<Typography variant="h4">
No Suggestions found
</Typography>
:
userdata.priorities.map((priority, index) => {
if (showDismissed === false && priority.active === false) {
return null
}
return (
<Priority
key={index}
globalUrl={globalUrl}
priority={priority}
checkLogin={checkLogin}
clickedFromOrgTab={true}
setAdminTab={setAdminTab}
setCurTab={setCurTab}
appFramework={appFramework}
/>
)
})
}
</div>
)
}
})
export default Priorities;
// 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>
);
};
+110 -65
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,
}}
@@ -11199,8 +11203,6 @@ const releaseToConnectLabel = "Release to Connect"
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>
{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>
);
+287 -121
View File
@@ -36,6 +36,7 @@ import algoliasearch from "algoliasearch/lite";
import { debounce } from "lodash";
import AppSelection from "../components/AppSelection.jsx";
import AppModal from "../components/AppModal.jsx";
import AppCreationModal from "../components/AppCreationModal.jsx";
const searchClient = algoliasearch(
@@ -45,6 +46,7 @@ const searchClient = algoliasearch(
// AppCard Component
const AppCard = ({ data, index, mouseHoverIndex, setMouseHoverIndex, globalUrl, deactivatedIndexes, currTab, handleAppClick, leftSideBarOpenByClick, userdata }) => {
const navigate = useNavigate();
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io" || window.location.host === "localhost:3000";
const appUrl = isCloud ? `/apps/${data.id}` : `https://shuffler.io/apps/${data.id}`;
var canEditApp = userdata.admin === "true" || userdata.id === data?.owner || data?.owner === "" || (userdata.admin === "true" && userdata.active_org.id === data?.reference_org) || !data?.generated
@@ -53,7 +55,7 @@ const AppCard = ({ data, index, mouseHoverIndex, setMouseHoverIndex, globalUrl,
backgroundColor: mouseHoverIndex === index ? "rgba(26, 26, 26, 1)" : "#212121",
color: "rgba(241, 241, 241, 1)",
cursor: "pointer",
fontFamily: "Inter",
fontFamily: theme?.typography?.fontFamily,
// position: "relative",
width: "100%",
height: 96,
@@ -76,7 +78,7 @@ const AppCard = ({ data, index, mouseHoverIndex, setMouseHoverIndex, globalUrl,
fontSize: 16,
overflow: "hidden",
display: "flex",
fontFamily: "Inter",
fontFamily: theme?.typography?.fontFamily,
width: '100%',
backgroundColor: mouseHoverIndex === index ? "#2F2F2F" : "#212121"
}}
@@ -105,7 +107,7 @@ const AppCard = ({ data, index, mouseHoverIndex, setMouseHoverIndex, globalUrl,
fontWeight: '400',
overflow: "hidden",
margin: "12px 0",
fontFamily: "Inter"
fontFamily: theme?.typography?.fontFamily
}}>
<div style={{
display: 'flex',
@@ -138,8 +140,14 @@ const AppCard = ({ data, index, mouseHoverIndex, setMouseHoverIndex, globalUrl,
display: "flex",
justifyContent: 'space-between',
paddingRight: 15,
height: 35,
}}>
<div style={{
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
color: "rgba(158, 158, 158, 1)",
}}>
<div style={{ overflow: "hidden", textAlign: 'start' }}>
{data.generated !== true && data.tags && data.tags.slice(0, 2).map((tag, tagIndex) => (
<span key={tagIndex}>
{tag}
@@ -151,31 +159,46 @@ const AppCard = ({ data, index, mouseHoverIndex, setMouseHoverIndex, globalUrl,
{currTab === 0 && !deactivatedIndexes.includes(index) && mouseHoverIndex === index && data.generated === true && (
<div style={{
display: "flex",
gap: 8
gap: 8,
alignItems: 'center',
paddingRight: 20
}}>
{
canEditApp && (
<button style={{ backgroundColor: "rgba(73, 73, 73, 1)", border: "none", cursor: "pointer", color: "white", borderRadius: 3, display: "flex", alignItems: "center", justifyContent: "center" }}>
<button style={{ backgroundColor: "rgba(73, 73, 73, 1)", border: "none", cursor: "pointer", color: "white", borderRadius: 3, display: "flex", alignItems: "center", justifyContent: "center", height: 35 }}
onClick={(event) => {
event.preventDefault();
event.stopPropagation();
if (canEditApp) {
const editUrl = "/apps/edit/" + data?.id;
navigate(editUrl)
}
}}
>
<EditIcon />
</button>
)
}
<Button
className="deactivate-button"
style={{
// marginLeft: 15,
sx={{
width: 102,
height: 35,
borderRadius: 3,
backgroundColor: "rgba(73, 73, 73, 1)",
borderRadius: 0.75,
bgcolor: "rgba(73, 73, 73, 1)",
color: "rgba(241, 241, 241, 1)",
textTransform: "none",
fontFamily: "Inter"
fontFamily: theme?.typography?.fontFamily,
transition: "background-color 0.3s ease",
"&:hover": {
bgcolor: "rgba(93, 93, 93, 1)",
},
}}
onClick={(event) => {
event.preventDefault();
event.stopPropagation();
const url = `${globalUrl}/api/v1/apps/${data.id}/deactivate`;
toast("Deactivating app. Please wait...");
fetch(url, {
method: 'GET',
headers: {
@@ -189,7 +212,7 @@ const AppCard = ({ data, index, mouseHoverIndex, setMouseHoverIndex, globalUrl,
if (responseJson.success === false) {
toast.error(responseJson.reason);
} else {
toast.success("App Deactivated Successfully. Reload UI to see updated changes.");
toast.success("App Deactivated Successfully.");
}
})
.catch(error => {
@@ -218,14 +241,35 @@ const Hits = ({
setIsAnyAppActivated,
searchQuery,
globalUrl,
isLoading,
isLoggedIn,
currTab,
leftSideBarOpenByClick
}) => {
const [hoverEffect, setHoverEffect] = useState(-1);
const [allActivatedAppIds, setAllActivatedAppIds] = useState(userdata?.active_apps);
const [allActivatedAppIds, setAllActivatedAppIds] = useState([]);
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
const [deactivatedIndexes, setDeactivatedIndexes] = React.useState([]);
const [isLoading, setIsLoading] = useState(true)
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) {
setAllActivatedAppIds(responseJson.active_apps)
}
setIsLoading(false)
})
.catch(error => {
console.log("Failed login check: ", error);
});
}, [currTab]);
const normalizedString = (name) => {
if (typeof name === 'string') {
@@ -235,12 +279,20 @@ const Hits = ({
}
};
useEffect(() => {
if (userdata && userdata.active_apps) {
setAllActivatedAppIds(userdata.active_apps);
}
}, [currTab, window.location]);
//Function for activation and deactivation of app
const handleActivateButton = (event, data, type) => {
//use prevent default so it will stop redirection to the app page
event.preventDefault();
event.stopPropagation();
if (!isLoggedIn) {
toast.error("Please log in to your account to activate the app.")
return;
@@ -287,25 +339,15 @@ const Hits = ({
let workflowDelay = 0;
const isHeader = true;
const [showNoAppFound, setShowNoAppFound] = useState(false);
useEffect(() => {
const timer = setTimeout(() => {
setShowNoAppFound(true);
}, 1000);
return () => clearTimeout(timer);
}, []);
return (
<div>
{!isLoading ? (
{!isLoading ?
(
<div>
{hits?.length === 0 && searchQuery.length >= 0 && showNoAppFound ? (
<div style={{ marginTop: 100, fontSize: 20, fontWeight: 500, width: "100%", textAlign: "center" }}>
<Typography variant="body1">No Apps Found</Typography>
</div>
{hits?.length === 0 && searchQuery.length >= 0 ? (
<Typography>No apps found</Typography>
) : (
<div
style={{
@@ -314,9 +356,11 @@ const Hits = ({
display: "grid",
gridTemplateColumns: "repeat(auto-fill, minmax(365px, 1fr))",
gap: "20px",
fontFamily: theme?.typography?.fontFamily,
justifyContent: "space-between",
alignItems: "start",
padding: "0 10px"
padding: "0 10px",
paddingBottom: 40
}}
>
{hits?.map((data, index) => {
@@ -339,7 +383,7 @@ const Hits = ({
backgroundColor: hoverEffect === index ? "rgba(26, 26, 26, 1)" : "#212121",
color: "rgba(241, 241, 241, 1)",
cursor: "pointer",
fontFamily: "Inter",
fontFamily: theme?.typography?.fontFamily,
// position: "relative",
width: "100%",
height: 96,
@@ -360,11 +404,12 @@ const Hits = ({
fontSize: 16,
overflow: "hidden",
display: "flex",
fontFamily: "Inter",
width: '100%',
backgroundColor: hoverEffect === index ? "#2F2F2F" : "#212121"
backgroundColor: hoverEffect === index ? "#2F2F2F" : "#212121",
fontFamily: theme?.typography?.fontFamily
}}
onClick={() => {
console.log("App modal", data)
handleAppClick(data);
}}
>
@@ -390,7 +435,7 @@ const Hits = ({
fontWeight: '400',
overflow: "hidden",
margin: "12px 0",
fontFamily: "Inter"
fontFamily: theme?.typography?.fontFamily
}}
>
<div
@@ -426,12 +471,16 @@ const Hits = ({
style={{
display: "flex",
justifyContent: 'space-between',
width: 230,
width: "100%",
textAlign: 'start',
color: "rgba(158, 158, 158, 1)",
height: 35,
alignItems: 'center'
}}
>
<div style={{ marginBottom: 15, }}>
<div style={{
flex: 1
}}>
{hoverEffect === index && isCloud ? (
<div>
{data.tags && (
@@ -463,7 +512,12 @@ const Hits = ({
)}
</div>
) : (
<div style={{ width: 230, textOverflow: "ellipsis", overflow: 'hidden', whiteSpace: 'nowrap', }}>
<div style={{
width: 230,
textOverflow: "ellipsis",
overflow: 'hidden',
whiteSpace: 'nowrap',
}}>
{data.tags &&
data.tags.map((tag, tagIndex) => (
<span key={tagIndex}>
@@ -474,17 +528,25 @@ const Hits = ({
</div>
)}
</div>
<div style={{ position: 'relative', bottom: 5 }}>
<div style={{
display: 'flex',
justifyContent: 'flex-end',
alignItems: 'center',
paddingRight: 10,
minWidth: 110
}}>
{hoverEffect === index && isCloud && (
<div>
{allActivatedAppIds && allActivatedAppIds.includes(data.objectID) ? (
<Button style={{
<Button
style={{
width: 102,
height: 35,
borderRadius: 200,
borderRadius: 3,
backgroundColor: "rgba(73, 73, 73, 1)",
color: "rgba(241, 241, 241, 1)",
textTransform: "none",
fontFamily: theme?.typography?.fontFamily,
}}
onClick={(event) => {
handleActivateButton(event, data, "deactivate");
@@ -494,12 +556,13 @@ const Hits = ({
) : (
<Button
style={{
backgroundColor: "#FF8544",
color: "black",
width: 102,
height: 35,
borderRadius: 200,
backgroundColor: "rgba(242, 101, 59, 1)",
color: "rgba(255, 255, 255, 1)",
borderRadius: 3,
textTransform: "none",
fontFamily: theme?.typography?.fontFamily,
}}
onClick={(event) => {
handleActivateButton(event, data, "activate");
@@ -535,7 +598,7 @@ const Hits = ({
const SearchBox = ({ refine, searchQuery, setSearchQuery }) => {
const inputRef = useRef(null);
const [localQuery, setLocalQuery] = useState(searchQuery);
const location = useLocation();
// Initialize search when component mounts or when switching to Discover tab
useEffect(() => {
if (searchQuery) {
@@ -592,7 +655,7 @@ const SearchBox = ({ refine, searchQuery, setSearchQuery }) => {
event.preventDefault();
}
}}
style={{ borderRadius: 8, height: 45, fontFamily: "Inter", flex: 1 }}
style={{ borderRadius: 8, height: 45, fontFamily: theme?.typography?.fontFamily, flex: 1 }}
InputProps={{
style: {
borderRadius: 8,
@@ -608,6 +671,9 @@ const SearchBox = ({ refine, searchQuery, setSearchQuery }) => {
onClick={() => {
setLocalQuery('');
debouncedRefine('');
const queryParams = new URLSearchParams(location.search);
queryParams.delete('q');
window.history.replaceState({}, '', `${location.pathname}?${queryParams.toString()}`);
}}
/>
)}
@@ -637,14 +703,26 @@ const CategoryDropdown = ({ items, currentRefinement, refine }) => {
onChange={handleChange}
displayEmpty
multiple
style={{ borderRadius: 8, height: 45, fontFamily: "Inter", flex: 1 }}
renderValue={(selected) => selected.length ? selected.join(', ') : 'All Categories'}
style={{ borderRadius: 8, height: 45, fontFamily: theme?.typography?.fontFamily, flex: 1 }}
renderValue={(selected) => {
if (selected.length === 0) return 'All Categories';
return (
<div style={{
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
maxWidth: '90%'
}}>
{selected.join(', ')}
</div>
);
}}
>
<MenuItem disabled value="" style={{ fontFamily: "Inter" }}>
<MenuItem disabled value="" style={{ fontFamily: theme?.typography?.fontFamily }}>
All Categories
</MenuItem>
{items.map(item => (
<MenuItem key={item.label} value={item.label} style={{ fontFamily: "Inter", fontSize: 16 }}>
<MenuItem key={item.label} value={item.label} style={{ fontFamily: theme?.typography?.fontFamily, fontSize: 16 }}>
<Checkbox checked={currentRefinement.includes(item.label)} />
{item.label} ({item.count})
</MenuItem>
@@ -686,15 +764,27 @@ const LabelDropdown = ({ items, currentRefinement, refine }) => {
onChange={handleChange}
displayEmpty
multiple
style={{ borderRadius: 8, height: 45, fontFamily: "Inter", flex: 1 }}
renderValue={(selected) => selected.length ? selected.join(', ') : 'All Labels'}
style={{ borderRadius: 8, height: 45, fontFamily: theme?.typography?.fontFamily, flex: 1 }}
renderValue={(selected) => {
if (selected.length === 0) return 'All Labels';
return (
<div style={{
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
maxWidth: '90%'
}}>
{selected.join(', ')}
</div>
);
}}
>
<MenuItem disabled value="">
All Labels
</MenuItem>
{items.map(item => (
<MenuItem key={item.label} value={item.label} style={{
fontFamily: "Inter",
fontFamily: theme?.typography?.fontFamily,
fontSize: 16
}}>
<Checkbox checked={currentRefinement.includes(item.label)} />
@@ -835,7 +925,7 @@ const LoadingGrid = () => {
// Main Apps Component
const Apps2 = (props) => {
const { globalUrl, isLoaded, serverside, userdata, isLoggedIn, checkLogin, isCloud } = props;
const { globalUrl, isLoaded, serverside, userdata, isLoggedIn, checkLogin } = props;
let navigate = useNavigate();
const { leftSideBarOpenByClick } = useContext(Context);
const location = useLocation();
@@ -869,9 +959,16 @@ const Apps2 = (props) => {
const [field1, setField1] = useState("");
const [field2, setField2] = useState("");
const [validation, setValidation] = useState(null);
const [createAppModalOpen, setCreateAppModalOpen] = useState(false);
const baseRepository = "https://github.com/frikky/shuffle-apps";
const isCloud =
window.location.host === "localhost:3002" ||
window.location.host === "shuffler.io"
? true
: false;
// Set the current tab based on the query parameter
useEffect(() => {
const queryParams = new URLSearchParams(location.search);
@@ -931,14 +1028,13 @@ const Apps2 = (props) => {
const fetchApps = async () => {
const baseUrl = globalUrl;
let url;
setIsLoading(true);
const userId = userdata?.id;
if (currTab === 1 && userId) {
url = `${baseUrl}/api/v1/users/${userId}/apps`;
} else if (currTab === 0) {
url = `${baseUrl}/api/v1/apps`;
}
setIsLoading(true);
try {
const response = await fetch(url, {
method: "GET",
@@ -1262,7 +1358,7 @@ const Apps2 = (props) => {
borderRadius: 2,
border: "1px solid #494949",
minWidth: '440px',
fontFamily: "Inter",
fontFamily: theme?.typography?.fontFamily,
backgroundColor: "#212121",
zIndex: 1000,
'& .MuiDialogContent-root': {
@@ -1272,10 +1368,10 @@ const Apps2 = (props) => {
backgroundColor: "#212121",
},
'& .MuiTypography-root': {
fontFamily: 'Inter, sans-serif',
fontFamily: theme?.typography?.fontFamily,
},
'& .MuiButton-root': {
fontFamily: 'Inter, sans-serif',
fontFamily: theme?.typography?.fontFamily,
},
}
}}
@@ -1393,7 +1489,8 @@ const Apps2 = (props) => {
/>
</div>
</DialogContent>
<DialogActions sx={{ p: 3,
<DialogActions sx={{
p: 3,
backgroundColor: "#212121"
}}>
<Button
@@ -1406,7 +1503,7 @@ const Apps2 = (props) => {
py: 1,
px: 3,
color: "#fff",
fontFamily: "Inter"
fontFamily: theme?.typography?.fontFamily
}}
onClick={() => setLoadAppsModalOpen(false)}
>
@@ -1423,7 +1520,7 @@ const Apps2 = (props) => {
py: 1,
px: 3,
color: "#fff",
fontFamily: "Inter"
fontFamily: theme?.typography?.fontFamily
}}
disabled={openApi.length === 0 || !openApi.includes("http")}
onClick={() => handleGithubValidation(true)}
@@ -1441,7 +1538,7 @@ const Apps2 = (props) => {
py: 1,
px: 3,
color: "black",
fontFamily: "Inter"
fontFamily: theme?.typography?.fontFamily
}}
disabled={openApi.length === 0 || !openApi.includes("http")}
onClick={() => handleGithubValidation(false)}
@@ -1510,6 +1607,7 @@ const Apps2 = (props) => {
const handleCreateApp = (e) => {
e.preventDefault();
setCreateAppModalOpen(true);
// setOpenModal(true);
};
@@ -1553,37 +1651,28 @@ const Apps2 = (props) => {
navigate(`${location.pathname}?${queryParams.toString()}`);
};
// Update useEffect to handle initial load and URL search params
useEffect(() => {
const queryParams = new URLSearchParams(location.search);
const searchParam = queryParams.get('q');
if (searchParam) {
setSearchQuery(searchParam);
}
}, []);
// Update useEffect for filtering to handle both tabs
// Update useEffect for filtering without URL manipulation
useEffect(() => {
if (currTab === 2) return; // Skip for "Discover Apps" tab as it uses Algolia
const apps = currTab === 1 ? userApps : orgApps;
const filteredApps = filterApps(apps, searchQuery, selectedCategory, selectedLabel);
setAppsToShow(filteredApps);
}, [searchQuery, selectedCategory, selectedLabel, currTab, userApps, orgApps]);
// Update URL with search query
// Add URL update only when search is performed
const handleSearchChange = (event) => {
const newSearchQuery = event.target.value;
setSearchQuery(newSearchQuery);
// Update URL only when user performs search
const queryParams = new URLSearchParams(location.search);
if (searchQuery) {
queryParams.set('q', searchQuery);
if (newSearchQuery) {
queryParams.set('q', newSearchQuery);
} else {
queryParams.delete('q');
}
window.history.replaceState({}, '', `${location.pathname}?${queryParams.toString()}`);
}, [searchQuery, selectedCategory, selectedLabel, currTab, userApps, orgApps]);
// Update search input handler to maintain state across tabs
const handleSearchChange = (event) => {
const newSearchQuery = event.target.value;
setSearchQuery(newSearchQuery);
};
const boxStyle = {
@@ -1594,7 +1683,7 @@ const Apps2 = (props) => {
width: "100%",
margin: "auto",
maxWidth: "70%",
fontFamily: "Inter",
fontFamily: theme?.typography?.fontFamily,
// padding: '20px 380px',
};
@@ -1619,7 +1708,7 @@ const Apps2 = (props) => {
}
return (
<div style={{ paddingTop: 70, minHeight: 1000, paddingLeft: leftSideBarOpenByClick ? 200 : 0, transition: "padding-left 0.3s ease", backgroundColor: "#1A1A1A" }}>
<div style={{ paddingTop: 70, paddingLeft: leftSideBarOpenByClick ? 200 : 0, transition: "padding-left 0.3s ease", backgroundColor: "#1A1A1A", fontFamily: theme?.typography?.fontFamily, zoom: 0.8, }}>
<InstantSearch searchClient={searchClient} indexName="appsearch">
<AppModal
open={openModal}
@@ -1628,10 +1717,17 @@ const Apps2 = (props) => {
userdata={userdata}
globalUrl={globalUrl}
/>
<AppCreationModal
open={createAppModalOpen}
onClose={() => setCreateAppModalOpen(false)}
theme={theme}
globalUrl={globalUrl}
isCloud={isCloud}
/>
{appsModalLoad}
<div style={boxStyle}>
<div style={{ display: "flex", flexDirection: "row", width: "100%", justifyContent: "space-between" }}>
<Typography variant="h4" style={{ marginBottom: 20, paddingLeft: 15, textTransform: 'none', fontFamily: "Inter" }}>
<Typography variant="h4" style={{ marginBottom: 20, paddingLeft: 15, textTransform: 'none', fontFamily: theme?.typography?.fontFamily }}>
Apps
</Typography>
{isCloud ? null : (
@@ -1648,7 +1744,7 @@ const Apps2 = (props) => {
color: "rgba(241, 241, 241, 1)",
fontSize: 14,
border: "1px solid rgba(73, 73, 73, 1)",
fontFamily: "Inter"
fontFamily: theme?.typography?.fontFamily,
}
}
}}
@@ -1688,7 +1784,7 @@ const Apps2 = (props) => {
color: "rgba(241, 241, 241, 1)",
fontSize: 14,
border: "1px solid rgba(73, 73, 73, 1)",
fontFamily: "Inter"
fontFamily: theme?.typography?.fontFamily,
}
}
}}
@@ -1720,22 +1816,25 @@ const Apps2 = (props) => {
</span>
)}
</div>
<div style={{ borderBottom: '1px solid gray', marginBottom: 30, marginRight: 10 }}>
<div style={{ borderBottom: '1px solid gray', marginBottom: 30 }}>
<Tabs
value={currTab}
onChange={(event, newTab) => handleTabChange(event, newTab)}
TabIndicatorProps={{ style: { height: '3px', borderRadius: 10 } }}
style={{ fontFamily: "Inter" }}
TabIndicatorProps={{ style: { height: '3px', borderRadius: 10, backgroundColor: "#FF8544" } }}
style={{ fontFamily: theme?.typography?.fontFamily }}
>
<Tab label="Organization Apps" style={{ textTransform: 'none', marginRight: 20, fontFamily: "Inter" }} />
<Tab label="My Apps" style={{ textTransform: 'none', marginRight: 20, fontFamily: "Inter" }} />
<Tab label="Discover Apps" style={{ textTransform: 'none', fontFamily: "Inter" }} />
<Tab label="Organization Apps" style={{ textTransform: 'none', marginRight: 20, fontFamily: theme?.typography?.fontFamily }} />
<Tab label="My Apps" style={{ textTransform: 'none', marginRight: 20, fontFamily: theme?.typography?.fontFamily }} />
<Tab label="Discover Apps" style={{ textTransform: 'none', fontFamily: theme?.typography?.fontFamily }} />
</Tabs>
</div>
<div style={{ display: "flex", justifyContent: "space-between", gap: 10, marginBottom: 20, height: 45 }}>
<div style={{ flex: 1, width: '100%', borderRadius: '7px' }}>
{
(currTab === 0 || currTab === 1) &&
<div style={{ display: "flex", justifyContent: "space-between", gap: 10, marginBottom: 20, height: 45, paddingRight: 25 }}>
<div style={{
width: "25%",
minWidth: "25%",
maxWidth: "25%"
}}>
{(currTab === 0 || currTab === 1) ? (
<TextField
fullWidth
variant="outlined"
@@ -1756,13 +1855,14 @@ const Apps2 = (props) => {
},
endAdornment: (
<InputAdornment position="end">
{searchQuery.length === 0 ? (
<Search />
) : (
{searchQuery.length === 0 ? <Search /> : (
<ClearIcon
style={{ cursor: "pointer" }}
onClick={() => {
setSearchQuery("");
setSearchQuery("")
const queryParams = new URLSearchParams(location.search);
queryParams.delete('q');
window.history.replaceState({}, '', `${location.pathname}?${queryParams.toString()}`);
}}
/>
)}
@@ -1770,13 +1870,16 @@ const Apps2 = (props) => {
),
}}
/>
}
{
currTab === 2 &&
) : (
<CustomSearchBox searchQuery={searchQuery} setSearchQuery={setSearchQuery} />
}
)}
</div>
<div style={{ flex: 1, width: '100%', borderRadius: '7px', position: 'relative' }}>
<div style={{
width: "25%",
minWidth: "25%",
maxWidth: "25%",
position: 'relative'
}}>
{currTab === 2 ? (
<CustomCategoryDropdown attribute="categories" />
) : (
@@ -1788,14 +1891,30 @@ const Apps2 = (props) => {
onChange={handleCategoryChange}
displayEmpty
multiple
style={{ borderRadius: 8, height: 45, fontFamily: "Inter" }}
renderValue={(selected) => selected.length ? selected.join(', ') : 'All Categories'}
style={{
borderRadius: 8,
height: 45,
fontFamily: theme?.typography?.fontFamily
}}
renderValue={(selected) => {
if (selected.length === 0) return 'All Categories';
return (
<div style={{
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
maxWidth: '90%'
}}>
{selected.join(', ')}
</div>
);
}}
>
<MenuItem disabled value="" style={{ fontFamily: "Inter" }}>
<MenuItem disabled value="" style={{ fontFamily: theme?.typography?.fontFamily }}>
All Categories
</MenuItem>
{categories?.map((category) => (
<MenuItem key={category.category} value={category.category} style={{ fontFamily: "Inter", fontSize: 16 }}>
<MenuItem key={category.category} value={category.category} style={{ fontFamily: theme?.typography?.fontFamily, fontSize: 16 }}>
<Checkbox checked={selectedCategory.includes(category.category)} />
{category.category}
</MenuItem>
@@ -1819,7 +1938,12 @@ const Apps2 = (props) => {
</>
)}
</div>
<div style={{ flex: 1, width: '100%', borderRadius: '7px', position: 'relative' }}>
<div style={{
width: "25%",
minWidth: "25%",
maxWidth: "25%",
position: 'relative'
}}>
{currTab === 2 ? (
<CustomLabelDropdown attribute="action_labels" />
) : (
@@ -1831,14 +1955,30 @@ const Apps2 = (props) => {
onChange={handleLabelChange}
displayEmpty
multiple
style={{ borderRadius: 8, height: 45, fontFamily: "Inter" }}
renderValue={(selected) => selected.length ? selected.join(', ') : 'All Labels'}
style={{
borderRadius: 8,
height: 45,
fontFamily: theme?.typography?.fontFamily
}}
renderValue={(selected) => {
if (selected.length === 0) return 'All Labels';
return (
<div style={{
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
maxWidth: '90%'
}}>
{selected.join(', ')}
</div>
);
}}
>
<MenuItem disabled value="" style={{ fontFamily: "Inter" }}>
<MenuItem disabled value="" style={{ fontFamily: theme?.typography?.fontFamily }}>
All Labels
</MenuItem>
{labels?.map((tag) => (
<MenuItem key={tag.tag} value={tag.tag} style={{ fontFamily: "Inter", fontSize: 16 }}>
<MenuItem key={tag.tag} value={tag.tag} style={{ fontFamily: theme?.typography?.fontFamily, fontSize: 16 }}>
<Checkbox checked={selectedLabel.includes(tag.tag)} />
{tag.tag}
</MenuItem>
@@ -1862,14 +2002,28 @@ const Apps2 = (props) => {
</>
)}
</div>
<div style={{ flex: 1 }}>
<div style={{
width: "25%",
minWidth: "25%",
maxWidth: "25%"
}}>
<Button
variant="contained"
color="primary"
onClick={handleCreateApp}
style={{ height: "100%", width: '100%', borderRadius: '7px', textTransform: 'none', backgroundColor: "#FF8544", color: "#1A1A1A", fontFamily: "Inter" }}
style={{
height: "100%",
width: '100%',
borderRadius: '7px',
textTransform: 'none',
backgroundColor: "#FF8544",
color: "#1A1A1A",
fontFamily: theme?.typography?.fontFamily,
fontSize: 16,
fontWeight: 500
}}
startIcon={<Add style={{ color: "#1A1A1A" }} />}
>
<Add style={{ marginRight: 10, color: "#1A1A1A", fontSize: 20 }} />
Create an App
</Button>
</div>
@@ -1892,7 +2046,8 @@ const Apps2 = (props) => {
gap: "20px",
justifyContent: "space-between",
alignItems: "start",
padding: "0 10px"
padding: "0 10px",
paddingBottom: 40
}}>
{appsToShow.map((data, index) => (
<AppCard
@@ -1931,7 +2086,7 @@ const Apps2 = (props) => {
}
{
currTab === 1 && (
<div style={{ minHeight: 570, overflowY: "auto", overflowX: "hidden" }}>
<div style={{ minHeight: 570 }}>
{isLoading ? (
<LoadingGrid />
) : (
@@ -1945,7 +2100,8 @@ const Apps2 = (props) => {
gap: "20px",
justifyContent: "space-between",
alignItems: "start",
padding: "0 10px"
padding: "0 10px",
paddingBottom: 40
}}>
{appsToShow.map((data, index) => (
<AppCard key={index} data={data} index={index} mouseHoverIndex={mouseHoverIndex} setMouseHoverIndex={setMouseHoverIndex} globalUrl={globalUrl} deactivatedIndexes={deactivatedIndexes} currTab={currTab} userdata={userdata}
@@ -1955,7 +2111,16 @@ const Apps2 = (props) => {
</div>
) : (
<div style={{ width: "100%", marginTop: 60, textAlign: "center", display: "flex", justifyContent: "center", alignItems: "center" }}>
<Typography variant="body1">No Apps Found</Typography>
<AppSelection
userdata={userdata}
globalUrl={globalUrl}
appFramework={appFramework}
setAppFramework={setAppFramework}
defaultSearch={defaultSearch}
setDefaultSearch={setDefaultSearch}
checkLogin={checkLogin}
isAppPage={true}
/>
</div>
)}
</>
@@ -1976,6 +2141,7 @@ const Apps2 = (props) => {
searchQuery={searchQuery}
mouseHoverIndex={mouseHoverIndex}
setMouseHoverIndex={setMouseHoverIndex}
currTab={currTab}
leftSideBarOpenByClick={leftSideBarOpenByClick}
/>
}
+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}
+13 -6
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
<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>
+319 -321
View File
@@ -58,6 +58,7 @@ import {
AvatarGroup,
Zoom,
Collapse,
Skeleton,
} from "@mui/material";
// Material UI Icons
@@ -430,14 +431,17 @@ export const GetIconInfo = (action) => {
};
const chipStyle = {
backgroundColor: "#3d3f43",
backgroundColor: "#2F2F2F",
marginRight: 5,
paddingLeft: 5,
paddingRight: 5,
height: 28,
height: 35,
cursor: "pointer",
borderColor: "#3d3f43",
color: "white",
borderColor: "#2F2F2F",
color: "#C8C8C8",
fontSize: "14px",
fontFamily: theme?.typography?.fontFamily,
borderRadius: "17.5px"
};
export const collapseField = (field) => {
@@ -618,9 +622,11 @@ const useDropzoneStyles = () => {
const { leftSideBarOpenByClick } = useContext(Context);
return {
maxWidth: window.innerWidth > 1366 ? 1366 : isMobile ? "100%" : 1200,
margin: "auto",
padding: 20,
paddingTop: 70,
// minHeight: 1000,
backgroundColor: "#1A1A1A",
fontFamily: theme?.typography?.fontFamily,
// maxWidth: window.innerWidth > 1366 ? 1366 : isMobile ? "100%" : 1200,
paddingLeft: leftSideBarOpenByClick ? 200 : 0,
transition: "padding-left 0.3s ease",
};
@@ -640,6 +646,7 @@ const DropzoneWrapper = memo(({ onDrop, WorkflowView }) => {
const Workflows2 = (props) => {
const { globalUrl, isLoggedIn, isLoaded, userdata, checkLogin } = props;
const { leftSideBarOpenByClick } = useContext(Context);
const location = useLocation();
const navigate = useNavigate();
const [currTab, setCurrTab] = useState(0);
@@ -708,7 +715,6 @@ const Workflows2 = (props) => {
const [apps, setApps] = React.useState([]);
document.title = "Shuffle - Workflows";
const handleTabChange = (event, newValue) => {
// Set loading when switching to public workflows tab
@@ -1516,24 +1522,25 @@ const Workflows2 = (props) => {
};
const paperAppStyle = {
minHeight: 130,
maxHeight: 130,
minHeight: 146,
maxHeight: 146,
overflow: "hidden",
width: "100%",
color: "white",
padding: "12px 12px 0px 15px",
display: "flex",
fontFamily: theme?.typography?.fontFamily,
boxSizing: "border-box",
position: "relative",
borderRadius: theme.palette?.borderRadius,
backgroundColor: theme.palette.surfaceColor,
borderRadius: "8px",
// backgroundColor: "#212121",
};
const gridContainer = {
height: "auto",
color: "white",
margin: "10px",
backgroundColor: theme.palette.surfaceColor,
backgroundColor: "#212121",
position: "relative",
};
@@ -1542,6 +1549,7 @@ const Workflows2 = (props) => {
width: 160,
height: 44,
justifyContent: "space-between",
fontFamily: theme?.typography?.fontFamily,
};
const exportAllWorkflows = (allWorkflows) => {
@@ -2107,6 +2115,80 @@ const Workflows2 = (props) => {
return appsFound
}
const WorkflowSkeleton = () => {
return (
<Paper elevation={0} style={{
backgroundColor: "#212121",
width: "100%",
height: 120,
borderRadius: 8,
}}>
<div style={{
display: "flex",
padding: 10,
width: "100%",
height: "100%"
}}>
<Skeleton
variant="rectangular"
width={100}
height={90}
style={{
borderRadius: 6,
backgroundColor: "rgba(255, 255, 255, 0.1)"
}}
/>
<div style={{
display: "flex",
flexDirection: "column",
marginLeft: 10,
flex: 1,
gap: 6
}}>
<Skeleton
variant="text"
width="40%"
height={24}
style={{ backgroundColor: "rgba(255, 255, 255, 0.1)" }}
/>
<Skeleton
variant="text"
width="60%"
height={20}
style={{ backgroundColor: "rgba(255, 255, 255, 0.1)" }}
/>
<Skeleton
variant="text"
width="30%"
height={20}
style={{ backgroundColor: "rgba(255, 255, 255, 0.1)" }}
/>
</div>
</div>
</Paper>
);
};
// Replace the loading sections in the main component with this
const LoadingWorkflowGrid = () => {
return (
<div style={{
marginTop: 16,
width: "100%",
display: "grid",
gridTemplateColumns: "repeat(auto-fill, minmax(365px, 1fr))",
gap: "20px",
justifyContent: "space-between",
alignItems: "start",
padding: "0 10px"
}}>
{[...Array(7)].map((_, index) => (
<WorkflowSkeleton key={index} />
))}
</div>
);
};
const WorkflowPaper = (props) => {
const { data, type = "org" } = props;
const [open, setOpen] = React.useState(false);
@@ -2283,8 +2365,9 @@ const Workflows2 = (props) => {
marginRight: 10,
};
image =
foundOrg.image === "" ? (
foundOrg.image === "" || foundOrg.image === null || foundOrg.image === undefined ? (
<img
alt={foundOrg.name}
src={theme.palette.defaultImage}
@@ -2336,8 +2419,30 @@ const Workflows2 = (props) => {
}
}
if (type === "public" && currTab === 2) {
const imageStyle = {
width: 24,
height: 24,
marginRight: 10,
border: "1px solid rgba(255,255,255,0.3)",
}
image = data.creator_info !== undefined && data.creator_info !== null && data.creator_info.image !== undefined && data.creator_info.image !== null && data.creator_info.image.length > 0 ? <Avatar alt={data.creator} src={data.creator_info.image} style={imageStyle} /> : <Avatar alt={"shuffle_image"} src={theme.palette.defaultImage} style={imageStyle} />
const creatorname = data.creator_info !== undefined && data.creator_info !== null && data.creator_info.username !== undefined && data.creator_info.username !== null && data.creator_info.username.length > 0 ? data.creator_info.username : "Shuffle"
if ((data.objectID === undefined || data.objectID === null) && data.id !== undefined && data.id !== null) {
data.objectID = data.id
}
//console.log("IMG: ", data)
var parsedUrl = `/workflows/${data.objectID}`
if (data.__queryID !== undefined && data.__queryID !== null) {
parsedUrl += `?queryID=${data.__queryID}`
}
}
return (
<div style={{ width: "100%", minWidth: 320, position: "relative", border: highlightIds.includes(data.id) ? "2px solid #f85a3e" : isDistributed || hasSuborgs ? "1px solid #40E0D0" : "inherit", borderRadius: theme.palette?.borderRadius, }}>
<div style={{ width: "100%", minWidth: 320, position: "relative", border: highlightIds.includes(data.id) ? "2px solid #f85a3e" : isDistributed || hasSuborgs ? "1px solid #40E0D0" : "inherit", borderRadius: theme.palette?.borderRadius, backgroundColor: "#212121", fontFamily: theme?.typography?.fontFamily }}>
<Paper square style={paperAppStyle}>
{selectedCategory !== "" ?
<Tooltip title={`Usecase Category: ${selectedCategory}`} placement="bottom">
@@ -2351,6 +2456,7 @@ const Workflows2 = (props) => {
width: 3,
backgroundColor: boxColor,
borderRadius: "0 100px 0 0",
fontFamily: theme?.typography?.fontFamily,
}}
onClick={() => {
addFilter(selectedCategory)
@@ -2360,7 +2466,7 @@ const Workflows2 = (props) => {
: null}
<Grid
item
style={{ display: "flex", flexDirection: "column", width: "100%" }}
style={{ display: "flex", flexDirection: "column", width: "100%", fontFamily: theme?.typography?.fontFamily }}
>
<Grid item style={{ display: "flex", maxHeight: 34 }}>
<Tooltip title={`Org "${orgName}". Click to edit image.`} placement="bottom">
@@ -2389,7 +2495,7 @@ const Workflows2 = (props) => {
maxWidth: 310,
padding: "12px 0",
}}>
{data.image !== undefined && data.image !== null && data.image.length > 0 ? (
{(data?.image !== undefined || data?.image_url !== undefined) ? (
<div style={{
marginBottom: 15,
borderRadius: theme.palette?.borderRadius,
@@ -2397,8 +2503,8 @@ const Workflows2 = (props) => {
border: `1px solid ${theme.palette.inputColor}`,
}}>
<img
src={data.image}
alt={data.name}
src={data?.image || data?.image_url}
alt={data?.name}
style={{
backgroundColor: theme.palette.surfaceColor,
width: "100%",
@@ -2411,6 +2517,8 @@ const Workflows2 = (props) => {
<Typography style={{
color: "rgba(255,255,255,0.9)",
fontSize: "16px",
fontFamily: theme?.typography?.fontFamily,
}}>
Edit: {data.name}
</Typography>
@@ -2443,10 +2551,14 @@ const Workflows2 = (props) => {
paddingBottom: 0,
maxHeight: 30,
flex: 10,
fontFamily: theme?.typography?.fontFamily,
fontWeight: 500,
}}
>
<Link
to={"/workflows/" + data.id}
to={
type === "public" ? parsedUrl : data.workflow_as_code ? `/workflows/${data.id}/code` : `/workflows/${data.id}`
}
style={{ textDecoration: "none", color: "inherit" }}
>
{parsedName}
@@ -2620,8 +2732,9 @@ const Workflows2 = (props) => {
style={{
justifyContent: "left",
overflow: "hidden",
marginTop: 5,
maxHeight: 28,
marginTop: 8,
maxHeight: 35,
fontFamily: theme?.typography?.fontFamily,
}}
>
{data.tags !== undefined && data.tags !== null
@@ -3079,11 +3192,7 @@ const Workflows2 = (props) => {
</Typography>
</span>
</Tooltip>
<Tooltip
color="primary"
title="Subflows used"
placement="bottom"
>
<Tooltip color="primary" title="Subflows used" placement="bottom">
<span
style={{
marginLeft: 15,
@@ -3235,7 +3344,7 @@ const Workflows2 = (props) => {
);
}
return (
<div style={gridContainer}>
<div style={{ ...gridContainer, backgroundColor: "#212121" }}>
<Tooltip title={`New Workflow`} placement="bottom">
<IconButton
style={{ position: "absolute", top: 10, right: 50, zIndex: 1000 }}
@@ -3797,9 +3906,9 @@ const Workflows2 = (props) => {
// }
useEffect(() => {
if (userdata !== undefined && userdata !== null) {
var filteredWorkflows = []
if (userdata !== undefined && userdata !== null && currTab !== 2) {
setIsLoadingWorkflow(true);
var filteredWorkflows = []
if (currTab === 0) {
filteredWorkflows = workflows.filter(workflow => workflow?.org_id === userdata?.active_org?.id)
}
@@ -3807,7 +3916,10 @@ const Workflows2 = (props) => {
filteredWorkflows = workflows.filter(workflow => workflow?.org_id === userdata?.active_org?.id && workflow?.owner === userdata?.id)
}
setFilteredWorkflows(filteredWorkflows)
setTimeout(() => {
setIsLoadingWorkflow(false);
}, 500);
}
}, [currTab, workflows, userdata])
@@ -3819,25 +3931,29 @@ const Workflows2 = (props) => {
var counted = 0
console.log("Public workflows", hits)
if (isLoadingPublicWorkflow) {
return (
<div style={{ textAlign: "center", marginTop: 50 }}>
<CircularProgress style={{ color: "#f85a3e" }} />
</div>
);
}
return (
isLoadingPublicWorkflow ?
(
<LoadingWorkflowGrid />
) : (
<div style={{
display: "grid",
gridTemplateColumns: "repeat(3, 1fr)", // Creates 3 equal columns
gap: "20px", // Adds consistent spacing between items
marginTop: 16,
width: "100%",
display: "grid",
gridTemplateColumns: "repeat(auto-fill, minmax(365px, 1fr))",
gap: "20px",
justifyContent: "space-between",
alignItems: "start",
padding: "0 10px",
paddingBottom: 40
}}>
{hits.map((data, index) => {
return <WorkflowPaper key={index} data={data} type="public" />
})}
</div>
)
)
}
@@ -3849,6 +3965,21 @@ const Workflows2 = (props) => {
setSelectedCategory(e.target.value)
}
const iconButtonStyle = {
color: 'white',
backgroundColor: '#212121',
borderRadius: '4px',
padding: "12px 16px",
cursor: 'pointer',
minWidth: '40px',
height: 'auto',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}
const WorkflowView = memo(() => {
if (workflows.length === 0) {
}
@@ -3858,47 +3989,98 @@ const Workflows2 = (props) => {
const foundPriority = userdata === undefined || userdata === null || userdata.priorities === undefined || userdata.priorities === null ? null : userdata.priorities.find(prio => prio.type === "usecase" && prio.active === true)
return (
<div style={viewStyle}>
<div style={workflowViewStyle}>
<div style={{ display: "flex", marginTop: 25, }}>
{/* <div style={{ flex: 1 }}>
<Typography variant="h1" style={{fontSize: 30}}>
<>
<div style={{
color: "white",
display: "flex",
flexDirection: "column",
width: "100%",
maxWidth: "70%",
margin: "auto",
}}>
<Typography variant="h4" style={{ marginBottom: 20, paddingLeft: 15, textTransform: 'none', fontFamily: theme?.typography?.fontFamily }}>
Workflows
</Typography>
</div> */}
{/*
<div style={{ flex: 1 }}>
<Typography style={{ marginTop: 7, marginBottom: "auto" }}>
<a
rel="noopener noreferrer"
target="_blank"
href="https://shuffler.io/docs/workflows"
style={{ textDecoration: "none", color: "#f85a3e" }}
<div style={{ borderBottom: '1px solid gray', marginBottom: 30 }}>
<Tabs
value={currTab}
onChange={handleTabChange}
TabIndicatorProps={{ style: { height: '3px', borderRadius: 10, backgroundColor: "#FF8544" } }}
style={{ fontFamily: theme?.typography?.fontFamily }}
>
Learn more about Workflows
</a>
</Typography>
<Tab label="Organization Workflows" style={{ textTransform: 'none', marginRight: 20, fontFamily: theme?.typography?.fontFamily }} />
<Tab label="My Workflows" style={{ textTransform: 'none', marginRight: 20, fontFamily: theme?.typography?.fontFamily }} />
<Tab label="Discover Workflows" style={{ textTransform: 'none', fontFamily: theme?.typography?.fontFamily }} />
</Tabs>
</div>
*/}
{/* {isMobile ? null :
<div style={{ display: "flex", margin: "0px 0px 20px 0px" }}>
<div style={{ flex: 1, float: "right" }}>
<div style={{ display: "flex", justifyContent: "space-between", gap: 10, marginBottom: 20, paddingRight: 25, minHeight: 47 }}>
<MuiChipsInput
style={{}}
style={{
width: "25%",
maxWidth: "25%",
minWidth: "25%",
height: 43,
maxHeight: "fit-content",
backgroundColor: "#212121",
zIndex: 1000,
}}
disabled={currTab === 2}
InputProps={{
style: {
color: "white",
maxWidth: 275,
minWidth: 275,
height: "fit-content",
maxHeight: "fit-content",
backgroundColor: "#212121",
},
placeholder: "Filter Workflows",
// endAdornment: (
// <InputAdornment position="end">
// <SearchIcon style={{ color: 'white', paddingRight: 5 }} />
// </InputAdornment>
// ),
onKeyDown: (e) => {
// Prevent default behavior for Enter and Backspace
if (e.key === 'Enter' || e.key === 'Backspace') {
e.preventDefault();
e.stopPropagation();
e.target.focus();
}
},
}}
rows={1}
placeholder="Filter Workflows"
color="primary"
fullWidth
clearInputOnBlur={false}
sx={{
// Container styling
'& .MuiOutlinedInput-root': {
height: "fit-content",
borderRadius: '4px',
backgroundColor: '#212121',
'& fieldset': {
borderColor: 'rgba(255, 255, 255, 0.23)',
},
'&:hover fieldset': {
borderColor: 'rgba(255, 255, 255, 0.4)',
},
},
// Adjust chip container to center vertically
'& .MuiInputBase-root': {
display: 'flex',
flexWrap: 'wrap',
gap: '4px',
padding: '4px 8px',
alignItems: 'center',
height: "fit-content", // Match height
},
// Rest of the styling remains the same...
}}
value={filters}
onChange={(chips) => {
console.log("CHANGE: ", chips);
setFilters(chips);
findWorkflow(chips);
}}
@@ -3911,180 +4093,6 @@ const Workflows2 = (props) => {
// removeFilter(index);
//}}
/>
</div>
</div>
} */}
{/* <div style={{ flex: 1, textAlign: "right", }}>
{workflowButtons}
</div> */}
</div>
{/* <div style={{width: "100%", minHeight: isMobile ? 0 : hasWorkflows ? 0 : 51, maxHeight: isMobile ? 0 : 51, marginTop: 10, }}>
{!isMobile && !hasWorkflows && usecases !== null && usecases !== undefined && usecases.length > 0 ?
<div style={{ display: "flex", }}>
{usecases.map((usecase, index) => {
if (usecase.name === "5. Verify") {
return null
}
const percentDone = usecase.matches.length > 0 ? parseInt(usecase.matches.length/usecase.list.length*100) : 0
if (percentDone === 0) {
usecase = findMatches(usecase, workflows)
}
return (
<Paper
key={usecase.name}
style={{
flex: 1,
backgroundImage: `linear-gradient(to right, ${usecase.color}, ${usecase.color} ${percentDone}%, transparent ${percentDone}%, transparent 100%)`,
backgroundColor: filters.includes(usecase.name.toLowerCase()) ? null : theme.palette.surfaceColor,
borderRadius: theme.palette?.borderRadius,
marginRight: index === usecases.length-1 ? 0 : 10,
cursor: "pointer",
border: `2px solid ${usecase.color}`,
overflow: "hidden",
padding: 10,
}}
onClick={() => {
console.log("Filters: ", filters, usecase.name.toLowerCase())
if (!filters.includes(usecase.name.toLowerCase())) {
addFilter(usecase.name)
} else {
removeFilter(filters.indexOf(usecase.name.toLowerCase()))
}
}}
>
<span style={{ textDecoration: "none", display: "flex", }}>
<Typography variant="body1" color="textPrimary" style={{flex: 4, }}>
{usecase.name}
</Typography>
<Typography variant="body2" color="textSecondary" style={{flex: 1, marginTop: 0,}}>
{usecase.matches.length}/{usecase.list.length}
</Typography>
</span>
</Paper>
)
})}
</div>
: null}
</div> */}
{userdata.priorities !== undefined && userdata.priorities !== null && userdata.priorities.length > 0 && userdata.priorities[0].name.includes("CPU") && userdata.priorities[0].active === true ?
<div style={{
border: "1px solid rgba(255,255,255,0.1)", borderRadius: theme.palette?.borderRadius, marginTop: 10,
marginBottom: 10, padding: 15, textAlign: "center", height: 70, textAlign: "left", backgroundColor:
theme.palette.surfaceColor, display: "flex", maxHeight: "105px", minHeight: "110px"
}}
>
<div style={{ flex: 2, overflow: "hidden", }}>
<Typography variant="body1" >
{userdata.priorities[0].name}
</Typography>
<div style={{ flex: "2 1 0%", overflow: "hidden" }}>
<span style={{ display: "flex", marginTop: "10px" }}>
<Typography variant="body2" color="textSecondary" style={{ marginTop: "3px" }}>
{userdata.priorities[0].description}
</Typography>
</span>
</div>
</div>
<div style={{ flex: 1, display: "flex", marginLeft: 30, }}>
<Button style={{ height: 50, borderRadius: 25, marginTop: 8, width: 175, backgroundColor: "rgba(255,255,255,0.8)" }} variant="contained" color="secondary" onClick={() => { navigate(userdata.priorities[0].url) }}>
explore
</Button>
{/*
<Button style={{borderRadius: 25, width: 200, height: 50, marginTop: 8, }} variant="text" color="secondary">
Ignore
</Button>
*/}
</div>
</div>
: null}
{/* {foundPriority != null && workflows.length < 6 ?
<Priority
globalUrl={globalUrl}
userdata={userdata}
priority={foundPriority}
checkLogin={checkLogin}
appFramework={appFramework}
/>
: null} */}
<div style={{
color: "white",
display: "flex",
flexDirection: "column",
width: "100%",
marginTop: 50,
margin: "auto",
// maxWidth: "80%",
}}>
<Typography variant="h4" style={{ marginBottom: 20, paddingLeft: 15 }}>
Workflows
</Typography>
<div style={{ borderBottom: '1px solid gray', marginBottom: 30 }}>
<Tabs
value={currTab}
onChange={handleTabChange}
TabIndicatorProps={{ style: { height: '3px', borderRadius: 10 } }}
>
<Tab label="Organization Workflows" style={{ textTransform: 'none', marginRight: 20 }} />
<Tab label="My Workflows" style={{ textTransform: 'none', marginRight: 20 }} />
<Tab label="Discover Workflows" style={{ textTransform: 'none' }} />
</Tabs>
</div>
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 20, gap: 10 }}>
<MuiChipsInput
placeholder="Filter Workflows"
variant="outlined"
color="primary"
value={filters}
onChange={(chips) => {
console.log("CHANGE: ", chips);
setFilters(chips);
findWorkflow(chips);
}}
style={{ flex: 0.9 }}
InputProps={{
style: {
color: "white",
borderRadius: 8,
},
}}
sx={{
'& .MuiOutlinedInput-root': {
'& fieldset': {
borderColor: 'rgba(255, 255, 255, 0.2)',
},
'&:hover fieldset': {
borderColor: 'rgba(255, 255, 255, 0.3)',
},
'&.Mui-focused fieldset': {
borderColor: '#f85a3e',
},
},
'& .MuiChip-root': {
backgroundColor: 'rgba(255, 255, 255, 0.1)',
color: 'white',
'& .MuiChip-deleteIcon': {
color: 'rgba(255, 255, 255, 0.7)',
'&:hover': {
color: 'white',
},
},
},
}}
/>
<Select
fullWidth
@@ -4092,8 +4100,23 @@ const Workflows2 = (props) => {
value={selectedCategory}
onChange={handleCategoryChange}
displayEmpty
disabled={currTab === 2}
multiple
style={{ flex: 0.9, maxWidth: 350, borderRadius: 8 }}
style={{
width: "25%",
minWidth: "25%",
maxWidth: "25%",
height: 47,
borderRadius: 4,
backgroundColor: "#212121",
}}
sx={{
'& .MuiOutlinedInput-root': {
'& fieldset': {
borderColor: 'rgba(255, 255, 255, 0.23)',
},
},
}}
renderValue={(selected) => selected.length ? selected.join(', ') : 'All Categories'}
>
<MenuItem disabled value="" style={{}}>All Categories</MenuItem>
@@ -4174,62 +4197,50 @@ const Workflows2 = (props) => {
})}
</Select>
<div style={{ display: "flex", gap: 8, height: "100%" }}>
<div style={{ width: "50%", minWidth: "50%", maxWidth: "50%", height: 47, display: "flex", gap: 5 }}>
<div style={{
display: "flex", height: "100%",
justifyContent: "space-around",
flex: 0.7,
paddingLeft: 1,
paddingRight: 1,
gap: 4
}}>
<Tooltip title="Explore Workflow Runs" placement="top">
<IconButton
style={{
color: 'white',
backgroundColor: '#212121',
borderRadius: '8px',
padding: '6px',
width: '55px',
height: '55px',
}}
style={iconButtonStyle}
onClick={() => navigate("/workflows/debug")}
disabled={currTab === 2}
>
<QueryStatsIcon />
<QueryStatsIcon style={{ color: "#F1F1F1" }} />
</IconButton>
</Tooltip>
<Tooltip title={view === "grid" ? "Grid view" : "List view"} placement="top">
<Tooltip title={view === "grid" ? "List view" : "Grid view"} placement="top">
<IconButton
style={{
color: 'white',
backgroundColor: '#212121',
borderRadius: '8px',
padding: '6px',
width: '55px',
height: '55px',
}}
style={iconButtonStyle}
onClick={() => {
if (view === "grid") {
localStorage.setItem("workflowView", "list");
setView("list");
} else {
localStorage.setItem("workflowView", "grid");
setView("grid");
}
const newView = view === "grid" ? "list" : "grid";
localStorage.setItem("workflowView", newView);
setView(newView);
}}
disabled={currTab === 2}
>
{
view === "list" ? <ListIcon /> : <GridOnIcon />
}
{view === "grid" ? <ListIcon /> : <GridOnIcon />}
</IconButton>
</Tooltip>
<Tooltip title="Import workflows" placement="top">
<IconButton
style={{
color: 'white',
backgroundColor: '#212121',
borderRadius: '8px',
padding: '6px',
width: '55px',
height: '55px',
}}
style={iconButtonStyle}
onClick={() => upload.click()}
disabled={currTab === 2}
>
{submitLoading ? <CircularProgress color="secondary" /> : <PublishIcon />}
</IconButton>
</Tooltip>
<input
hidden
type="file"
@@ -4237,62 +4248,60 @@ const Workflows2 = (props) => {
ref={(ref) => (upload = ref)}
onChange={importFiles}
/>
<Tooltip title={`Download ALL workflows (${workflows.length})`}
placement="top">
<Tooltip title={`Download ALL workflows (${workflows.length})`} placement="top">
<IconButton
style={{
color: 'white',
backgroundColor: '#212121',
borderRadius: '8px',
padding: '6px',
width: '55px',
height: '55px',
}}
disabled={isCloud}
variant="text"
onClick={() => {
exportAllWorkflows(workflows);
}}
style={{ ...iconButtonStyle, cursor: "pointer" }}
disabled={isCloud || currTab === 2}
onClick={() => exportAllWorkflows(workflows)}
>
<GetAppIcon />
</IconButton>
</Tooltip>
</div>
<Button
variant="contained"
color="primary"
onClick={handleCreateWorkflow}
style={{
height: "56px",
borderRadius: 8,
borderRadius: 4,
flex: 0.8,
textTransform: 'none',
backgroundColor: "#FF8544",
color: "#1A1A1A",
fontFamily: theme?.typography?.fontFamily,
fontSize: 16,
fontWeight: 500
}}
startIcon={<Add style={{ color: "#1A1A1A" }} />}
>
<Add style={{ marginRight: 10 }} />
Create Workflow
</Button>
</div>
</div>
<div style={{
width: "100%",
position: "relative",
zIndex: 1
}}>
{
isLoadingWorkflow ? (
<div style={{ textAlign: "center", marginTop: 50 }}>
<CircularProgress style={{ color: "#f85a3e" }} />
</div>
<LoadingWorkflowGrid/>
) : (
view === "grid" && currTab !== 2 ? (
<>
<div style={{
display: "grid",
gridTemplateColumns: "repeat(3, 1fr)", // Creates 3 equal columns
gap: "20px", // Adds consistent spacing between items
marginTop: 16,
width: "100%",
display: "grid",
gridTemplateColumns: "repeat(auto-fill, minmax(365px, 1fr))",
gap: "20px",
justifyContent: "space-between",
alignItems: "start",
padding: "0 10px",
paddingBottom: 40
}}>
@@ -4330,7 +4339,7 @@ const Workflows2 = (props) => {
(
<InstantSearch searchClient={searchClient} indexName="workflows">
<Configure clickAnalytics />
<CustomHits hitsPerPage={5} />
<CustomHits hitsPerPage={5} type="public" />
</InstantSearch>
)
}
@@ -4338,19 +4347,8 @@ const Workflows2 = (props) => {
</div>
</div>
</>
{/* {foundPriority != null && filteredWorkflows.length > 6 ?
<Priority
style={{marginTop: 15, }}
globalUrl={globalUrl}
priority={foundPriority}
checkLogin={checkLogin}
appFramework={appFramework}
/>
: null} */}
</div>
</div>
);
});
@@ -4758,7 +4756,7 @@ const Workflows2 = (props) => {
);
// Maybe use gridview or something, idk
return <div>{loadedCheck}</div>;
return <div style={{zoom: 0.8, }}>{loadedCheck}</div>;
};
+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) {