Synced over a lot of sidebar changes

This commit is contained in:
Frikky
2024-12-04 17:01:33 +01:00
parent 32264ad5fa
commit 24a8c18eb5
30 changed files with 2811 additions and 1831 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 [actions, setActions] = useState([]);
const [info, setInfo] = useState({}); const [info, setInfo] = useState({});
const [serverurl, setServerUrl] = useState(""); const [serverurl, setServerUrl] = useState("");
@@ -1265,12 +1265,12 @@ const ApiExplorer = memo(({ openapi, globalUrl, userdata, HandleApiExecution, se
style={{ style={{
display: "flex", display: "flex",
flexDirection: 'row', 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> </div>
); );
@@ -1279,7 +1279,7 @@ const ApiExplorer = memo(({ openapi, globalUrl, userdata, HandleApiExecution, se
export default ApiExplorer; 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 [apiResponse, setApiResponse] = useState({});
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const loadAction = 10; const loadAction = 10;
@@ -1366,7 +1366,7 @@ const ActionResponseAndRequest = memo(({ConfigurationTab, selectedAppData,action
))} ))}
</div> </div>
<ActionResponse userdata={userdata} apiResponse={apiResponse} ExampleBody={ExampleBody}/> <ActionResponse isLoaded={isLoaded} isLoggedIn={isLoggedIn} apiResponse={apiResponse} ExampleBody={ExampleBody}/>
</div> </div>
)}) )})
@@ -1381,6 +1381,8 @@ const ActionsList = memo(({
userdata, userdata,
info, info,
openapi, openapi,
isLoggedIn,
isLoaded
}) => { }) => {
const [searchQuery, setSearchQuery] = useState(""); const [searchQuery, setSearchQuery] = useState("");
@@ -1436,7 +1438,7 @@ const ActionsList = memo(({
} }
}; };
return ( 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 style={{ borderBottom: '1px solid #494949', paddingTop: 10, paddingBottom: 10}}>
<div> <div>
{info?.title ? ( {info?.title ? (
@@ -1487,7 +1489,7 @@ const ActionsList = memo(({
marginTop: 15, marginTop: 15,
backgroundColor: "#1a1a1a", backgroundColor: "#1a1a1a",
overflowY: "auto", overflowY: "auto",
height: userdata?.support ? "calc(100vh - 190px)" : "calc(100vh - 260px)", height: (isLoaded && isLoggedIn) ? "calc(100vh - 190px)" : "calc(100vh - 260px)",
paddingRight: 5, 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 [height, setHeight] = useState("14vh")
const [responseTabIndex, setResponseTabIndex] = useState(0) const [responseTabIndex, setResponseTabIndex] = useState(0)
const [oldResponse, setOldResponse] = useState(apiResponse) const [oldResponse, setOldResponse] = useState(apiResponse)
@@ -2897,7 +2899,7 @@ const ActionResponse = memo(({ apiResponse, ExampleBody, userdata }) => {
}, []); }, []);
return ( return (
<ApiResponseWrapper userdata={userdata}> <ApiResponseWrapper isLoggedIn={isLoggedIn} isLoaded={isLoaded}>
<div <div
style={{ style={{
width: '100%', 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); const { leftSideBarOpenByClick, windowWidth } = useContext(Context);
return ( return (
<div <div
style={{ style={{
width: userdata?.support width: (isLoggedIn && isLoaded)
? leftSideBarOpenByClick ? leftSideBarOpenByClick
? windowWidth >= 1920 ? "calc(100% - 630px)" : "calc(100% - 570px)" ? windowWidth >= 1920 ? "calc(100% - 630px)" : "calc(100% - 570px)"
: windowWidth >= 1920 ? "calc(100vw - 460px)": "calc(100% - 410px)" : 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 ( return (
<PaddingWrapper userdata={userdata}> <PaddingWrapper isLoggedIn={isLoggedIn} isLoaded={isLoaded}>
{children} {children}
</PaddingWrapper> </PaddingWrapper>
); );
+174 -29
View File
@@ -1,4 +1,6 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { useNavigate } from 'react-router';
import { import {
Dialog, Dialog,
DialogTitle, DialogTitle,
@@ -21,17 +23,22 @@ import LaunchIcon from '@mui/icons-material/Launch';
import CheckCircleIcon from '@mui/icons-material/CheckCircle'; import CheckCircleIcon from '@mui/icons-material/CheckCircle';
import { CloudDownloadOutlined } from '@mui/icons-material'; import { CloudDownloadOutlined } from '@mui/icons-material';
import { findSpecificApp } from './AppFramework'; 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 [frameworkData, setFrameworkData] = useState({})
const [userdata, setUserdata] = useState({})
const [usecases, setUsecases] = useState([]) const [usecases, setUsecases] = useState([])
const [workflows, setWorkflows] = useState([]) const [workflows, setWorkflows] = useState([])
const [prevSubcase, setPrevSubcase] = useState({}) const [prevSubcase, setPrevSubcase] = useState({})
const [inputUsecase, setInputUsecase] = useState({}) const [inputUsecase, setInputUsecase] = useState({})
const [latestUsecase, setLatestUsecase] = useState([]) const [latestUsecase, setLatestUsecase] = useState([])
const [foundAppUsecase, setFoundAppUsecase] = useState({}) const [foundAppUsecase, setFoundAppUsecase] = useState({})
const navigate = useNavigate();
const parseUsecase = (subcase) => { const parseUsecase = (subcase) => {
const srcdata = findSpecificApp(frameworkData, subcase.type) const srcdata = findSpecificApp(frameworkData, subcase.type)
const dstdata = findSpecificApp(frameworkData, subcase.last) const dstdata = findSpecificApp(frameworkData, subcase.last)
@@ -48,6 +55,25 @@ const AppModal = ({ open, onClose, app, userdata, globalUrl }) => {
return subcase 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 = () => { const getFramework = () => {
fetch(globalUrl + "/api/v1/apps/frameworkConfiguration", { fetch(globalUrl + "/api/v1/apps/frameworkConfiguration", {
@@ -266,6 +292,89 @@ const AppModal = ({ open, onClose, app, userdata, globalUrl }) => {
setFoundAppUsecase(foundSubcase); setFoundAppUsecase(foundSubcase);
}, [latestUsecase]) }, [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 = const isCloud =
window.location.host === "localhost:3002" || window.location.host === "localhost:3002" ||
window.location.host === "shuffler.io" || window.location.host === "localhost:3000" window.location.host === "shuffler.io" || window.location.host === "localhost:3000"
@@ -280,7 +389,8 @@ const AppModal = ({ open, onClose, app, userdata, globalUrl }) => {
newAppname = newAppname?.replaceAll("_", " "); 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 ( return (
@@ -294,7 +404,7 @@ const AppModal = ({ open, onClose, app, userdata, globalUrl }) => {
borderRadius: 2, borderRadius: 2,
border: "1px solid #494949", border: "1px solid #494949",
minWidth: '440px', minWidth: '440px',
fontFamily: "Inter", fontFamily: theme?.typography?.fontFamily,
backgroundColor: "#212121", backgroundColor: "#212121",
'& .MuiDialogContent-root': { '& .MuiDialogContent-root': {
backgroundColor: "#212121", backgroundColor: "#212121",
@@ -303,10 +413,10 @@ const AppModal = ({ open, onClose, app, userdata, globalUrl }) => {
backgroundColor: "#212121", backgroundColor: "#212121",
}, },
'& .MuiTypography-root': { '& .MuiTypography-root': {
fontFamily: 'Inter, sans-serif', fontFamily: theme?.typography?.fontFamily,
}, },
'& .MuiButton-root': { '& .MuiButton-root': {
fontFamily: 'Inter, sans-serif', fontFamily: theme?.typography?.fontFamily,
}, },
} }
}} }}
@@ -320,7 +430,7 @@ const AppModal = ({ open, onClose, app, userdata, globalUrl }) => {
pt: 2, pt: 2,
pl: 3, pl: 3,
pr: 2, pr: 2,
fontFamily: "Inter" fontFamily: theme?.typography?.fontFamily
}} }}
> >
<Typography component="div" sx={{ fontWeight: 500, color: "#F1F1F1", fontSize: "22px" }}> <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 }}> <DialogContent sx={{ py: 3, px: 3 }}>
<Box sx={{ display: 'flex', alignItems: 'space-between', justifyContent: 'space-between' }}> <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 <img
alt={app?.name} alt={app?.name}
src={app?.large_image || app?.image_url} src={app?.large_image || app?.image_url}
@@ -364,11 +474,9 @@ const AppModal = ({ open, onClose, app, userdata, globalUrl }) => {
</Typography> </Typography>
{ {
isCloud && ( isCloud && (
<a <Link
rel="noopener noreferrer" to={"/apps/" + (app?.id || app?.objectID)}
href={"https://shuffler.io/apps/" + app?.id}
style={{ textDecoration: "none", color: "#f85a3e", marginTop: "-2px" }} style={{ textDecoration: "none", color: "#f85a3e", marginTop: "-2px" }}
target="_blank"
> >
<IconButton <IconButton
style={{ style={{
@@ -378,7 +486,7 @@ const AppModal = ({ open, onClose, app, userdata, globalUrl }) => {
> >
<OpenInNewIcon /> <OpenInNewIcon />
</IconButton> </IconButton>
</a> </Link>
) )
} }
</div> </div>
@@ -391,6 +499,11 @@ const AppModal = ({ open, onClose, app, userdata, globalUrl }) => {
</div> </div>
</div> </div>
<div style={{ display: "flex", flexDirection: "row", justifyContent: "center", alignItems: "center", gap: 10 }}> <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 <Button
variant="contained" variant="contained"
sx={{ sx={{
@@ -403,11 +516,16 @@ const AppModal = ({ open, onClose, app, userdata, globalUrl }) => {
height: '40px', height: '40px',
padding: 2, padding: 2,
color: "#fff", color: "#fff",
fontFamily: "Inter" fontFamily: theme?.typography?.fontFamily
}}
onClick={(event) => {
event.preventDefault();
event.stopPropagation();
downloadApp(app);
}} }}
> >
<CloudDownloadOutlined /> <CloudDownloadOutlined />
</Button> </Button>) : null}
<Button <Button
variant="contained" variant="contained"
sx={{ sx={{
@@ -419,9 +537,21 @@ const AppModal = ({ open, onClose, app, userdata, globalUrl }) => {
px: 3, px: 3,
height: '40px', height: '40px',
color: "#fff", 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"} {canEditApp ? "Edit" : "Fork"}
</Button> </Button>
@@ -431,7 +561,7 @@ const AppModal = ({ open, onClose, app, userdata, globalUrl }) => {
<div style={{ <div style={{
display: "flex", display: "flex",
justifyContent: "space-between", justifyContent: "space-between",
fontFamily: "Inter", fontFamily: theme?.typography?.fontFamily,
padding: "26px 0px" padding: "26px 0px"
}}> }}>
<div style={{ <div style={{
@@ -441,7 +571,7 @@ const AppModal = ({ open, onClose, app, userdata, globalUrl }) => {
<Typography <Typography
variant="h6" variant="h6"
sx={{ sx={{
fontFamily: 'Inter, sans-serif', fontFamily: theme?.typography?.fontFamily,
fontSize: '24px', fontSize: '24px',
fontWeight: 600, fontWeight: 600,
mb: 0.3, mb: 0.3,
@@ -454,7 +584,7 @@ const AppModal = ({ open, onClose, app, userdata, globalUrl }) => {
variant="body2" variant="body2"
sx={{ sx={{
color: 'rgba(255, 255, 255, 0.7)', color: 'rgba(255, 255, 255, 0.7)',
fontFamily: 'Inter, sans-serif', fontFamily: theme?.typography?.fontFamily,
fontSize: '14px' fontSize: '14px'
}} }}
> >
@@ -486,7 +616,7 @@ const AppModal = ({ open, onClose, app, userdata, globalUrl }) => {
paddingLeft: "10px", paddingLeft: "10px",
paddingTop: "5px" 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 ? ( app?.collection ? (
<> <>
@@ -494,13 +624,25 @@ const AppModal = ({ open, onClose, app, userdata, globalUrl }) => {
<Typography variant="body1" sx={{ <Typography variant="body1" sx={{
fontWeight: 500, fontWeight: 500,
color: '#fff', color: '#fff',
marginTop: "1px" marginTop: "1px",
fontFamily: theme?.typography?.fontFamily,
fontSize: "16px"
}}> }}>
app.collection app.collection
</Typography> </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> </div>
@@ -517,7 +659,7 @@ const AppModal = ({ open, onClose, app, userdata, globalUrl }) => {
width: "100%" width: "100%"
}}> }}>
<div style={{ <div style={{
fontFamily: "Inter", fontFamily: theme?.typography?.fontFamily,
fontSize: "16px", fontSize: "16px",
color: "#fff", color: "#fff",
marginBottom: "16px", marginBottom: "16px",
@@ -544,7 +686,7 @@ const AppModal = ({ open, onClose, app, userdata, globalUrl }) => {
{ {
foundAppUsecase === undefined ? ( foundAppUsecase === undefined ? (
<Avatar sx={{ width: 32, height: 32, bgcolor: 'background.paper', border: 1, borderColor: 'divider' }}> <Avatar sx={{ width: 32, height: 32, bgcolor: 'background.paper', border: 1, borderColor: 'divider' }}>
<Search sx={{ color: 'text.primary', zIndex: 10, fontSize: 18}} /> <Search sx={{ color: 'text.primary', zIndex: 10, fontSize: 18 }} />
</Avatar> </Avatar>
) : ( ) : (
<Avatar <Avatar
@@ -563,7 +705,7 @@ const AppModal = ({ open, onClose, app, userdata, globalUrl }) => {
{ {
foundAppUsecase === undefined ? ( foundAppUsecase === undefined ? (
<Avatar sx={{ width: 32, height: 32, bgcolor: 'background.paper', border: 1, borderColor: 'divider' }}> <Avatar sx={{ width: 32, height: 32, bgcolor: 'background.paper', border: 1, borderColor: 'divider' }}>
<AddIcon sx={{ color: 'text.primary', zIndex: 10, fontSize: 18}} /> <AddIcon sx={{ color: 'text.primary', zIndex: 10, fontSize: 18 }} />
</Avatar> </Avatar>
) : ( ) : (
<Avatar <Avatar
@@ -586,7 +728,7 @@ const AppModal = ({ open, onClose, app, userdata, globalUrl }) => {
</Box> </Box>
</div> </div>
<div style={{ display: "flex", justifyContent: "center", fontFamily: "Inter" }}> <div style={{ display: "flex", justifyContent: "center", fontFamily: theme?.typography?.fontFamily }}>
<Button <Button
variant="contained" variant="contained"
sx={{ sx={{
@@ -599,11 +741,14 @@ const AppModal = ({ open, onClose, app, userdata, globalUrl }) => {
fontSize: "14px", fontSize: "14px",
letterSpacing: "0.5px", letterSpacing: "0.5px",
color: "black", color: "black",
fontFamily: "Inter", fontFamily: theme?.typography?.fontFamily,
minWidth: '200px' minWidth: '200px'
}} }}
onClick={() => {
navigate("/usecases2")
}}
> >
Create a Usecase Find a Usecase
</Button> </Button>
</div> </div>
</DialogContent> </DialogContent>
+1 -1
View File
@@ -465,7 +465,7 @@ const AppSelection = props => {
style={{ style={{
fontSize: 16, fontSize: 16,
color: "rgba(158, 158, 158, 1)", color: "rgba(158, 158, 158, 1)",
fontFamily: "Inter", fontFamily: theme?.typography?.fontFamily,
}} }}
> >
Your organization has no apps yet, select your starting apps here 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 ReactGA from 'react-ga4';
import theme from "../theme.jsx"; import theme from "../theme.jsx";
@@ -41,7 +41,8 @@ import {
Delete, Delete,
RestaurantRounded, RestaurantRounded,
Cloud, Cloud,
CheckCircle CheckCircle,
Padding,
} from "@mui/icons-material"; } from "@mui/icons-material";
//import { useAlert //import { useAlert
@@ -49,8 +50,9 @@ import { typecost, typecost_single, } from "../views/HandlePaymentNew.jsx";
import BillingStats from "./BillingStats.jsx"; import BillingStats from "./BillingStats.jsx";
import { handlePayasyougo } from "../views/HandlePaymentNew.jsx" import { handlePayasyougo } from "../views/HandlePaymentNew.jsx"
import DeleteIcon from '@mui/icons-material/Delete'; 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 { globalUrl, userdata, serverside, billingInfo, stripeKey, selectedOrganization, handleGetOrg, clickedFromOrgTab } = props;
//const alert = useAlert(); //const alert = useAlert();
let navigate = useNavigate(); let navigate = useNavigate();
@@ -81,18 +83,12 @@ const Billing = (props) => {
} }
}, [userdata]); }, [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); useEffect(() => {
if (BillingEmail !== selectedOrganization?.Billing?.Email) {
useState(() => { setBillingEmail(selectedOrganization?.Billing?.Email);
// Set the billing email }
setBillingEmail(
selectedOrganization.Billing !== undefined &&
selectedOrganization.Billing.Email !== undefined &&
selectedOrganization.Billing.Email.length > 0
? selectedOrganization.Billing.Email
: selectedOrganization.org
);
// Set and sort the alert thresholds // Set and sort the alert thresholds
const alertThresholds = selectedOrganization.Billing !== undefined && const alertThresholds = selectedOrganization.Billing !== undefined &&
@@ -126,7 +122,6 @@ const Billing = (props) => {
]; ];
const handleGetDeals = (orgId) => { const handleGetDeals = (orgId) => {
console.log("Get deals!");
if (orgId.length === 0) { if (orgId.length === 0) {
toast( toast(
@@ -178,7 +173,7 @@ const Billing = (props) => {
width: 340, width: 340,
height: 480, height: 480,
// width: "100%", // width: "100%",
backgroundColor: theme.palette.platformColor, backgroundColor: theme.palette.backgroundColor,
borderRadius: theme.palette?.borderRadius * 2, borderRadius: theme.palette?.borderRadius * 2,
border: "1px solid rgba(255,255,255,0.3)", border: "1px solid rgba(255,255,255,0.3)",
marginRight: 10, marginRight: 10,
@@ -384,7 +379,7 @@ const Billing = (props) => {
} }
if (hovered) { if (hovered) {
newPaperstyle.backgroundColor = theme.palette.surfaceColor newPaperstyle.backgroundColor = "#2b2b2b"
} }
const handleClickOpen = () => { const handleClickOpen = () => {
@@ -412,7 +407,6 @@ const Billing = (props) => {
const HandleChangeBillingEmail = (orgId) => { const HandleChangeBillingEmail = (orgId) => {
const email = newBillingEmail; const email = newBillingEmail;
const emailPattern = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$/; 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)) { if (!emailPattern.test(email)) {
toast("Please enter a valid email address"); toast("Please enter a valid email address");
return; return;
@@ -445,7 +439,6 @@ const Billing = (props) => {
} }
return response.json(); return response.json();
}).then((responseJson) => { }).then((responseJson) => {
console.log("Got org:", responseJson);
if (responseJson.success === true) { if (responseJson.success === true) {
toast.success("Successfully updated billing email"); toast.success("Successfully updated billing email");
setBillingEmail(newBillingEmail); setBillingEmail(newBillingEmail);
@@ -460,7 +453,7 @@ const Billing = (props) => {
} }
return ( return (
<Paper <div
style={newPaperstyle} style={newPaperstyle}
onMouseEnter={() => setHovered(true)} onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)} onMouseLeave={() => setHovered(false)}
@@ -726,7 +719,7 @@ const Billing = (props) => {
</Typography> </Typography>
<div style={{ display: 'flex', flexDirection: 'row' }}> <div style={{ display: 'flex', flexDirection: 'row' }}>
<Typography variant="body2" style={{ marginTop: !userdata.has_card_available ? 5 : 0 }}> <Typography variant="body2" style={{ marginTop: !userdata.has_card_available ? 5 : 0 }}>
Billing email: {BillingEmail} {BillingEmail?.length > 0 ? `Billing email: ${BillingEmail}` : null}
</Typography> </Typography>
{userdata.has_card_available === true && ( {userdata.has_card_available === true && (
<Button <Button
@@ -817,11 +810,11 @@ const Billing = (props) => {
color="primary" color="primary"
style={{ style={{
marginTop: !userdata.has_card_available ? 20 : 10, marginTop: !userdata.has_card_available ? 20 : 10,
borderRadius: 25, borderRadius: 8,
height: 40, height: 40,
fontSize: 16, fontSize: 16,
color: "white", color: userdata.has_card_available ? "#ff8544" : "#1a1a1a",
backgroundColor: userdata.has_card_available ? null : "#f86743", backgroundColor: userdata.has_card_available ? null : "#ff8544",
// backgroundImage: userdata.has_card_available ? null : "linear-gradient(to right, #f86a3e, #f34079)", // backgroundImage: userdata.has_card_available ? null : "linear-gradient(to right, #f86a3e, #f34079)",
textTransform: "none", textTransform: "none",
@@ -850,11 +843,11 @@ const Billing = (props) => {
color="primary" color="primary"
style={{ style={{
marginTop: 10, marginTop: 10,
borderRadius: 25, borderRadius: 8,
height: 40, height: 40,
fontSize: 16, fontSize: 16,
color: "white", color: "#1a1a1a",
backgroundColor: "#f86743", backgroundColor: "#ff8544",
textTransform: 'none' textTransform: 'none'
}} }}
onClick={() => { onClick={() => {
@@ -873,7 +866,6 @@ const Billing = (props) => {
: null} : null}
{showSupport ? {showSupport ?
<Button variant="outlined" color="primary" style={{ marginTop: 20, marginBottom: 10, }} onClick={() => { <Button variant="outlined" color="primary" style={{ marginTop: 20, marginBottom: 10, }} onClick={() => {
console.log("Support click")
if (window.drift !== undefined) { if (window.drift !== undefined) {
//window.drift.api.startInteraction({ interactionId: 340045 }) //window.drift.api.startInteraction({ interactionId: 340045 })
window.drift.api.startInteraction({ interactionId: 340043 }) window.drift.api.startInteraction({ interactionId: 340043 })
@@ -884,7 +876,7 @@ const Billing = (props) => {
Get Support Get Support
</Button> </Button>
: null} : null}
</Paper> </div>
) )
} }
const ConsultationManagement = (props) => { const ConsultationManagement = (props) => {
@@ -1035,12 +1027,12 @@ const Billing = (props) => {
}) })
return ( return (
<Paper style={{ <div style={{
padding: 20, padding: 20,
// maxWidth: 400, // maxWidth: 400,
width: 340, width: 340,
height: 480, height: 480,
backgroundColor: hovered ? "#232427" : theme.palette.platformColor, backgroundColor: hovered ? "#2b2b2b" : theme.palette.backgroundColor,
borderRadius: theme.palette?.borderRadius * 2, borderRadius: theme.palette?.borderRadius * 2,
border: "1px solid rgba(255,255,255,0.3)", border: "1px solid rgba(255,255,255,0.3)",
marginRight: 10, marginRight: 10,
@@ -1131,11 +1123,11 @@ const Billing = (props) => {
color="primary" color="primary"
style={{ style={{
marginTop: userdata.support ? 0 : 10, marginTop: userdata.support ? 0 : 10,
borderRadius: 25, borderRadius: 8,
height: 40, height: 40,
fontSize: 16, fontSize: 16,
color: "white", color: "#1A1A1A",
backgroundColor: "#f86743", backgroundColor: "#FF8544",
textTransform: 'none', textTransform: 'none',
}} }}
onClick={() => { onClick={() => {
@@ -1150,7 +1142,7 @@ const Billing = (props) => {
> >
Buy Buy
</Button> </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 }}> <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. 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> </Typography>
@@ -1158,7 +1150,7 @@ const Billing = (props) => {
<Button <Button
variant="contained" variant="contained"
color="primary" 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={() => { onClick={() => {
if (Cloud) { if (Cloud) {
ReactGA.event({ ReactGA.event({
@@ -1186,10 +1178,10 @@ const Billing = (props) => {
color="primary" color="primary"
style={{ style={{
marginTop: userdata.support ? 5 : 10, marginTop: userdata.support ? 5 : 10,
borderRadius: 25, borderRadius: 8,
height: 40, height: 40,
fontSize: 16, fontSize: 16,
color: "white", color: "#ff8544",
textTransform: 'none', textTransform: 'none',
cursor: getProfessionalServices ? 'pointer' : 'not-allowed', cursor: getProfessionalServices ? 'pointer' : 'not-allowed',
opacity: getProfessionalServices ? 1 : 0.6, opacity: getProfessionalServices ? 1 : 0.6,
@@ -1259,7 +1251,7 @@ const Billing = (props) => {
</Button> </Button>
</DialogContent> </DialogContent>
</Dialog> </Dialog>
</Paper > </div >
) )
} }
@@ -1308,13 +1300,13 @@ const Billing = (props) => {
} }
return ( return (
<Paper <div
style={{ style={{
padding: 20, padding: 20,
height: 480, height: 480,
// maxWidth: 400, // maxWidth: 400,
width: 340, width: 340,
backgroundColor: hovered ? "#232427" : theme.palette.platformColor, backgroundColor: hovered ? "#2b2b2b" : theme.palette.backgroundColor,
borderRadius: theme.palette?.borderRadius * 2, borderRadius: theme.palette?.borderRadius * 2,
border: "1px solid rgba(255,255,255,0.3)", border: "1px solid rgba(255,255,255,0.3)",
marginRight: 10, marginRight: 10,
@@ -1370,11 +1362,12 @@ const Billing = (props) => {
color="primary" color="primary"
style={{ style={{
marginTop: 10, marginTop: 10,
borderRadius: 25, borderRadius: 8,
height: 40, height: 40,
fontSize: 16, fontSize: 16,
color: "white", color: "#1A1A1A",
textTransform: 'none' textTransform: 'none',
backgroundColor: "#FF8544"
}} }}
onClick={() => { onClick={() => {
if (Cloud) { if (Cloud) {
@@ -1397,10 +1390,12 @@ const Billing = (props) => {
color="primary" color="primary"
style={{ style={{
marginTop: 10, marginTop: 10,
borderRadius: 25, borderRadius: 8,
height: 40, height: 40,
fontSize: 16, fontSize: 16,
textTransform: 'none' textTransform: 'none',
color: "#FF8544",
backgroundColor: "#1a1a1a",
}} }}
onClick={() => { onClick={() => {
if (Cloud) { if (Cloud) {
@@ -1425,6 +1420,7 @@ const Billing = (props) => {
style: { style: {
width: 500, width: 500,
margin: 0, margin: 0,
backgroundColor: "rgb(26, 26, 26)",
} }
}} }}
> >
@@ -1464,7 +1460,7 @@ const Billing = (props) => {
<Button <Button
variant="contained" variant="contained"
color="primary" 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} onClick={handlePrivateTraining}
> >
Submit Request Submit Request
@@ -1472,7 +1468,7 @@ const Billing = (props) => {
</DialogContent> </DialogContent>
</Dialog> </Dialog>
</div> </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 ( 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} {addDealModal}
{clickedFromOrgTab ? {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 }}> <Typography variant="h4" style={{ marginTop: 20, marginBottom: 10 }}>
Billing & Licensing Billing & Licensing
</Typography>} </Typography>}
@@ -1942,11 +1940,11 @@ const Billing = (props) => {
{isChildOrg ? {isChildOrg ?
<Typography variant="h6" style={{ marginBottom: 50, }}> <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> </Typography>
: null} : 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 : {isCloud && billingInfo.subscription !== undefined && billingInfo.subscription !== null ? isChildOrg ? null :
<SubscriptionObject <SubscriptionObject
index={0} index={0}
@@ -2258,10 +2256,10 @@ const Billing = (props) => {
) : null*/} ) : null*/}
{!isChildOrg && isCloud && ( {!isChildOrg && isCloud && (
<div style={{ display: 'flex', flexDirection: 'column', marginTop: 10 }}> <div style={{ display: 'flex', flexDirection: 'column', marginTop: 10 }}>
<Typography style={{ marginBottom: 5 }} variant="h4"> <Typography style={{ marginBottom: 5, fontSize: 24, fontWeight: "bold" }}>
Professional Services Professional Services
</Typography> </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. 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> </Typography>
<div style={{ display: 'flex', flexDirection: 'row', marginTop: 5 }}> <div style={{ display: 'flex', flexDirection: 'row', marginTop: 5 }}>
@@ -2280,15 +2278,14 @@ const Billing = (props) => {
)} )}
<div style={{ marginTop: isCloud && 40, marginLeft: 10 }}> <div style={{ marginTop: isCloud && 40, marginLeft: 10 }}>
<Typography <Typography
style={{ marginBottom: 5 }} style={{ marginBottom: 5, fontSize: 24, fontWeight: "bold" }}
variant="h4"
> >
Manage Billing Manage Billing
</Typography> </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. 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>
<Typography variant="body1">Current Usage:</Typography> <Typography style={{fontSize: 18, marginTop: 10}}>Current Usage:</Typography>
<LinearProgress <LinearProgress
variant="determinate" variant="determinate"
value={currentAppRunsInPercentage} value={currentAppRunsInPercentage}
@@ -2300,15 +2297,15 @@ const Billing = (props) => {
marginBottom: 10, 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. 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> </Typography>
<div> <div>
<Typography variant="body1" style={{ marginTop: 20 }}> <Typography style={{ marginTop: 20, fontSize: 18 }}>
Set email alert thresholds for app runs Set email alert thresholds for app runs
</Typography> </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 You will be notified by email when you reach the
{currentIndex !== -1 {currentIndex !== -1
? " " + getSafeValue(alertThresholds[currentIndex].percentage) + '%' + " " ? " " + getSafeValue(alertThresholds[currentIndex].percentage) + '%' + " "
@@ -2320,8 +2317,8 @@ const Billing = (props) => {
: " " + 0 + " "} : " " + 0 + " "}
app runs. app runs.
</Typography> </Typography>
<Typography variant="body1" color="textSecondary" style={{ marginTop: 10 }}> <Typography color="textSecondary" style={{ fontSize: 16 }}>
Please note: Once your app runs reach the set alert threshold, all admins in the organization will receive an email notification. <span style={{fontWeight: 'bold'}}>Please note</span>: Once your app runs reach the set alert threshold, all admins in the organization will receive an email notification.
</Typography> </Typography>
<div style={{ marginTop: 15 }}> <div style={{ marginTop: 15 }}>
{alertThresholds.map((threshold, index) => ( {alertThresholds.map((threshold, index) => (
@@ -2329,7 +2326,7 @@ const Billing = (props) => {
<TextField <TextField
style={{ style={{
marginTop: 10, marginTop: 10,
backgroundColor: theme.palette.inputColor, backgroundColor: "#212121",
width: 250, width: 250,
}} }}
InputProps={{ InputProps={{
@@ -2354,7 +2351,7 @@ const Billing = (props) => {
<TextField <TextField
style={{ style={{
marginTop: 10, marginTop: 10,
backgroundColor: theme.palette.inputColor, backgroundColor: "#212121",
width: 250, width: 250,
marginLeft: 15, marginLeft: 15,
}} }}
@@ -2421,11 +2418,11 @@ const Billing = (props) => {
Save Save
</Button> </Button>
</div>
</div> </div>
<div style={{ marginTop: 40, display: 'flex', flexDirection: 'column' }}> <div style={{ marginTop: 40, display: 'flex', flexDirection: 'column' }}>
<Typography <Typography
style={{ marginTop: 10, marginLeft: 10, marginBottom: 5 }} style={{ marginTop: 10, marginLeft: 10, marginBottom: 5, fontSize: 24, fontWeight: "bold" }}
variant="h4"
> >
Utilization & Stats Utilization & Stats
</Typography> </Typography>
@@ -2438,7 +2435,39 @@ const Billing = (props) => {
userdata={userdata} userdata={userdata}
/> />
</div> </div>
</Wrapper>
) )
} })
export default Billing; export default memo(Billing);
const PaddingWrapper = memo(({ clickedFromOrgTab, children }) => {
const wrapperStyle = useMemo(() => ({
width: clickedFromOrgTab
? "100%"
: "auto",
padding: "27px 10px 19px 27px",
backgroundColor: '#212121',
borderRadius: '16px',
height: '100%',
boxSizing: 'border-box',
borderLeft: '1px solid #494949',
overflow: 'hidden',
maxHeight: "1700px", overflowY: "auto",scrollbarColor: '#494949 transparent', scrollbarWidth: 'thin'
}), [clickedFromOrgTab]);
return (
<div style={wrapperStyle}>
{children}
</div>
);
});
const Wrapper = memo(({ children, clickedFromOrgTab }) => {
return (
<PaddingWrapper clickedFromOrgTab={clickedFromOrgTab}>
{children}
</PaddingWrapper>
);
});
+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 theme from '../theme.jsx';
import classNames from "classnames"; import classNames from "classnames";
@@ -37,6 +37,7 @@ import {
} from 'reaviz'; } from 'reaviz';
import { typecost, typecost_single, } from "../views/HandlePaymentNew.jsx"; import { typecost, typecost_single, } from "../views/HandlePaymentNew.jsx";
import { Context } from '../context/ContextApi.jsx';
const LineChartWrapper = ({keys, inputname, height, width}) => { const LineChartWrapper = ({keys, inputname, height, width}) => {
const [hovered, setHovered] = useState(""); const [hovered, setHovered] = useState("");
@@ -83,7 +84,7 @@ const AppStats = (defaultprops) => {
const [resultRows, setResultRows] = useState([]) const [resultRows, setResultRows] = useState([])
const [resultLoading, setResultLoading] = useState(true) 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(() => { useEffect(() => {
if (workflows === undefined || workflows === null || workflows.length === 0) { if (workflows === undefined || workflows === null || workflows.length === 0) {
@@ -92,7 +93,6 @@ const AppStats = (defaultprops) => {
}, []) }, [])
const getWorkflowStats = async (workflow, startTime, endTime) => { const getWorkflowStats = async (workflow, startTime, endTime) => {
if (!userdata.support) { if (!userdata.support) {
return workflow return workflow
@@ -162,8 +162,6 @@ const AppStats = (defaultprops) => {
const loadWorkflowStats = (foundWorkflows, startTime, endTime) => { const loadWorkflowStats = (foundWorkflows, startTime, endTime) => {
if (!userdata.support) { if (!userdata.support) {
console.log("Not support")
return return
} }
@@ -488,8 +486,13 @@ const AppStats = (defaultprops) => {
setApprunCosts(appcostRuns) setApprunCosts(appcostRuns)
} }
const getStats = () => { const getStats = (orgid) => {
fetch(`${globalUrl}/api/v1/orgs/${selectedOrganization.id}/stats`, {
if (orgid === undefined || orgid === null) {
return
}
fetch(`${globalUrl}/api/v1/orgs/${orgid}/stats`, {
method: "GET", method: "GET",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
@@ -519,8 +522,10 @@ const AppStats = (defaultprops) => {
} }
useEffect(() => { useEffect(() => {
getStats() if(selectedOrganization?.id?.length > 0) {
}, []) getStats(selectedOrganization.id)
}
}, [selectedOrganization])
const paperStyle = { const paperStyle = {
textAlign: "center", textAlign: "center",
@@ -639,7 +644,7 @@ const AppStats = (defaultprops) => {
const data = ( const data = (
<div className="content" style={{width: "100%", margin: "auto", }}> <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 All shown statistics are gathered from <a
href={`${globalUrl}/api/v1/orgs/${selectedOrganization.id}/stats`} href={`${globalUrl}/api/v1/orgs/${selectedOrganization.id}/stats`}
target="_blank" target="_blank"
@@ -722,6 +727,40 @@ const AppStats = (defaultprops) => {
</div> </div>
: null} : 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, }}> <LocalizationProvider dateAdapter={AdapterDayjs} style={{flex: 1, }}>
<div style={{display: "flex", flexDirection: "column", }}> <div style={{display: "flex", flexDirection: "column", }}>
<DateTimePicker <DateTimePicker
@@ -754,6 +793,8 @@ const AppStats = (defaultprops) => {
/> />
</div> </div>
</LocalizationProvider> </LocalizationProvider>
)}
</div> </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 ReactGA from 'react-ga4';
import theme from "../theme.jsx"; import theme from "../theme.jsx";
import { ToastContainer, toast } from "react-toastify" import { ToastContainer, toast } from "react-toastify"
@@ -21,6 +21,7 @@ import {
red, red,
green, green,
} from "../views/AngularWorkflow.jsx" } from "../views/AngularWorkflow.jsx"
import { Context } from "../context/ContextApi.jsx";
//import { useAlert //import { useAlert
@@ -30,6 +31,7 @@ const Branding = (props) => {
const [publishingInfo, setPublishingInfo] = useState(""); const [publishingInfo, setPublishingInfo] = useState("");
const [publishRequirements, setPublishRequirements] = useState([]) const [publishRequirements, setPublishRequirements] = useState([])
const { leftSideBarOpenByClick } = useContext(Context)
const handleEditOrg = (joinStatus) => { const handleEditOrg = (joinStatus) => {
const data = { 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 leadinfo = selectedOrganization.lead_info === undefined || selectedOrganization.lead_info === null || selectedOrganization.lead_info === "" ? "" : JSON.stringify(selectedOrganization.lead_info)
const isPartner = leadinfo.includes("partner") const isPartner = leadinfo.includes("partner")
console.log("LEADINFO: ", leadinfo)
console.log("SELECTEDORGANIZATION: ", selectedOrganization)
return ( return (
<div style={{ width: clickedFromOrgTab? 1030: "auto", padding: 27, height: "auto", backgroundColor: '#212121', borderRadius: '16px', }}> <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', }}>
<h2 style={{marginTop: clickedFromOrgTab ?0:null,}}> <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 Partner Status & Branding
</h2> </Typography>
<Typography variant="body1" color="textSecondary" style={{ marginTop: 20, marginBottom: 10 }}> <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. You can customize your organization's branding by uploading a logo, changing the color scheme and a lot more.
</Typography> </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, }} />} {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> </Typography>
<a href="https://shuffler.io/partners" target="_blank" style={{ textDecoration: "none", }}> <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, }} />} {!isPartner ? <CheckCircleIcon style={{color: red, }} /> : <CheckCircleIcon style={{color: green, }} />}
<Tooltip title="Official Partner Program (manual verification)" placement="top" arrow> <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> </Tooltip>
</Typography> </Typography>
</a> </a>
<a href={`/partners/${selectedOrganization.creator_id}/edit`} target="_blank"> {!isPublished ? (
<Button disabled={isPublished} variant="contained" style={{ marginTop: 20, marginBottom: 10, }} onClick={() => { <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 Modify Public Partner Details
</Button> </Button>
</a> </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, }} /> <Divider style={{marginTop: 50, marginBottom: 50, }} />
<h2> <Typography style={{fontSize: 24, fontWeight: "bold"}}>
Partner Program Partner Program
</h2> </Typography>
<div style={{ display: "flex", width: 900, }}> <div style={{ display: "flex", width: 900, marginTop: 10}}>
<div> <div>
<span> <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> 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> </Typography>
{selectedOrganization.creator_id == "" ? {selectedOrganization.creator_id == "" ?
@@ -167,9 +194,8 @@ const Branding = (props) => {
null null
} }
<Button <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"} variant={selectedOrganization.creator_id == "" ? "contained" : "outlined"}
color={selectedOrganization.creator_id == "" ? "primary" : "secondary"} color={selectedOrganization.creator_id == "" ? "primary" : "secondary"}
disabled={!isOrganizationReady()} disabled={!isOrganizationReady()}
@@ -196,6 +222,8 @@ const Branding = (props) => {
</div> </div>
</div> </div>
</div> </div>
</div>
</div>
) )
} }
+170 -61
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 theme from "../theme.jsx";
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import ReactJson from "react-json-view-ssr"; import ReactJson from "react-json-view-ssr";
@@ -19,6 +19,7 @@ import {
Dialog, Dialog,
DialogTitle, DialogTitle,
DialogActions, DialogActions,
Skeleton,
} from "@mui/material"; } from "@mui/material";
import { import {
@@ -47,6 +48,7 @@ import {
VisibilityOff as VisibilityOffIcon, VisibilityOff as VisibilityOffIcon,
} from "@mui/icons-material"; } from "@mui/icons-material";
import { validateJson, } from "../views/Workflows.jsx"; import { validateJson, } from "../views/Workflows.jsx";
import { Context } from "../context/ContextApi.jsx";
const scrollStyle1 = { const scrollStyle1 = {
height: 100, height: 100,
@@ -65,7 +67,7 @@ const scrollStyle2 = {
} }
const CacheView = (props) => { const CacheView = memo((props) => {
const { globalUrl, userdata, serverside, orgId, isSelectedDataStore } = props; const { globalUrl, userdata, serverside, orgId, isSelectedDataStore } = props;
const [orgCache, setOrgCache] = React.useState(""); const [orgCache, setOrgCache] = React.useState("");
const [listCache, setListCache] = React.useState([]); const [listCache, setListCache] = React.useState([]);
@@ -78,11 +80,13 @@ const CacheView = (props) => {
const [cacheCursor, setCacheCursor] = React.useState(""); const [cacheCursor, setCacheCursor] = React.useState("");
const [dataValue, setDataValue] = React.useState({}); const [dataValue, setDataValue] = React.useState({});
const [editCache, setEditCache] = React.useState(false); const [editCache, setEditCache] = React.useState(false);
const [cachedLoaded, setCachedLoaded] = React.useState(false);
const [show, setShow] = useState({}); const [show, setShow] = useState({});
useEffect(() => { useEffect(() => {
if(orgId?.length >0){
listOrgCache(orgId); listOrgCache(orgId);
}, []); }
}, [orgId]);
const listOrgCache = (orgId) => { const listOrgCache = (orgId) => {
fetch(globalUrl + `/api/v1/orgs/${orgId}/list_cache`, { fetch(globalUrl + `/api/v1/orgs/${orgId}/list_cache`, {
@@ -104,6 +108,7 @@ const CacheView = (props) => {
.then((responseJson) => { .then((responseJson) => {
if (responseJson.success === true) { if (responseJson.success === true) {
setListCache(responseJson.keys); setListCache(responseJson.keys);
setCachedLoaded(true);
} }
if (responseJson.cursor !== undefined && responseJson.cursor !== null && responseJson.cursor !== "") { 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 = ( const modalView = (
// console.log("key:", dataValue.key), // console.log("key:", dataValue.key),
//console.log("value:",dataValue.value), //console.log("value:",dataValue.value),
@@ -241,11 +274,23 @@ const CacheView = (props) => {
setModalOpen(false); setModalOpen(false);
}} }}
PaperProps={{ PaperProps={{
style: { sx: {
backgroundColor: theme.palette.surfaceColor, borderRadius: theme?.palette?.DialogStyle?.borderRadius,
color: "white", border: theme?.palette?.DialogStyle?.border,
minWidth: "800px", minWidth: "800px",
minHeight: "320px", 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" } { editCache ? "Edit Cache" : "Add Cache" }
</span> </span>
</DialogTitle> </DialogTitle>
<div style={{ paddingLeft: "30px", paddingRight: '30px' }}> <div style={{ paddingLeft: "30px", paddingRight: '30px', backgroundColor: "#212121", }}>
Key Key
<TextField <TextField
color="primary" color="primary"
@@ -278,7 +323,7 @@ const CacheView = (props) => {
onChange={(e) => setKey(e.target.value)} onChange={(e) => setKey(e.target.value)}
/> />
</div> </div>
<div style={{ paddingLeft: 30, paddingRight: 30 }}> <div style={{ paddingLeft: 30, paddingRight: 30, backgroundColor: "#212121" }}>
<div style={{display: "flex", }}> <div style={{display: "flex", }}>
<Typography style={{marginTop: 25, marginBottom: 0, flex: 20, }}> <Typography style={{marginTop: 25, marginBottom: 0, flex: 20, }}>
Value - ({isValidJson.valid === true ? "Valid" : "Invalid"} JSON) Value - ({isValidJson.valid === true ? "Valid" : "Invalid"} JSON)
@@ -320,7 +365,7 @@ const CacheView = (props) => {
</div> </div>
<DialogActions style={{ paddingLeft: "30px", paddingRight: '30px' }}> <DialogActions style={{ paddingLeft: "30px", paddingRight: '30px' }}>
<Button <Button
style={{ borderRadius: "0px" }} style={{ borderRadius: "2px", fontSize: 16, color: "#ff8544", textTransform:"none" }}
onClick={() => { onClick={() => {
setModalOpen(false) setModalOpen(false)
setValue("") setValue("")
@@ -332,7 +377,7 @@ const CacheView = (props) => {
</Button> </Button>
<Button <Button
variant="contained" variant="contained"
style={{ borderRadius: "0px" }} style={{ borderRadius: "2px", backgroundColor: "#ff8544",color: "#1a1a1a", textTransform:"none" }}
onClick={() => { onClick={() => {
{editCache ? editOrgCache(orgId) : addOrgCache(orgId)} {editCache ? editOrgCache(orgId) : addOrgCache(orgId)}
@@ -348,9 +393,10 @@ const CacheView = (props) => {
); );
return ( return (
<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" }}>
<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, }}>
{modalView} {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 }}> <div style={{ marginTop: isSelectedDataStore?null:20, marginBottom: 20 }}>
<h2 style={{ display: isSelectedDataStore?null: "inline" }}>Shuffle Datastore</h2> <h2 style={{ display: isSelectedDataStore?null: "inline" }}>Shuffle Datastore</h2>
<span style={{ marginLeft: isSelectedDataStore?null:25, color:isSelectedDataStore?"#9E9E9E":null}}> <span style={{ marginLeft: isSelectedDataStore?null:25, color:isSelectedDataStore?"#9E9E9E":null}}>
@@ -366,7 +412,7 @@ const CacheView = (props) => {
</span> </span>
</div> </div>
<Button <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" variant="contained"
color="primary" color="primary"
onClick={() =>{ onClick={() =>{
@@ -379,7 +425,7 @@ const CacheView = (props) => {
Add Cache Add Cache
</Button> </Button>
<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" variant="contained"
color="primary" color="primary"
onClick={() => listOrgCache(orgId)} onClick={() => listOrgCache(orgId)}
@@ -392,28 +438,79 @@ const CacheView = (props) => {
marginBottom: 20, marginBottom: 20,
}} }}
/>} />}
<List style={{borderRadius: isSelectedDataStore?8:null, border:isSelectedDataStore?"1px solid #494949":null, marginTop:isSelectedDataStore?24:null}}> <div
<ListItem style={{width: isSelectedDataStore?"100%":null, borderBottom:isSelectedDataStore?"1px solid #494949":null}}> 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 <ListItemText
primary="Key" key={index}
style={{ minWidth: isSelectedDataStore?200:250, maxWidth: isSelectedDataStore?200:250, }} primary={header}
/> style={{
<ListItemText display: "table-cell",
primary="Value" padding: "0px 8px 8px 8px",
style={{ minWidth: isSelectedDataStore?300:400, maxWidth: isSelectedDataStore?300:400, overflowX: "auto", overflowY: "hidden", }} whiteSpace: "nowrap",
/> overflow: "hidden",
<ListItemText textOverflow: "ellipsis",
primary="Actions" borderBottom: "1px solid #494949"
style={{ minWidth: 150, maxWidth: 150, marginLeft: isSelectedDataStore?80:null,}} }}
/>
<ListItemText
style={{textAlign:isSelectedDataStore?"center":null}}
primary="Updated"
/> />
))}
</ListItem> </ListItem>
{listCache === undefined || listCache === null {cachedLoaded === false
? null ? [...Array(6)].map((_, rowIndex) => (
: listCache.map((data, index) => { <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"; var bgColor = isSelectedDataStore? "#212121":"#27292d";
if (index % 2 === 0) { if (index % 2 === 0) {
bgColor = isSelectedDataStore? "#1A1A1A":"#1f2023"; bgColor = isSelectedDataStore? "#1A1A1A":"#1f2023";
@@ -421,35 +518,46 @@ const CacheView = (props) => {
const validate = validateJson(data.value); const validate = validateJson(data.value);
return ( 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 <ListItemText
style={{ style={{
maxWidth: 200, display: "table-cell",
minWidth: 200,
overflow: "hidden", overflow: "hidden",
padding: 8,
verticalAlign: "middle",
}} }}
primary={data.key} primary={data.key}
/> />
<ListItemText <ListItemText
style={{ style={{
minWidth: 300, display: "table-cell",
maxWidth: 300, overflowY: "auto",
// height:200, overflowX: "auto",
overflowX: "hidden", border: "1px solid rgba(255,255,255,0.7)",
borderRadius: 6,
backgroundColor: "#151515",
maxHeight: 300,
verticalAlign: "middle",
}} }}
primary={validate.valid ? primary={validate.valid ?
<ReactJson <ReactJson
src={validate.result} src={validate.result}
theme={theme.palette.jsonTheme} theme={theme.palette.jsonTheme}
style={theme.palette.reactJsonStyle} style={{
padding: 5,
maxHeight: 300,
overflowY: "auto",
}}
collapsed={true} collapsed={true}
enableClipboard={(copy) => { enableClipboard={(copy) => {
//handleReactJsonClipboard(copy); // handleReactJsonClipboard(copy);
}} }}
collapseStringsAfterLength={theme.palette.jsonCollapseStringsAfterLength}
iconStyle={theme.palette.jsonIconStyle}
displayDataTypes={false} displayDataTypes={false}
onSelect={(select) => { onSelect={(select) => {
//HandleJsonCopy(showResult, select, data.action.label); // HandleJsonCopy(showResult, select, data.action.label);
//console.log("SELECTED!: ", select); console.log("SELECTED!: ", select);
}} }}
name={"value"} name={"value"}
/> />
@@ -459,11 +567,12 @@ const CacheView = (props) => {
/> />
<ListItemText <ListItemText
style={{ style={{
maxWidth: 200, display: "table-cell",
minWidth: 200, verticalAlign: "middle",
marginLeft: 50, padding: 8
}} }}
primary=<span style={{ display: "inline" }}> primary={(
<span style={{ display: "inline" }}>
<Tooltip <Tooltip
title="Edit item" title="Edit item"
style={{}} style={{}}
@@ -482,9 +591,7 @@ const CacheView = (props) => {
setModalOpen(true) setModalOpen(true)
}} }}
> >
<EditIcon <img src="/icons/editIcon.svg" alt="edit" />
style={{ color: "white" }}
/>
</IconButton> </IconButton>
</span> </span>
</Tooltip> </Tooltip>
@@ -508,7 +615,6 @@ const CacheView = (props) => {
</Tooltip> </Tooltip>
<Tooltip <Tooltip
title={"Delete item"} title={"Delete item"}
style={{ marginLeft: 25, }}
aria-label={"Delete"} aria-label={"Delete"}
> >
<span> <span>
@@ -519,18 +625,18 @@ const CacheView = (props) => {
//deleteFile(orgId); //deleteFile(orgId);
}} }}
> >
<DeleteIcon <img src="/icons/deleteIcon.svg" alt="delete" />
style={{ color: "white" }}
/>
</IconButton> </IconButton>
</span> </span>
</Tooltip> </Tooltip>
</span> </span>
)}
/> />
<ListItemText <ListItemText
style={{ style={{
maxWidth: 225, display: "table-cell",
minWidth: 225, verticalAlign: "middle",
padding: 8
}} }}
primary={new Date(data.edited * 1000).toISOString()} primary={new Date(data.edited * 1000).toISOString()}
/> />
@@ -539,7 +645,10 @@ const CacheView = (props) => {
})} })}
</List> </List>
</div> </div>
</div>
</div>
</div>
); );
} });
export default CacheView;
export default memo(CacheView);
+2 -2
View File
@@ -1149,7 +1149,7 @@ const EditWorkflow = (props) => {
</div> </div>
: null} : null}
{/*!isEditing ? <> {!isEditing ? <>
<div style={{ marginTop: 20, }}> <div style={{ marginTop: 20, }}>
<FormControlLabel <FormControlLabel
control={<Checkbox />} control={<Checkbox />}
@@ -1164,7 +1164,7 @@ const EditWorkflow = (props) => {
/> />
</div> </div>
</> : null*/} </> : null}
<Tooltip color="primary" title={"Add more details"} placement="top"> <Tooltip color="primary" title={"Add more details"} placement="top">
<Button <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 { toast } from 'react-toastify';
import { import {
@@ -21,6 +21,7 @@ import {
DialogContent, DialogContent,
DialogActions, DialogActions,
Typography, Typography,
Skeleton,
} from "@mui/material"; } from "@mui/material";
import { import {
@@ -39,11 +40,13 @@ import {
import Dropzone from "../components/Dropzone.jsx"; import Dropzone from "../components/Dropzone.jsx";
import ShuffleCodeEditor from "../components/ShuffleCodeEditor1.jsx"; import ShuffleCodeEditor from "../components/ShuffleCodeEditor1.jsx";
import theme from "../theme.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 { globalUrl, userdata, serverside, selectedOrganization, isCloud,isSelectedFiles } = props;
const [files, setFiles] = React.useState([]); const [files, setFiles] = React.useState([]);
const [showLoader, setShowLoader] = useState(true)
const [selectedCategory, setSelectedCategory] = React.useState("default"); const [selectedCategory, setSelectedCategory] = React.useState("default");
const [openFileId, setOpenFileId] = React.useState(false); const [openFileId, setOpenFileId] = React.useState(false);
const [fileCategories, setFileCategories] = React.useState([]); const [fileCategories, setFileCategories] = React.useState([]);
@@ -58,7 +61,6 @@ const Files = (props) => {
const [downloadBranch, setDownloadBranch] = React.useState("main"); const [downloadBranch, setDownloadBranch] = React.useState("main");
const [downloadFolder, setDownloadFolder] = React.useState("translation_standards"); const [downloadFolder, setDownloadFolder] = React.useState("translation_standards");
const [contentLoading, setContentLoading] = React.useState(false) const [contentLoading, setContentLoading] = React.useState(false)
//const alert = useAlert(); //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"] 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 = ""; var upload = "";
@@ -66,8 +68,6 @@ const Files = (props) => {
const handleKeyDown = (event) => { const handleKeyDown = (event) => {
if (event.key === 'Enter') { if (event.key === 'Enter') {
console.log('do validate')
console.log("new namespace name->",event.target.value);
fileCategories.push(event.target.value); fileCategories.push(event.target.value);
setSelectedCategory(event.target.value); setSelectedCategory(event.target.value);
setRenderTextBox(false); setRenderTextBox(false);
@@ -136,6 +136,7 @@ const Files = (props) => {
.then((responseJson) => { .then((responseJson) => {
if (responseJson.files !== undefined && responseJson.files !== null) { if (responseJson.files !== undefined && responseJson.files !== null) {
setFiles(responseJson.files); setFiles(responseJson.files);
setShowLoader(false)
} else if (responseJson.list !== undefined && responseJson.list !== null) { } else if (responseJson.list !== undefined && responseJson.list !== null) {
// Set the "namespace" field in all items // Set the "namespace" field in all items
if (namespace !== undefined && namespace !== null) { if (namespace !== undefined && namespace !== null) {
@@ -147,8 +148,10 @@ const Files = (props) => {
} }
setFiles(responseJson.list); setFiles(responseJson.list);
setShowLoader(false)
} else { } else {
setFiles([]); setFiles([]);
setShowLoader(false)
} }
if (namespace === undefined || namespace === null || namespace === "default") { if (namespace === undefined || namespace === null || namespace === "default") {
@@ -244,14 +247,26 @@ const Files = (props) => {
const fileDownloadModal = loadFileModalOpen ? const fileDownloadModal = loadFileModalOpen ?
<Dialog <Dialog
open={loadFileModalOpen} open={loadFileModalOpen}
onClose={() => {}}
PaperProps={{ PaperProps={{
style: { sx: {
backgroundColor: theme.palette.surfaceColor, borderRadius: theme?.palette?.DialogStyle?.borderRadius,
color: "white", border: theme?.palette?.DialogStyle?.border,
fontFamily: theme?.typography?.fontFamily,
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
zIndex: 1000,
minWidth: "800px", minWidth: "800px",
minHeight: "320px", 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> <DialogTitle>
@@ -364,7 +379,7 @@ const Files = (props) => {
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
<Button <Button
style={{ borderRadius: "0px" }} style={{ borderRadius: "2px", textTransform: 'none', fontSize:16, color: "#ff8544" }}
onClick={() => setLoadFileModalOpen(false)} onClick={() => setLoadFileModalOpen(false)}
color="primary" color="primary"
> >
@@ -372,7 +387,7 @@ const Files = (props) => {
</Button> </Button>
<Button <Button
variant="contained" variant="contained"
style={{ borderRadius: "0px" }} style={{ borderRadius: "2px", textTransform: 'none', fontSize:16, color: "#1a1a1a", backgroundColor: "#ff8544" }}
disabled={downloadUrl.length === 0 || !downloadUrl.includes("http")} disabled={downloadUrl.length === 0 || !downloadUrl.includes("http")}
onClick={() => { onClick={() => {
handleGithubValidation(); handleGithubValidation();
@@ -637,24 +652,18 @@ const Files = (props) => {
return ( return (
<Dropzone <Dropzone
style={{ style={{
maxWidth: window.innerWidth > 1366 ? 1366 : 1200,
margin: "auto", margin: "auto",
padding: isSelectedFiles ? null : 20, padding: isSelectedFiles ? null : 20,
width: '100%',
height: "100%",
}} }}
onDrop={uploadFile} 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"> <div style={{height: "100%", maxHeight: 1700,overflowY: 'auto', scrollbarColor: '#494949 transparent', scrollbarWidth: 'thin'}}>
<IconButton <div style={{ height: "100%", width: "calc(100% - 20px)", scrollbarColor: '#494949 transparent', scrollbarWidth: 'thin' }}>
color="secondary" <DownloadFileIcon setLoadFileModalOpen={setLoadFileModalOpen} isSelectedFiles={isSelectedFiles} />
style={{position: "absolute", right: 0, top: isSelectedFiles?null:0, left: isSelectedFiles? 990:null }}
variant="text"
onClick={() => setLoadFileModalOpen(true)}
>
<CloudDownloadIcon />
</IconButton>
</Tooltip>
{fileDownloadModal} {fileDownloadModal}
@@ -681,9 +690,9 @@ const Files = (props) => {
onClick={() => { onClick={() => {
upload.click(); 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> </Button>
{/* <FileCategoryInput {/* <FileCategoryInput
isSet={renderTextBox} /> */} isSet={renderTextBox} /> */}
@@ -702,7 +711,7 @@ const Files = (props) => {
}} }}
/> />
<Button <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" variant="contained"
color="primary" color="primary"
onClick={() => getFiles(selectedCategory)} onClick={() => getFiles(selectedCategory)}
@@ -710,21 +719,23 @@ const Files = (props) => {
<CachedIcon /> <CachedIcon />
</Button> </Button>
{/* <div style={{height: 35, width: 1, color: "#494949"}}></div> */}
{fileCategories !== undefined && {fileCategories !== undefined &&
fileCategories !== null && fileCategories !== null &&
fileCategories.length > 1 ? ( fileCategories.length > 1 ? (
<FormControl style={{ minWidth: 150, maxWidth: 150 }}> <FormControl style={{ minWidth: 150, maxWidth: 150 }}>
<InputLabel id="input-namespace-label">File Category</InputLabel>
<Select <Select
labelId="input-namespace-select-label" labelId="input-namespace-select-label"
id="input-namespace-select-id" id="input-namespace-select-id"
style={{ style={{
color: "white", color: "white",
minWidth: 150, minWidth: 122,
maxWidth: 150, maxWidth: 122,
height: 35,
float: "right", float: "right",
position: 'relative',
top: 8
}} }}
value={selectedCategory} value={selectedCategory}
onChange={(event) => { onChange={(event) => {
@@ -759,7 +770,7 @@ const Files = (props) => {
</Select> </Select>
</FormControl> </FormControl>
) : null} ) : null}
<div style={{display: "inline-flex", position:"relative"}}> <div style={{display: "inline-flex", position:"relative", top: 8}}>
{renderTextBox ? {renderTextBox ?
<Tooltip title={"Close"} style={{}} aria-label={""}> <Tooltip title={"Close"} style={{}} aria-label={""}>
@@ -777,13 +788,14 @@ const Files = (props) => {
: :
<Tooltip title={"Add new file category"} style={{}} aria-label={""}> <Tooltip title={"Add new file category"} style={{}} aria-label={""}>
<Button <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" color="primary"
onClick={() => { onClick={() => {
setRenderTextBox(true); setRenderTextBox(true);
}} }}
> >
<AddIcon/> <AddIcon/>
File Category
</Button> </Button>
</Tooltip> </Tooltip>
} }
@@ -824,44 +836,86 @@ const Files = (props) => {
backgroundColor: theme.palette.inputColor, backgroundColor: theme.palette.inputColor,
}} }}
/>} />}
<div
<List style={{borderRadius: isSelectedFiles?8:null, border:isSelectedFiles?"1px solid #494949":null, marginTop:isSelectedFiles?24:null}}>
<ListItem style={{width:isSelectedFiles?"100%":null, borderBottom:isSelectedFiles?"1px solid #494949":null}}>
{/*
<ListItemText
primary="Updated"
style={{ maxWidth: 185, minWidth: 185 }}
/>
*/}
<ListItemText
primary="Name"
style={{ style={{
maxWidth: 250, borderRadius: 8,
minWidth: 250, marginTop: 24,
overflow: "hidden", border: "1px solid #494949",
marginLeft: 10, 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> </ListItem>
{files === undefined || files === null || files.length === 0 ? null : {showLoader ?
files.map((file, index) => { [...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 === "") { if (file.namespace === "") {
file.namespace = "default"; file.namespace = "default";
} }
@@ -877,14 +931,16 @@ const Files = (props) => {
const filenamesplit = file.filename.split(".") const filenamesplit = file.filename.split(".")
const iseditable = file.filesize < 2000000 && file.status === "active" && allowedFileTypes.includes(filenamesplit[filenamesplit.length-1]) const iseditable = file.filesize < 2000000 && file.status === "active" && allowedFileTypes.includes(filenamesplit[filenamesplit.length-1])
return ( return (
<ListItem <ListItem
key={index} key={index}
style={{ style={{
display: 'table-row',
backgroundColor: bgColor, backgroundColor: bgColor,
maxHeight: 100, maxHeight: 100,
overflow: "hidden", overflow: "hidden",
borderBottomLeftRadius: files?.length - 1 === index ? 8 : 0,
borderBottomRightRadius: files?.length - 1 === index ? 8 : 0,
}} }}
> >
{/* {/*
@@ -898,12 +954,17 @@ const Files = (props) => {
/> />
*/} */}
<ListItemText <ListItemText
style={{ primaryTypographyProps={{
maxWidth: 250, style: {
minWidth: 250, display: 'table-cell',
overflow: "hidden", maxWidth: "170px",
marginLeft: 10, whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
overflow: 'hidden',
padding: 8
},
}} }}
primary={file.filename} primary={file.filename}
/> />
<ListItemText <ListItemText
@@ -911,12 +972,13 @@ const Files = (props) => {
file.workflow_id === "global" || file.workflow_id === "" || file.workflow_id === null || file.workflow_id === undefined ? file.workflow_id === "global" || file.workflow_id === "" || file.workflow_id === null || file.workflow_id === undefined ?
<IconButton <IconButton
disabled={file.workflow_id === "global"} disabled={file.workflow_id === "global"}
style={{marginLeft: 10}}
> >
<OpenInNewIcon <OpenInNewIcon
style={{ style={{
color: color:
file.workflow_id !== "global" file.workflow_id !== "global"
? "white" ? "#FF8444"
: "grey", : "grey",
}} }}
/> />
@@ -939,12 +1001,13 @@ const Files = (props) => {
> >
<IconButton <IconButton
disabled={file.workflow_id === "global"} disabled={file.workflow_id === "global"}
style={{marginLeft: 10}}
> >
<OpenInNewIcon <OpenInNewIcon
style={{ style={{
color: color:
file.workflow_id !== "global" file.workflow_id !== "global"
? "white" ? "#FF8444"
: "grey", : "grey",
}} }}
/> />
@@ -955,38 +1018,42 @@ const Files = (props) => {
) )
} }
style={{ style={{
minWidth: 100, display: 'table-cell',
maxWidth: 100,
overflow: "hidden", overflow: "hidden",
textAlign: isSelectedFiles?"center":null
}} }}
/> />
<ListItemText <ListItemText
primary={file.md5_sum} primary={(
style={{ <Tooltip title={file.md5_sum}>
minWidth: 300, {file.md5_sum}
maxWidth: 300, </Tooltip>
)}
primaryTypographyProps={{
style:{
display: 'table-cell',
marginLeft:isSelectedFiles? 15:null, marginLeft:isSelectedFiles? 15:null,
overflow: isSelectedFiles?"auto":"hidden", overflow: "hidden",
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
maxWidth: 200,
}
}} }}
/> />
<ListItemText <ListItemText
primary={file.status} primary={file.status}
style={{ style={{
minWidth: 75, display: 'table-cell',
maxWidth: 75,
overflow: "hidden", overflow: "hidden",
textAlign:isSelectedFiles?"center":null, textAlign:isSelectedFiles?"left":null,
marginLeft: 10, color: file.status === "active" ? "#2BC07E" : "#FD4C62"
}} }}
/> />
<ListItemText <ListItemText
primary={file.filesize} primary={file.filesize}
style={{ style={{
minWidth: isSelectedFiles?80:125, display: 'table-cell',
maxWidth: isSelectedFiles?80:125,
marginLeft: isSelectedFiles?15:null,
overflow: "hidden", overflow: "hidden",
textAlign:'center'
}} }}
/> />
<ListItemText <ListItemText
@@ -999,14 +1066,14 @@ const Files = (props) => {
<span> <span>
<IconButton <IconButton
disabled={!iseditable} disabled={!iseditable}
style = {{padding: "6px"}} style = {{padding: "6px", }}
onClick={() => { onClick={() => {
setOpenEditor(true) setOpenEditor(true)
setOpenFileId(file.id) setOpenFileId(file.id)
readFileData(file) readFileData(file)
}} }}
> >
<EditIcon <img src="/icons/editIcon.svg" alt="edit icon"
style={{color: iseditable ? "white" : "grey",}} style={{color: iseditable ? "white" : "grey",}}
/> />
</IconButton> </IconButton>
@@ -1051,7 +1118,7 @@ const Files = (props) => {
downloadFile(file); downloadFile(file);
}} }}
> >
<CloudDownloadIcon <img src="/icons/downloadIcon.svg" alt="download icon"
style={{ style={{
color: color:
file.status === "active" file.status === "active"
@@ -1070,36 +1137,14 @@ const Files = (props) => {
<IconButton <IconButton
style = {{padding: "6px"}} style = {{padding: "6px"}}
onClick={() => { onClick={() => {
const elementName = "copy_element_shuffle"; console.log("file is : ", file)
var copyText =
document.getElementById(elementName);
if (
copyText !== null &&
copyText !== undefined
) {
const clipboard = navigator.clipboard;
if (clipboard === undefined) {
toast(
"Can only copy over HTTPS (port 3443)"
);
return;
}
navigator.clipboard.writeText(file.id); navigator.clipboard.writeText(file.id);
copyText.select();
copyText.setSelectionRange(
0,
99999
); /* For mobile devices */
/* Copy the text inside the text field */
document.execCommand("copy"); document.execCommand("copy");
toast(file.id + " copied to clipboard"); toast(file.id + " copied to clipboard");
}
}} }}
> >
<FileCopyIcon style={{ color: "white" }} /> <img src="/icons/copyIcon.svg" alt="copy icon" style={{ color: "white" }} />
</IconButton> </IconButton>
</Tooltip> </Tooltip>
<Tooltip <Tooltip
@@ -1115,7 +1160,7 @@ const Files = (props) => {
deleteFile(file) deleteFile(file)
}} }}
> >
<DeleteIcon <img src="/icons/deleteIcon.svg" alt="delete icon"
style={{ style={{
color: color:
file.status === "active" file.status === "active"
@@ -1128,19 +1173,42 @@ const Files = (props) => {
</Tooltip> </Tooltip>
</span> </span>
style={{ style={{
minWidth: 250, display: 'table-cell',
maxWidth: 250, textAlign:'center'
// overflow: "hidden", // overflow: "hidden",
}} }}
/> />
</ListItem> </ListItem>
); );
}) })
)
} }
</List> </List>
</div> </div>
</div>
</div>
</div>
</Dropzone> </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>
)
})
+208 -51
View File
@@ -7,6 +7,7 @@ import {
Add as AddIcon, Add as AddIcon,
BorderColor, BorderColor,
Close as CloseIcon, Close as CloseIcon,
ConstructionOutlined,
} from "@mui/icons-material"; } from "@mui/icons-material";
import SearchBox from "./SearchData.jsx"; import SearchBox from "./SearchData.jsx";
import { import {
@@ -85,7 +86,21 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
return orgOptions.find((option) => option.name === selectedOrg); return orgOptions.find((option) => option.name === selectedOrg);
}, [selectedOrg, orgOptions]); }, [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) => { const CustomPopper = (props) => {
return ( return (
@@ -141,7 +156,7 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
> >
{props.children} {props.children}
</Box> </Box>
<Link to="/admin2" style={hrefStyle}> <Link to="/admin?tab=tenants" style={hrefStyle}>
<Box <Box
sx={{ sx={{
width: "100%", width: "100%",
@@ -446,7 +461,7 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
}, },
}} }}
> >
<Link to="/admin2" style={hrefStyle}> <Link to="/admin" style={hrefStyle}>
<MenuItem <MenuItem
onClick={(event) => { onClick={(event) => {
handleClose(); handleClose();
@@ -466,7 +481,7 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
</Link> </Link>
<Divider style={{ marginTop: 10, marginBottom: 10, }} /> <Divider style={{ marginTop: 10, marginBottom: 10, }} />
<Link to="/admin2?admin_tab=priorities" style={hrefStyle}> <Link to="/admin?admin_tab=notifications" style={hrefStyle}>
<MenuItem <MenuItem
onClick={(event) => { onClick={(event) => {
handleClose(); handleClose();
@@ -478,7 +493,7 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
}) })
</MenuItem> </MenuItem>
</Link> </Link>
<Link to="/usecases2" style={hrefStyle}> <Link to="/usecases" style={hrefStyle}>
<MenuItem <MenuItem
onClick={(event) => { onClick={(event) => {
handleClose(); handleClose();
@@ -641,7 +656,7 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
}; };
const getRegionTag = (region_url) => { const getRegionTag = (region_url) => {
let regiontag = "eu"; let regiontag = "UK";
if ( if (
region_url !== undefined && region_url !== undefined &&
region_url !== null && region_url !== null &&
@@ -653,15 +668,25 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
regiontag = namesplit[namesplit.length - 1]; regiontag = namesplit[namesplit.length - 1];
if (regiontag === "california") { if (regiontag === "california") {
regiontag = "us"; regiontag = "US";
} else if (regiontag === "frankfurt") { } else if (regiontag === "frankfurt") {
regiontag = "fr"; regiontag = "EU";
} else if (regiontag === "ca"){
regiontag = "CA";
} }
} }
} }
return regiontag; return regiontag;
}; };
useEffect(() => {
if(activeOrgName !== userdata?.active_org?.name){
setActiveOrgName(userdata?.active_org?.name || "Select Organization");
}
}, [userdata]);
const CheckOrgStates = useCallback(() => { const CheckOrgStates = useCallback(() => {
setOrgOptions( setOrgOptions(
userdata?.orgs?.map((org) => ({ userdata?.orgs?.map((org) => ({
@@ -723,6 +748,20 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
</Dialog> </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 ( return (
<div <div
style={{ style={{
@@ -736,7 +775,8 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
boxShadow: "0px 4px 12px rgba(0, 0, 0, 0.2)" , boxShadow: "0px 4px 12px rgba(0, 0, 0, 0.2)" ,
resize: 'both', resize: 'both',
height: "calc(100vh - 32px)", zoom: 0.8,
height: "calc((100vh - 32px)*1.2)",
}} }}
> >
{modalView} {modalView}
@@ -759,7 +799,7 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
<Box <Box
sx={{ sx={{
display: "flex", display: "flex",
alignItems: "center", // alignItems: "center",
justifyContent: "center", justifyContent: "center",
position: "relative", position: "relative",
right: !expandLeftNav && 8, right: !expandLeftNav && 8,
@@ -815,7 +855,7 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
<Fade in={expandLeftNav} timeout={500}> <Fade in={expandLeftNav} timeout={500}>
<TextField <TextField
id="sidebar-search" id="sidebar-search"
placeholder= "Search" placeholder="Search"
sx={{ sx={{
width: "100%", width: "100%",
maxWidth: 228, maxWidth: 228,
@@ -831,7 +871,7 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
}, },
"&:hover fieldset": { "&:hover fieldset": {
borderColor: "#ffffff", borderColor: "#ffffff",
display: "block" display: "block",
}, },
"&.Mui-focused fieldset": { "&.Mui-focused fieldset": {
borderColor: "#A9A9A9", borderColor: "#A9A9A9",
@@ -847,12 +887,28 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
variant="outlined" variant="outlined"
InputProps={{ InputProps={{
startAdornment: ( startAdornment: (
<SearchIcon style={{ color: "#CDCDCD", width: 24, height: 24, marginLeft: 16}} /> <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, disableUnderline: true,
}} }}
onClick={()=>{setSearchBarModalOpen(true)}} onClick={() => {
onChange={()=> {setSearchBarModalOpen(true)}} setSearchBarModalOpen(true);
}}
onChange={() => {
setSearchBarModalOpen(true);
}}
/> />
</Fade> </Fade>
):( ):(
@@ -891,10 +947,16 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
setCurrentOpenTab("automate"); setCurrentOpenTab("automate");
localStorage.setItem("lastTabOpenByUser", "automate"); localStorage.setItem("lastTabOpenByUser", "automate");
}} }}
sx={{ variant="text"
style={{
...ButtonStyle, ...ButtonStyle,
backgroundColor: ((currentOpenTab === "automate" && currentPath.includes("/dashboards/automate"))|| (!expandLeftNav && (currentPath === "/workflows" || currentPath === "/usecases2" || currentPath.includes("/search"))))? "#2f2f2f": "transparent", 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 <img
@@ -920,9 +982,16 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
setOpenautomateTab((prev) => !prev); setOpenautomateTab((prev) => !prev);
setOpenSecurityTab(false); setOpenSecurityTab(false);
}} }}
sx={{ style={{
color: "#FFFFFF",
marginLeft: 0.625, marginLeft: 0.625,
}} }}
onMouseOver={(event)=>{
event.currentTarget.style.backgroundColor = "#2f2f2f";
}}
onMouseOut={(event)=>{
event.currentTarget.style.backgroundColor = "transparent";
}}
> >
{openautomatetab ? ( {openautomatetab ? (
<ExpandLessIcon <ExpandLessIcon
@@ -952,23 +1021,28 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
flexDirection: "column", flexDirection: "column",
paddingLeft: 16, paddingLeft: 16,
gap: 4, gap: 4,
marginTop: expandLeftNav ? 16 : 0,
}} }}
> >
<Link to="/usecases2" style={hrefStyle}> <Link to="/usecases" style={hrefStyle}>
<Button <Button
onClick={(event) => { onClick={(event) => {
setCurrentOpenTab("usecases"); setCurrentOpenTab("usecases");
localStorage.setItem("lastTabOpenByUser", "usecases"); localStorage.setItem("lastTabOpenByUser", "usecases");
}} }}
sx={{ style={{
width: "100%", width: "100%",
height: 35, height: 35,
color: "#C8C8C8", color: "#C8C8C8",
justifyContent: "flex-start", justifyContent: "flex-start",
textTransform: "none", textTransform: "none",
backgroundColor: currentOpenTab === "usecases" && expandLeftNav && currentPath.includes("/usecases2")? "#2f2f2f": "transparent", backgroundColor: currentOpenTab === "usecases" && expandLeftNav && currentPath.includes("/usecases")? "#2f2f2f": "transparent",
"&:hover": { backgroundColor: "#2f2f2f" }, 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} disableRipple={expandLeftNav ? false : true}
> >
@@ -985,22 +1059,28 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
</span> </span>
</Button> </Button>
</Link> </Link>
<Link to="/workflows2" style={hrefStyle}> <Link to="/workflows" style={hrefStyle}>
<Button <Button
onClick={(event) => { onClick={(event) => {
setCurrentOpenTab("workflows"); setCurrentOpenTab("workflows");
localStorage.setItem("lastTabOpenByUser", "workflows"); localStorage.setItem("lastTabOpenByUser", "workflows");
}} }}
sx={{ style={{
width: "100%", width: "100%",
height: 35, height: 35,
color: "#C8C8C8", color: "#C8C8C8",
justifyContent: "flex-start", justifyContent: "flex-start",
textTransform: "none", textTransform: "none",
backgroundColor: currentOpenTab === "workflows" && currentPath === "/workflows" && expandLeftNav? "#2f2f2f": "transparent", backgroundColor: currentOpenTab === "workflows" && currentPath === "/workflows" && expandLeftNav? "#2f2f2f": "transparent",
"&:hover": { backgroundColor: "#2f2f2f" }, marginLeft: 16
}} }}
disableRipple={expandLeftNav ? false : true} 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 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 <span
@@ -1015,20 +1095,26 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
</span> </span>
</Button> </Button>
</Link> </Link>
<Link to="/apps2" style={hrefStyle}> <Link to="/apps" style={hrefStyle}>
<Button <Button
onClick={(event) => { onClick={(event) => {
setCurrentOpenTab("apps"); setCurrentOpenTab("apps");
localStorage.setItem("lastTabOpenByUser", "apps"); localStorage.setItem("lastTabOpenByUser", "apps");
}} }}
sx={{ style={{
width: "100%", width: "100%",
height: 35, height: 35,
color: "#C8C8C8", color: "#C8C8C8",
justifyContent: "flex-start", justifyContent: "flex-start",
textTransform: "none", textTransform: "none",
backgroundColor: currentOpenTab === "apps" && expandLeftNav && currentPath.includes("/search") ? "#2f2f2f": "transparent", backgroundColor: currentOpenTab === "apps" && expandLeftNav && currentPath.includes("/apps2") ? "#2f2f2f": "transparent",
"&:hover": { backgroundColor: "#2f2f2f" }, 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} disableRipple={expandLeftNav ? false : true}
> >
@@ -1038,7 +1124,7 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
display: expandLeftNav ? "inline" : "none", display: expandLeftNav ? "inline" : "none",
opacity: expandLeftNav ? 1 : 0, opacity: expandLeftNav ? 1 : 0,
transition: "opacity 0.3s ease", transition: "opacity 0.3s ease",
color: currentOpenTab === "apps" && currentPath.includes("/search") ? "#F1F1F1" : "#C8C8C8", color: currentOpenTab === "apps" && currentPath.includes("/apps2") ? "#F1F1F1" : "#C8C8C8",
}} }}
> >
Apps Apps
@@ -1051,7 +1137,7 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
sx={{ sx={{
display: "flex", display: "flex",
flexDirection: "row", flexDirection: "row",
marginTop: 2.5, marginTop: 1
}} }}
> >
<span style={{ display: "inline-block", width: "100%" }}> <span style={{ display: "inline-block", width: "100%" }}>
@@ -1072,7 +1158,7 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
setCurrentOpenTab("security"); setCurrentOpenTab("security");
localStorage.setItem("lastTabOpenByUser", "security"); localStorage.setItem("lastTabOpenByUser", "security");
}} }}
sx={{ style={{
...ButtonStyle, ...ButtonStyle,
backgroundColor: backgroundColor:
((currentOpenTab === "security" && currentPath.includes("/security")) || ((currentOpenTab === "security" && currentPath.includes("/security")) ||
@@ -1080,11 +1166,19 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
(currentPath.includes("detections") || currentPath.includes("response")))) (currentPath.includes("detections") || currentPath.includes("response"))))
? "#2f2f2f" ? "#2f2f2f"
: "transparent", : "transparent",
"&:hover": {
backgroundColor: userdata?.support ? "#2f2f2f" : "transparent",
},
cursor: userdata?.support ? "pointer" : "not-allowed", 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 <ShieldOutlinedIcon
style={{ style={{
@@ -1105,19 +1199,25 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
: "#6F6F6F", : "#6F6F6F",
}} }}
> >
Security Content
</span> </span>
</Button> </Button>
</Link> </Link>
</span> </span>
<IconButton <IconButton
disabled={true}
onClick={() => { onClick={() => {
setOpenSecurityTab((prev) => !prev); setOpenSecurityTab((prev) => !prev);
setOpenautomateTab(false); setOpenautomateTab(false);
}} }}
sx={{ style={{
marginLeft: 0.625, marginLeft: 0.625,
color: "#FFFFFF",
}}
onMouseOver={(event)=>{
event.currentTarget.style.backgroundColor = "#2f2f2f";
}}
onMouseOut={(event)=>{
event.currentTarget.style.backgroundColor = "transparent";
}} }}
> >
{openSecurityTab ? ( {openSecurityTab ? (
@@ -1144,14 +1244,12 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
display: "flex", display: "flex",
flexDirection: "column", flexDirection: "column",
paddingLeft: 16, paddingLeft: 16,
gap: 4,
marginTop: expandLeftNav ? 16 : 0,
}} }}
disableRipple={expandLeftNav ? false : true} disableRipple={expandLeftNav ? false : true}
> >
<span style={{ display: "inline-block", width: "100%" }}> <span style={{ display: "inline-block", width: "100%" }}>
<Link <Link
to={userdata?.support ? "/detections" : "#"} to={"/forms"}
style={{ style={{
...hrefStyle, ...hrefStyle,
pointerEvents: userdata?.support ? "auto" : "none", pointerEvents: userdata?.support ? "auto" : "none",
@@ -1196,14 +1294,14 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
: "#6F6F6F", : "#6F6F6F",
}} }}
> >
Detection Forms
</span> </span>
</Button> </Button>
</Link> </Link>
</span> </span>
<span style={{ display: "inline-block", width: "100%" }}> <span style={{ display: "inline-block", width: "100%" }}>
<Link <Link
to={userdata?.support ? "/response" : "#"} to={"/admin?tab=datastore"}
style={{ style={{
...hrefStyle, ...hrefStyle,
pointerEvents: userdata?.support ? "auto" : "none", pointerEvents: userdata?.support ? "auto" : "none",
@@ -1230,7 +1328,6 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
}, },
cursor: userdata?.support ? "pointer" : "not-allowed", cursor: userdata?.support ? "pointer" : "not-allowed",
}} }}
disabled={!userdata?.support}
> >
<span style={{ position: "relative", left: !expandLeftNav ? 10 : 0, marginRight: 10, fontSize: 16 }}> <span style={{ position: "relative", left: !expandLeftNav ? 10 : 0, marginRight: 10, fontSize: 16 }}>
• •
@@ -1248,26 +1345,81 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
: "#6F6F6F", : "#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> </span>
</Button> </Button>
</Link> </Link>
</span> </span>
</Box> </Box>
</Collapse> </Collapse>
<Link to="/docs" style={hrefStyle}> <Link to="/docs" style={hrefStyle}>
<Button <Button
onClick={(event) => { onClick={(event) => {
setCurrentOpenTab("docs"); setCurrentOpenTab("docs");
localStorage.setItem("lastTabOpenByUser", "docs"); localStorage.setItem("lastTabOpenByUser", "docs");
}} }}
sx={{ style={{
...ButtonStyle, ...ButtonStyle,
marginTop: 2, marginTop: 8,
marginTop: 8,
backgroundColor: currentOpenTab === "docs" && currentPath.includes("/docs") ? "#2f2f2f": "transparent", 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 <img
@@ -1390,10 +1542,15 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
color: "#bbb", color: "#bbb",
fontSize: "16px", fontSize: "16px",
marginRight: 12, 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} {option.region_url}
</span>) : null </span>
) : null
} }
<img <img
src={option.image ? option.image : "/images/no_image.png"} src={option.image ? option.image : "/images/no_image.png"}
+1 -1
View File
@@ -127,7 +127,7 @@ const MFASetup = ({ isLoaded, globalUrl, setCookie }) => {
}; };
return ( 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" }}> <Paper elevation={3} style={{ padding: "30px", backgroundColor: "#212121" }}>
<Typography variant="h5" style={{ color: "white", marginBottom: 10, textAlign: "center" }}> <Typography variant="h5" style={{ color: "white", marginBottom: 10, textAlign: "center" }}>
Multi-Factor Authentication Setup Multi-Factor Authentication Setup
+24 -2
View File
@@ -110,8 +110,30 @@ const OrgHeader = (props) => {
orgDescription, orgDescription,
selectedOrganization.id, selectedOrganization.id,
selectedOrganization.image, 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, defaults,
sso_config sso_config
) => { ) => {
console.log("defatult in handleEditOrg", defaults)
const data = { const data = {
name: name, name: name,
description: description, description: description,
@@ -309,6 +309,7 @@ const OrgHeaderexpanded = (props) => {
openid_authorization: openidAuthorization, openid_authorization: openidAuthorization,
openid_token: openidToken, openid_token: openidToken,
SSORequired: SSORequired, SSORequired: SSORequired,
auto_provision: selectedOrganization?.sso_config?.auto_provision,
} }
) )
} }
@@ -485,6 +486,7 @@ const OrgHeaderexpanded = (props) => {
value={data} value={data}
onClick={(e) => { onClick={(e) => {
var parsedinput = { target: { value: data } } var parsedinput = { target: { value: data } }
console.log("Parsed input: ", parsedinput)
handleWorkflowSelectionUpdate(parsedinput) handleWorkflowSelectionUpdate(parsedinput)
}} }}
> >
@@ -753,7 +755,7 @@ const OrgHeaderexpanded = (props) => {
{SSORequired ? "Required" : "Optional"} {SSORequired ? "Required" : "Optional"}
</div> </div>
</div> </div>
<div {/* <div
style={{ style={{
display: "flex", display: "flex",
flexDirection: "column", flexDirection: "column",
@@ -800,7 +802,7 @@ const OrgHeaderexpanded = (props) => {
</Button> </Button>
</span> </span>
</Tooltip> </Tooltip>
</div> </div> */}
<div></div> <div></div>
<Grid item xs={12} style={{}}> <Grid item xs={12} style={{}}>
<Typography variant="h6" style={{ textAlign: "center" }}> <Typography variant="h6" style={{ textAlign: "center" }}>
+33 -30
View File
@@ -53,6 +53,7 @@ import {
import { import {
HelpOutline as HelpOutlineIcon, HelpOutline as HelpOutlineIcon,
OpenInFull as OpenInFullIcon,
Description as DescriptionIcon, Description as DescriptionIcon,
GetApp as GetAppIcon, GetApp as GetAppIcon,
Search as SearchIcon, Search as SearchIcon,
@@ -2090,7 +2091,7 @@ const ParsedAction = (props) => {
{data?.validation?.valid === true ? {data?.validation?.valid === true ?
<Tooltip title="Authentication has been validated" placement="top"> <Tooltip title="Authentication has been validated" placement="top">
<Chip <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"} label={"Valid"}
variant="outlined" variant="outlined"
color="secondary" color="secondary"
@@ -2099,7 +2100,7 @@ const ParsedAction = (props) => {
: null } : null }
{data?.last_modified === true ? {data?.last_modified === true ?
<Chip <Chip
style={{marginLeft: 0, padding: 0, marginRight: 10, cursor: "pointer",}} style={{marginLeft: 0, padding: 0, marginRight: 10, cursor: "pointer", maxHeight: 25, }}
label={"Latest"} label={"Latest"}
variant="outlined" variant="outlined"
color="secondary" color="secondary"
@@ -3452,36 +3453,10 @@ const ParsedAction = (props) => {
disableUnderline: true, disableUnderline: true,
endAdornment: hideExtraTypes ? null : ( endAdornment: hideExtraTypes ? null : (
<InputAdornment position="end"> <InputAdornment position="end">
<ButtonGroup orientation={multiline ? "vertical" : "horizontal"}> <ButtonGroup color="secondary" 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>
<Tooltip title="Autocomplete text" placement="bottom"> <Tooltip title="Autocomplete text" placement="bottom">
<AddCircleOutlineIcon <AddCircleOutlineIcon
style={{ cursor: "pointer", margin: multiline ? 5 : 0, }} style={{ color: "rgba(255,255,255,0.7)", cursor: "pointer", margin: multiline ? 5 : 0, }}
onClick={(event) => { onClick={(event) => {
event.preventDefault() event.preventDefault()
@@ -4355,6 +4330,34 @@ const ParsedAction = (props) => {
{tmpitem} <span style={{color: theme.palette.main}}>{selectedActionParameters[count].required || selectedActionParameters[count].configuration ? "*" : ""}</span> {tmpitem} <span style={{color: theme.palette.main}}>{selectedActionParameters[count].required || selectedActionParameters[count].configuration ? "*" : ""}</span>
</div> </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> </div>
{datafield} {datafield}
{/*shufflecode*/} {/*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 { toast } from "react-toastify";
import theme from "../theme.jsx"; import theme from "../theme.jsx";
@@ -13,18 +13,21 @@ import {
Card, Card,
Chip, Chip,
Switch, Switch,
Skeleton,
} from "@mui/material"; } from "@mui/material";
import { Context } from "../context/ContextApi.jsx";
import { useNavigate, Link } from "react-router-dom"; import { useNavigate, Link } from "react-router-dom";
import Priority from "../components/Priority.jsx"; import Priority from "../components/Priority.jsx";
import { constrainMatrix } from "reaviz";
//import { useAlert //import { useAlert
const Priorities = (props) => { const Priorities = memo((props) => {
const { globalUrl, userdata,clickedFromOrgTab, serverside, billingInfo, stripeKey, checkLogin, setAdminTab, setCurTab, notifications, setNotifications, } = props; const { globalUrl, userdata,clickedFromOrgTab, serverside, billingInfo, stripeKey, checkLogin, setAdminTab, setCurTab, notifications, setNotifications, } = props;
const [showDismissed, setShowDismissed] = React.useState(false); const [showDismissed, setShowDismissed] = React.useState(false);
const [showRead, setShowRead] = React.useState(false); const [showRead, setShowRead] = React.useState(false);
const [appFramework, setAppFramework] = React.useState({}); const [appFramework, setAppFramework] = React.useState({});
const [selectedWorkflow, setSelectedWorkflow] = React.useState("NO HIGHLIGHT"); const [selectedWorkflow, setSelectedWorkflow] = React.useState("NO HIGHLIGHT");
const [selectedExecutionId, setSelectedExecutionId] = React.useState("NO HIGHLIGHT"); const [selectedExecutionId, setSelectedExecutionId] = React.useState("NO HIGHLIGHT");
const [highlightKMS, setHighlightKMS] = React.useState(false) const [highlightKMS, setHighlightKMS] = React.useState(false)
@@ -216,8 +219,105 @@ const Priorities = (props) => {
const notificationWidth = "100%" const notificationWidth = "100%"
const imagesize = 22 const imagesize = 22
const boxColor = "#86c142" 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 image = "";
var orgName = ""; var orgName = "";
@@ -280,7 +380,7 @@ const Priorities = (props) => {
return ( return (
<Paper <Paper
style={{ style={{
backgroundColor: theme.palette.platformColor, backgroundColor: theme.palette.inputColor.backgroundColor,
width: clickedFromOrgTab ? null :notificationWidth, width: clickedFromOrgTab ? null :notificationWidth,
padding: 30, padding: 30,
borderBottom: "1px solid rgba(255,255,255,0.4)", borderBottom: "1px solid rgba(255,255,255,0.4)",
@@ -321,7 +421,7 @@ const Priorities = (props) => {
style={{marginRight: 15, height: 25, }} 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} {data.title}
</Typography > </Typography >
</div> </div>
@@ -331,7 +431,7 @@ const Priorities = (props) => {
: :
null 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} {data.description}
</Typography > </Typography >
<div style={{ display: "flex" }}> <div style={{ display: "flex" }}>
@@ -377,121 +477,62 @@ const Priorities = (props) => {
</Tooltip> </Tooltip>
</ButtonGroup> </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)} <b>First seen</b>: {new Date(data.created_at * 1000).toISOString().slice(0, 19)}
</Typography > </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)} <b>Last seen</b>: {new Date(data.updated_at * 1000).toISOString().slice(0, 19)}
</Typography > </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} <b>Times seen</b>: {data.amount}
</Typography > </Typography >
</div> </div>
</Paper> </Paper>
); );
} })
return (
<div style={{width: clickedFromOrgTab ? 1030:1000, padding: clickedFromOrgTab ? 27:null, height: clickedFromOrgTab ? "auto":null, backgroundColor: clickedFromOrgTab ? '#212121':null, borderRadius: clickedFromOrgTab ? '16px':null, }}>
<h2 style={{ display: clickedFromOrgTab?null:"inline", marginBottom: clickedFromOrgTab? 8:null, marginTop: clickedFromOrgTab?40:null, color: clickedFromOrgTab?"#ffffff":null }}>Notifications ({
notifications?.filter((notification) => showRead === true || notification.read === false).length
})</h2>
<span style={{ marginLeft: clickedFromOrgTab?null:25, color: clickedFromOrgTab?"#9E9E9E":null, }}> const NotificationComponent = memo(({notifications, showRead, selectedExecutionId, selectedWorkflow, highlightKMS, userdata, imagesize, boxColor, clickedFromOrgTab, notificationWidth, dismissNotification}) => {
Notifications help you find potential problems with your workflows and apps.&nbsp;
<a return(
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> <div>
{notifications.map((notification, index) => { {notifications === null || notifications === undefined || notifications?.length === 0 ? (
null
) :
<div>
{notifications?.map((notification, index) => {
if (showRead === false && notification.read === true) { if (showRead === false && notification.read === true) {
return null return null
} }
return ( 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> </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> </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, maxHeight: isMobile ? "100%" : 700,
border: theme.palette.defaultBorder, border: theme.palette.defaultBorder,
padding: isMobile ? "25px 10px 25px 10px" : 25, padding: isMobile ? "25px 10px 25px 10px" : 25,
zoom: 0.8,
backgroundColor: "black",
}, },
}} }}
> >
+1 -1
View File
@@ -382,7 +382,7 @@ const data = [
css: { css: {
"background-color": "#f85a3e", "background-color": "#f85a3e",
"border-color": "#f85a3e", "border-color": "#f85a3e",
"border-width": "12px", "border-width": "7px",
"transition-property": "border-width", "transition-property": "border-width",
"transition-duration": "0.25s", "transition-duration": "0.25s",
label: "data(label)", label: "data(label)",
+6
View File
@@ -50,6 +50,12 @@ const theme = createTheme(adaptV4Theme({
borderRadius: 5, borderRadius: 5,
height: 40, height: 40,
}, },
DialogStyle: {
backgroundColor: "#212121",
borderRadius: 2,
boxShadow: "0px 0px 10px 0px rgba(0,0,0,0.75)",
border: "1px solid #494949",
},
innerTextfieldStyle: { innerTextfieldStyle: {
height: 40, height: 40,
fontSize: 16, fontSize: 16,
+109 -14
View File
@@ -4,13 +4,13 @@ import { toast } from "react-toastify";
const Admin2 = (props) => { const Admin2 = (props) => {
// Destructure props if needed // 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 [selectedTab, setSelectedTab] = useState('editdetails');
const [selectedStatus, setSelectedStatus] = React.useState([]); const [selectedStatus, setSelectedStatus] = React.useState([]);
const [selectedOrganization, setSelectedOrganization] = useState({}); const [selectedOrganization, setSelectedOrganization] = useState({});
const [organizationFeatures, setOrganizationFeatures] = useState({}); const [organizationFeatures, setOrganizationFeatures] = useState({});
const [orgRequest, setOrgRequest] = React.useState(true); const [orgRequest, setOrgRequest] = React.useState(true);
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
const handleGetOrg = (orgId) => { const handleGetOrg = (orgId) => {
if ( if (
serverside !== true && serverside !== true &&
@@ -51,8 +51,11 @@ const Admin2 = (props) => {
.then((responseJson) => { .then((responseJson) => {
if (responseJson["success"] === false) { if (responseJson["success"] === false) {
toast( 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 { } else {
if ( if (
responseJson.sync_features === undefined || 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 = ( const handleEditOrg = (
name, name,
description, description,
@@ -159,6 +233,7 @@ const Admin2 = (props) => {
defaults, defaults,
sso_config, sso_config,
lead_info, lead_info,
{ mfa_required } = {}
) => { ) => {
const data = { const data = {
name: name, name: name,
@@ -168,6 +243,7 @@ const Admin2 = (props) => {
defaults: defaults, defaults: defaults,
sso_config: sso_config, sso_config: sso_config,
lead_info: lead_info, lead_info: lead_info,
mfa_required: mfa_required !== undefined ? mfa_required : selectedOrganization?.mfa_required,
}; };
const url = globalUrl + `/api/v1/orgs/${selectedOrganization.id}`; const url = globalUrl + `/api/v1/orgs/${selectedOrganization.id}`;
@@ -202,21 +278,40 @@ const Admin2 = (props) => {
}); });
}; };
// useEffect(() => {
// handleGetOrg();
// }, []);
const handleStatusChange = (event) => { const handleStatusChange = (event) => {
const { value } = event.target; const { value } = event.target;
setSelectedStatus(value); setSelectedStatus(value);
handleEditOrg( handleEditOrg(
"", selectedOrganization?.name,
"", selectedOrganization?.description,
selectedOrganization.id, 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, value.length === 0 ? ["none"] : value,
); );
}; };
@@ -228,14 +323,14 @@ const Admin2 = (props) => {
orgRequest orgRequest
) { ) {
const orgId = userdata.active_org.id const orgId = userdata.active_org.id
console.log("orgID", orgId)
setOrgRequest(false); setOrgRequest(false);
handleGetOrg(orgId); handleGetOrg(orgId);
} }
return ( return (
<div style={{ display: 'flex', justifyContent: 'center', paddingTop: 30 }}> <div style={{ display: 'flex', justifyContent: 'center', paddingTop: 29, zoom: 0.8, }}>
<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} /> <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> </div>
); );
}; };
+110 -65
View File
@@ -422,7 +422,7 @@ const AngularWorkflow = (defaultprops) => {
const [subworkflow, setSubworkflow] = React.useState({}); const [subworkflow, setSubworkflow] = React.useState({});
const [subworkflowStartnode, setSubworkflowStartnode] = React.useState(""); const [subworkflowStartnode, setSubworkflowStartnode] = React.useState("");
const [leftViewOpen, setLeftViewOpen] = React.useState(isMobile ? false : true); 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 [creatorProfile, setCreatorProfile] = React.useState({});
const [usecases, setUsecases] = React.useState([]); const [usecases, setUsecases] = React.useState([]);
const [files, setFiles] = 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", "node", (e) => onNodeAdded(e));
cy.on("add", "edge", (e) => onEdgeAdded(e)); cy.on("add", "edge", (e) => onEdgeAdded(e));
} else { } else {
@@ -4052,7 +4052,7 @@ const releaseToConnectLabel = "Release to Connect"
.then(() => { .then(() => {
console.log("DONE: ", workflow_id); console.log("DONE: ", workflow_id);
getWorkflow(workflow_id.value, nodedata); 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 (nodedata.app_name === "Webhook" || nodedata.app_name === "Schedule" || nodedata.app_name === "Gmail" || nodedata.app_name === "Office365") {
if (!found) { 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 // Find how many executions it has
var executions = 0 var executions = 0
const matchingExecutions = workflowExecutions.filter((execution => execution.execution_source === nodedata.app_name.toLowerCase())) const matchingExecutions = workflowExecutions.filter((execution => execution.execution_source === nodedata.app_name.toLowerCase()))
@@ -6121,7 +6121,7 @@ const releaseToConnectLabel = "Release to Connect"
event.target.remove() event.target.remove()
//console.log("Found branch already!") //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 return
@@ -7782,7 +7782,7 @@ const releaseToConnectLabel = "Release to Connect"
const item = typeIds[idkey] const item = typeIds[idkey]
if (item.data.id === nodedata.id) { if (item.data.id === nodedata.id) {
//console.log("items: ", item.data.id, nodedata.id) //console.log("items: ", item.data.id, nodedata.id)
parsedStyle["border-width"] = "12px" parsedStyle["border-width"] = "7px"
break break
} }
} }
@@ -8391,7 +8391,7 @@ const releaseToConnectLabel = "Release to Connect"
// Reset view for cytoscape // Reset view for cytoscape
if (cy !== undefined && cy !== null) { if (cy !== undefined && cy !== null) {
cy.add(insertedNodes); cy.add(insertedNodes);
cy.fit(null, 200); cy.fit(null, 400);
} else { } else {
setElements(insertedNodes); setElements(insertedNodes);
} }
@@ -8772,7 +8772,7 @@ const releaseToConnectLabel = "Release to Connect"
} }
// preview: true, // preview: true,
cy.fit(null, 200); cy.fit(null, 400)
cy.on("boxselect", "node", (e) => { cy.on("boxselect", "node", (e) => {
if (e.target.data("isButton") || e.target.data("isDescriptor") || e.target.data("isSuggestion")) { if (e.target.data("isButton") || e.target.data("isDescriptor") || e.target.data("isSuggestion")) {
@@ -9380,7 +9380,9 @@ const releaseToConnectLabel = "Release to Connect"
onChange={handleSetTab} onChange={handleSetTab}
aria-label="Left sidebar tab" aria-label="Left sidebar tab"
orientation={isMobile ? "vertical" : "horizontal"} orientation={isMobile ? "vertical" : "horizontal"}
style={{}} style={{
display: "flex",
}}
> >
<Tab <Tab
value={0} value={0}
@@ -9421,7 +9423,7 @@ const releaseToConnectLabel = "Release to Connect"
<Grid item> <Grid item>
<FavoriteBorderIcon style={iconStyle} /> <FavoriteBorderIcon style={iconStyle} />
</Grid> </Grid>
{isMobile ? null : <Grid item>Variables</Grid>} {isMobile ? null : <Grid item>Vars</Grid>}
</Grid> </Grid>
} }
style={tabStyle} style={tabStyle}
@@ -9776,11 +9778,13 @@ const releaseToConnectLabel = "Release to Connect"
const barHeight = bodyHeight - appBarSize - 50; const barHeight = bodyHeight - appBarSize - 50;
const appScrollStyle = { const appScrollStyle = {
overflow: "scroll", overflow: "scroll",
maxHeight: isMobile ? bodyHeight - appBarSize * 4 : barHeight-300, maxHeight: isMobile ? bodyHeight - appBarSize * 4 : barHeight-70,
minHeight: isMobile ? bodyHeight - appBarSize * 4 : barHeight-300, minHeight: isMobile ? bodyHeight - appBarSize * 4 : barHeight-70,
marginTop: 1, marginTop: 1,
overflowY: "auto", overflowY: "auto",
overflowX: "hidden", overflowX: "hidden",
zoom: 0.9,
} }
const handleAppDrag = (e, app) => { const handleAppDrag = (e, app) => {
@@ -10568,12 +10572,12 @@ const releaseToConnectLabel = "Release to Connect"
var viewedApps = [] var viewedApps = []
return ( return (
<div style={appViewStyle}> <div style={appViewStyle}>
<div style={{ flex: "1" }}> <div style={{ flex: "1", zoom: 0.9, }}>
<TextField <TextField
style={{ style={{
backgroundColor: theme.palette.inputColor, backgroundColor: theme.palette.inputColor,
borderRadius: theme.palette?.borderRadius, borderRadius: theme.palette?.borderRadius,
marginTop: 5, marginTop: 10,
marginRight: 10, marginRight: 10,
height: 40, height: 40,
}} }}
@@ -11199,8 +11203,6 @@ const releaseToConnectLabel = "Release to Connect"
top: appBarSize - 35, top: appBarSize - 35,
right: 25, right: 25,
height: "90vh", height: "90vh",
width: isMobile ? "100%" : minSize,
minWidth: minSize,
maxWidth: 600, maxWidth: 600,
maxHeight: "100vh", maxHeight: "100vh",
border: "1px solid rgb(91, 96, 100)", border: "1px solid rgb(91, 96, 100)",
@@ -11210,6 +11212,9 @@ const releaseToConnectLabel = "Release to Connect"
overflow: "auto", overflow: "auto",
overflowAnchor: "none", overflowAnchor: "none",
minWidth: minSize,
width: isMobile ? "100%" : minSize,
zoom: 0.9,
}; };
const setTriggerFolderWrapperMulti = (event) => { const setTriggerFolderWrapperMulti = (event) => {
@@ -16153,13 +16158,15 @@ const releaseToConnectLabel = "Release to Connect"
position: "absolute", position: "absolute",
top: isMobile ? 30 : 25, 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", transition: "left 0.3s ease",
maxWidth: 500, zoom: 0.9,
} }
const TopCytoscapeBar = (props) => { const TopCytoscapeBar = (props) => {
const [hovered, setHovered] = useState(false)
if (workflow.public === true) { if (workflow.public === true) {
return null return null
} }
@@ -16174,12 +16181,30 @@ const releaseToConnectLabel = "Release to Connect"
<div style={topBarStyle}> <div style={topBarStyle}>
<div style={{ <div style={{
margin: "0px 10px 0px 35px", margin: "0px 10px 0px 35px",
maxWidth: 500,
}}> }}>
<Typography variant="h6" style={{ <Typography variant="h6" style={{
margin: 0, margin: 0,
}}> cursor: "pointer",
{workflow.name} 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> </Typography>
{workflowAsCode && ( {workflowAsCode && (
<Tooltip title="Switch to Code View"> <Tooltip title="Switch to Code View">
@@ -16204,7 +16229,7 @@ const releaseToConnectLabel = "Release to Connect"
{!distributedFromParent ? {!distributedFromParent ?
isCorrectOrg ? null : isCorrectOrg ? null :
<Typography variant="body1"> <Typography variant="body2">
<b>Warning</b>: Change <span <b>Warning</b>: Change <span
style={{color: "#FF8544", cursor: "pointer", pointerEvents: "auto", }} style={{color: "#FF8544", cursor: "pointer", pointerEvents: "auto", }}
onClick={() => { onClick={() => {
@@ -16275,7 +16300,7 @@ const releaseToConnectLabel = "Release to Connect"
>Active Organization</span> to edit this Workflow. >Active Organization</span> to edit this Workflow.
</Typography> </Typography>
: :
<Typography variant="body1"> <Typography variant="body2" color="textSecondary">
Warning: This workflow is controlled by your parent org and may not be editable. Warning: This workflow is controlled by your parent org and may not be editable.
</Typography> </Typography>
} }
@@ -16285,7 +16310,7 @@ const releaseToConnectLabel = "Release to Connect"
<InputLabel <InputLabel
id="suborg-changer" id="suborg-changer"
style={{ color: "white" }} style={{ color: "rgba(255,255,255,0.7)", }}
> >
Select an Org Select an Org
</InputLabel> </InputLabel>
@@ -16294,10 +16319,15 @@ const releaseToConnectLabel = "Release to Connect"
pointerEvents: "auto", pointerEvents: "auto",
backgroundColor: theme.palette.inputColor, backgroundColor: theme.palette.inputColor,
color: "white", color: "white",
height: 50,
maxWidth: 250, maxWidth: 250,
minWidth: 250, minWidth: 250,
borderRadius: theme.palette?.borderRadius, borderRadius: theme.palette?.borderRadius,
height: 40,
}}
InputProps={{
style: {
height: 40,
}
}} }}
labelId="suborg-changer" labelId="suborg-changer"
value={workflow.org_id} value={workflow.org_id}
@@ -16470,47 +16500,16 @@ const releaseToConnectLabel = "Release to Connect"
</Select> </Select>
</FormControl> </FormControl>
} }
</div>
<div style={{display: "flex", marginLeft: 10, maxWidth: 250, pointerEvents: "auto", }}>
{parentWorkflows.slice(0,5).map((wf, index) => {
return (
<a href={`/workflows/${wf.id}`} target="_blank" rel="noopener noreferrer" key={index}>
<Tooltip arrow placement="left" title={
<span style={{}}>
{wf.image !== undefined && wf.image !== null && wf.image.length > 0 ?
<img
src={wf.image}
alt={wf.name}
style={{ backgroundColor: theme.palette.surfaceColor, maxHeight: 200, minHeigth: 200, borderRadius: theme.palette?.borderRadius, }}
/>
: null}
<Typography>
Parent workflow: '{wf.name}'
</Typography>
</span>
} placement="bottom">
<span onClick={() => {
console.log("Click: ", wf)
}}>
<img src={theme.palette.defaultImage} style={{height: 25, width: 25, cursor: "pointer", border: 15, marginRight: 5, marginTop: 5, filter: "grayscale(90%)", }} />
</span>
</Tooltip>
</a>
)
})}
</div> </div>
{showEnvironment === true && environments.length > 1 && selectedActionEnvironment !== undefined && selectedActionEnvironment !== null && selectedActionEnvironment.Name !== undefined && selectedActionEnvironment.Name !== null ? {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 <InputLabel
id="execution_location" id="execution_location"
style={{ color: "white" }} style={{ color: "rgba(255,255,255,0.7)", marginLeft: 40, }}
> >
Execution Location Location
</InputLabel> </InputLabel>
<Select <Select
labelId="execution_location" labelId="execution_location"
@@ -16523,6 +16522,11 @@ const releaseToConnectLabel = "Release to Connect"
style: { style: {
}, },
}} }}
InputProps={{
style: {
height: 40,
}
}}
onChange={(e) => { onChange={(e) => {
setLastSaved(false) setLastSaved(false)
const env = environments.find((a) => a.Name === e.target.value); const env = environments.find((a) => a.Name === e.target.value);
@@ -16539,13 +16543,14 @@ const releaseToConnectLabel = "Release to Connect"
}} }}
style={{ style={{
pointerEvents: "auto", pointerEvents: "auto",
backgroundColor: theme.palette.inputColor,
color: "white", color: "white",
height: 50,
maxWidth: 250, maxWidth: 250,
minWidth: 250, minWidth: 250,
borderRadius: theme.palette?.borderRadius, borderRadius: theme.palette?.borderRadius,
marginLeft: 10, marginLeft: 35,
backgroundColor: theme.palette.inputColor,
height: 40,
}} }}
> >
{environments.map((data, index) => { {environments.map((data, index) => {
@@ -16603,6 +16608,43 @@ const releaseToConnectLabel = "Release to Connect"
</FormControl> </FormControl>
: null} : 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> </div>
); );
}; };
@@ -17210,7 +17252,7 @@ const releaseToConnectLabel = "Release to Connect"
<Tooltip color="primary" title="Stop execution" placement="top"> <Tooltip color="primary" title="Stop execution" placement="top">
<span> <span>
<Button <Button
style={{ height: boxSize, width: boxSize }} style={{ height: boxSize, width: boxSize+5 }}
color="secondary" color="secondary"
variant="contained" variant="contained"
onClick={() => { onClick={() => {
@@ -17228,7 +17270,7 @@ const releaseToConnectLabel = "Release to Connect"
workflow.public workflow.public
|| executionRequestStarted || executionRequestStarted
} }
style={{ height: boxSize, width: boxSize, backgroundColor: green, }} style={{ height: boxSize, width: boxSize+5, backgroundColor: green, }}
color="primary" color="primary"
variant="contained" variant="contained"
onClick={() => { onClick={() => {
@@ -17265,7 +17307,7 @@ const releaseToConnectLabel = "Release to Connect"
{executionButton} {executionButton}
<Tooltip <Tooltip
color="secondary" color="secondary"
title={`Show previous runs (${workflowExecutions.length}) (Ctrl + ')`} title={`Show previous workflow runs (${workflowExecutions.length}) (Ctrl + ')`}
placement="top-start" placement="top-start"
> >
<Button <Button
@@ -17492,6 +17534,8 @@ const releaseToConnectLabel = "Release to Connect"
workflow.configuration.exit_on_error !== undefined ? ( workflow.configuration.exit_on_error !== undefined ? (
<WorkflowMenu /> <WorkflowMenu />
) : null} ) : null}
{/*
<Tooltip <Tooltip
color="secondary" color="secondary"
title="Edit workflow details" title="Edit workflow details"
@@ -17514,6 +17558,7 @@ const releaseToConnectLabel = "Release to Connect"
</Button> </Button>
</span> </span>
</Tooltip> </Tooltip>
*/}
<Tooltip <Tooltip
color="secondary" color="secondary"
+7 -4
View File
@@ -1,3 +1,4 @@
import React, { memo, useCallback } from "react"; import React, { memo, useCallback } from "react";
import { useState, useEffect, useContext, Suspense } from "react"; import { useState, useEffect, useContext, Suspense } from "react";
import { useNavigate, Link, useLocation } from "react-router-dom"; import { useNavigate, Link, useLocation } from "react-router-dom";
@@ -57,7 +58,7 @@ const ApiExplorer = React.lazy(() => import("../components/ApiExplorer.jsx"));
const ApiExplorerWrapper = (props) => { 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 isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"
const location = useLocation(); const location = useLocation();
const navigate = useNavigate(); const navigate = useNavigate();
@@ -1754,7 +1755,7 @@ const ApiExplorerWrapper = (props) => {
)}); )});
return ( return (
<Wrapper userdata={userdata}> <Wrapper isLoggedIn={isLoggedIn} isLoaded={isLoaded}>
<Suspense fallback={skeletonLoader}> <Suspense fallback={skeletonLoader}>
{authenticationModal} {authenticationModal}
<ApiExplorer <ApiExplorer
@@ -1765,6 +1766,8 @@ const ApiExplorerWrapper = (props) => {
HandleApiExecution={HandleApiExecution} HandleApiExecution={HandleApiExecution}
selectedAppData={selectedAppData} selectedAppData={selectedAppData}
ConfigurationTab={ConfigurationTab} ConfigurationTab={ConfigurationTab}
isLoggedIn={isLoggedIn}
isLoaded={isLoaded}
/> />
</Suspense> </Suspense>
</Wrapper> </Wrapper>
@@ -1774,13 +1777,13 @@ const ApiExplorerWrapper = (props) => {
export default ApiExplorerWrapper; export default ApiExplorerWrapper;
const Wrapper = ({children, userdata})=>{ const Wrapper = ({children, isLoaded,isLoggedIn})=>{
const { leftSideBarOpenByClick } = useContext(Context); const { leftSideBarOpenByClick } = useContext(Context);
return( 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} {children}
</div> </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 {leftSideBarOpenByClick } = useContext(Context)
const marginLeft = userdata?.support
? leftSideBarOpenByClick ? 250 : 80
: 0;
return ( return (
<div style={{ marginLeft, transition: 'margin-left 0.3s ease' }}> <div style={{ marginLeft: leftSideBarOpenByClick ? 250 : 80, transition: 'margin-left 0.3s ease' }}>
{children} {children}
</div> </div>
); );
+287 -121
View File
@@ -36,6 +36,7 @@ import algoliasearch from "algoliasearch/lite";
import { debounce } from "lodash"; import { debounce } from "lodash";
import AppSelection from "../components/AppSelection.jsx"; import AppSelection from "../components/AppSelection.jsx";
import AppModal from "../components/AppModal.jsx"; import AppModal from "../components/AppModal.jsx";
import AppCreationModal from "../components/AppCreationModal.jsx";
const searchClient = algoliasearch( const searchClient = algoliasearch(
@@ -45,6 +46,7 @@ const searchClient = algoliasearch(
// AppCard Component // AppCard Component
const AppCard = ({ data, index, mouseHoverIndex, setMouseHoverIndex, globalUrl, deactivatedIndexes, currTab, handleAppClick, leftSideBarOpenByClick, userdata }) => { 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 isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io" || window.location.host === "localhost:3000";
const appUrl = isCloud ? `/apps/${data.id}` : `https://shuffler.io/apps/${data.id}`; const appUrl = isCloud ? `/apps/${data.id}` : `https://shuffler.io/apps/${data.id}`;
var canEditApp = userdata.admin === "true" || userdata.id === data?.owner || data?.owner === "" || (userdata.admin === "true" && userdata.active_org.id === data?.reference_org) || !data?.generated 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", backgroundColor: mouseHoverIndex === index ? "rgba(26, 26, 26, 1)" : "#212121",
color: "rgba(241, 241, 241, 1)", color: "rgba(241, 241, 241, 1)",
cursor: "pointer", cursor: "pointer",
fontFamily: "Inter", fontFamily: theme?.typography?.fontFamily,
// position: "relative", // position: "relative",
width: "100%", width: "100%",
height: 96, height: 96,
@@ -76,7 +78,7 @@ const AppCard = ({ data, index, mouseHoverIndex, setMouseHoverIndex, globalUrl,
fontSize: 16, fontSize: 16,
overflow: "hidden", overflow: "hidden",
display: "flex", display: "flex",
fontFamily: "Inter", fontFamily: theme?.typography?.fontFamily,
width: '100%', width: '100%',
backgroundColor: mouseHoverIndex === index ? "#2F2F2F" : "#212121" backgroundColor: mouseHoverIndex === index ? "#2F2F2F" : "#212121"
}} }}
@@ -105,7 +107,7 @@ const AppCard = ({ data, index, mouseHoverIndex, setMouseHoverIndex, globalUrl,
fontWeight: '400', fontWeight: '400',
overflow: "hidden", overflow: "hidden",
margin: "12px 0", margin: "12px 0",
fontFamily: "Inter" fontFamily: theme?.typography?.fontFamily
}}> }}>
<div style={{ <div style={{
display: 'flex', display: 'flex',
@@ -138,8 +140,14 @@ const AppCard = ({ data, index, mouseHoverIndex, setMouseHoverIndex, globalUrl,
display: "flex", display: "flex",
justifyContent: 'space-between', justifyContent: 'space-between',
paddingRight: 15, 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) => ( {data.generated !== true && data.tags && data.tags.slice(0, 2).map((tag, tagIndex) => (
<span key={tagIndex}> <span key={tagIndex}>
{tag} {tag}
@@ -151,31 +159,46 @@ const AppCard = ({ data, index, mouseHoverIndex, setMouseHoverIndex, globalUrl,
{currTab === 0 && !deactivatedIndexes.includes(index) && mouseHoverIndex === index && data.generated === true && ( {currTab === 0 && !deactivatedIndexes.includes(index) && mouseHoverIndex === index && data.generated === true && (
<div style={{ <div style={{
display: "flex", display: "flex",
gap: 8 gap: 8,
alignItems: 'center',
paddingRight: 20
}}> }}>
{ {
canEditApp && ( 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 /> <EditIcon />
</button> </button>
) )
} }
<Button <Button
className="deactivate-button" className="deactivate-button"
style={{ sx={{
// marginLeft: 15,
width: 102, width: 102,
height: 35, height: 35,
borderRadius: 3, borderRadius: 0.75,
backgroundColor: "rgba(73, 73, 73, 1)", bgcolor: "rgba(73, 73, 73, 1)",
color: "rgba(241, 241, 241, 1)", color: "rgba(241, 241, 241, 1)",
textTransform: "none", textTransform: "none",
fontFamily: "Inter" fontFamily: theme?.typography?.fontFamily,
transition: "background-color 0.3s ease",
"&:hover": {
bgcolor: "rgba(93, 93, 93, 1)",
},
}} }}
onClick={(event) => { onClick={(event) => {
event.preventDefault();
event.stopPropagation(); event.stopPropagation();
const url = `${globalUrl}/api/v1/apps/${data.id}/deactivate`; const url = `${globalUrl}/api/v1/apps/${data.id}/deactivate`;
toast("Deactivating app. Please wait...");
fetch(url, { fetch(url, {
method: 'GET', method: 'GET',
headers: { headers: {
@@ -189,7 +212,7 @@ const AppCard = ({ data, index, mouseHoverIndex, setMouseHoverIndex, globalUrl,
if (responseJson.success === false) { if (responseJson.success === false) {
toast.error(responseJson.reason); toast.error(responseJson.reason);
} else { } else {
toast.success("App Deactivated Successfully. Reload UI to see updated changes."); toast.success("App Deactivated Successfully.");
} }
}) })
.catch(error => { .catch(error => {
@@ -218,14 +241,35 @@ const Hits = ({
setIsAnyAppActivated, setIsAnyAppActivated,
searchQuery, searchQuery,
globalUrl, globalUrl,
isLoading,
isLoggedIn, isLoggedIn,
currTab,
leftSideBarOpenByClick leftSideBarOpenByClick
}) => { }) => {
const [hoverEffect, setHoverEffect] = useState(-1); 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 isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
const [deactivatedIndexes, setDeactivatedIndexes] = React.useState([]); 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) => { const normalizedString = (name) => {
if (typeof name === 'string') { 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 //Function for activation and deactivation of app
const handleActivateButton = (event, data, type) => { const handleActivateButton = (event, data, type) => {
//use prevent default so it will stop redirection to the app page //use prevent default so it will stop redirection to the app page
event.preventDefault(); event.preventDefault();
event.stopPropagation();
if (!isLoggedIn) { if (!isLoggedIn) {
toast.error("Please log in to your account to activate the app.") toast.error("Please log in to your account to activate the app.")
return; return;
@@ -287,25 +339,15 @@ const Hits = ({
let workflowDelay = 0; let workflowDelay = 0;
const isHeader = true; const isHeader = true;
const [showNoAppFound, setShowNoAppFound] = useState(false);
useEffect(() => {
const timer = setTimeout(() => {
setShowNoAppFound(true);
}, 1000);
return () => clearTimeout(timer);
}, []);
return ( return (
<div> <div>
{!isLoading ? ( {!isLoading ?
(
<div> <div>
{hits?.length === 0 && searchQuery.length >= 0 && showNoAppFound ? ( {hits?.length === 0 && searchQuery.length >= 0 ? (
<div style={{ marginTop: 100, fontSize: 20, fontWeight: 500, width: "100%", textAlign: "center" }}> <Typography>No apps found</Typography>
<Typography variant="body1">No Apps Found</Typography>
</div>
) : ( ) : (
<div <div
style={{ style={{
@@ -314,9 +356,11 @@ const Hits = ({
display: "grid", display: "grid",
gridTemplateColumns: "repeat(auto-fill, minmax(365px, 1fr))", gridTemplateColumns: "repeat(auto-fill, minmax(365px, 1fr))",
gap: "20px", gap: "20px",
fontFamily: theme?.typography?.fontFamily,
justifyContent: "space-between", justifyContent: "space-between",
alignItems: "start", alignItems: "start",
padding: "0 10px" padding: "0 10px",
paddingBottom: 40
}} }}
> >
{hits?.map((data, index) => { {hits?.map((data, index) => {
@@ -339,7 +383,7 @@ const Hits = ({
backgroundColor: hoverEffect === index ? "rgba(26, 26, 26, 1)" : "#212121", backgroundColor: hoverEffect === index ? "rgba(26, 26, 26, 1)" : "#212121",
color: "rgba(241, 241, 241, 1)", color: "rgba(241, 241, 241, 1)",
cursor: "pointer", cursor: "pointer",
fontFamily: "Inter", fontFamily: theme?.typography?.fontFamily,
// position: "relative", // position: "relative",
width: "100%", width: "100%",
height: 96, height: 96,
@@ -360,11 +404,12 @@ const Hits = ({
fontSize: 16, fontSize: 16,
overflow: "hidden", overflow: "hidden",
display: "flex", display: "flex",
fontFamily: "Inter",
width: '100%', width: '100%',
backgroundColor: hoverEffect === index ? "#2F2F2F" : "#212121" backgroundColor: hoverEffect === index ? "#2F2F2F" : "#212121",
fontFamily: theme?.typography?.fontFamily
}} }}
onClick={() => { onClick={() => {
console.log("App modal", data)
handleAppClick(data); handleAppClick(data);
}} }}
> >
@@ -390,7 +435,7 @@ const Hits = ({
fontWeight: '400', fontWeight: '400',
overflow: "hidden", overflow: "hidden",
margin: "12px 0", margin: "12px 0",
fontFamily: "Inter" fontFamily: theme?.typography?.fontFamily
}} }}
> >
<div <div
@@ -426,12 +471,16 @@ const Hits = ({
style={{ style={{
display: "flex", display: "flex",
justifyContent: 'space-between', justifyContent: 'space-between',
width: 230, width: "100%",
textAlign: 'start', textAlign: 'start',
color: "rgba(158, 158, 158, 1)", color: "rgba(158, 158, 158, 1)",
height: 35,
alignItems: 'center'
}} }}
> >
<div style={{ marginBottom: 15, }}> <div style={{
flex: 1
}}>
{hoverEffect === index && isCloud ? ( {hoverEffect === index && isCloud ? (
<div> <div>
{data.tags && ( {data.tags && (
@@ -463,7 +512,12 @@ const Hits = ({
)} )}
</div> </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 &&
data.tags.map((tag, tagIndex) => ( data.tags.map((tag, tagIndex) => (
<span key={tagIndex}> <span key={tagIndex}>
@@ -474,17 +528,25 @@ const Hits = ({
</div> </div>
)} )}
</div> </div>
<div style={{ position: 'relative', bottom: 5 }}> <div style={{
display: 'flex',
justifyContent: 'flex-end',
alignItems: 'center',
paddingRight: 10,
minWidth: 110
}}>
{hoverEffect === index && isCloud && ( {hoverEffect === index && isCloud && (
<div> <div>
{allActivatedAppIds && allActivatedAppIds.includes(data.objectID) ? ( {allActivatedAppIds && allActivatedAppIds.includes(data.objectID) ? (
<Button style={{ <Button
style={{
width: 102, width: 102,
height: 35, height: 35,
borderRadius: 200, borderRadius: 3,
backgroundColor: "rgba(73, 73, 73, 1)", backgroundColor: "rgba(73, 73, 73, 1)",
color: "rgba(241, 241, 241, 1)", color: "rgba(241, 241, 241, 1)",
textTransform: "none", textTransform: "none",
fontFamily: theme?.typography?.fontFamily,
}} }}
onClick={(event) => { onClick={(event) => {
handleActivateButton(event, data, "deactivate"); handleActivateButton(event, data, "deactivate");
@@ -494,12 +556,13 @@ const Hits = ({
) : ( ) : (
<Button <Button
style={{ style={{
backgroundColor: "#FF8544",
color: "black",
width: 102, width: 102,
height: 35, height: 35,
borderRadius: 200, borderRadius: 3,
backgroundColor: "rgba(242, 101, 59, 1)",
color: "rgba(255, 255, 255, 1)",
textTransform: "none", textTransform: "none",
fontFamily: theme?.typography?.fontFamily,
}} }}
onClick={(event) => { onClick={(event) => {
handleActivateButton(event, data, "activate"); handleActivateButton(event, data, "activate");
@@ -535,7 +598,7 @@ const Hits = ({
const SearchBox = ({ refine, searchQuery, setSearchQuery }) => { const SearchBox = ({ refine, searchQuery, setSearchQuery }) => {
const inputRef = useRef(null); const inputRef = useRef(null);
const [localQuery, setLocalQuery] = useState(searchQuery); const [localQuery, setLocalQuery] = useState(searchQuery);
const location = useLocation();
// Initialize search when component mounts or when switching to Discover tab // Initialize search when component mounts or when switching to Discover tab
useEffect(() => { useEffect(() => {
if (searchQuery) { if (searchQuery) {
@@ -592,7 +655,7 @@ const SearchBox = ({ refine, searchQuery, setSearchQuery }) => {
event.preventDefault(); event.preventDefault();
} }
}} }}
style={{ borderRadius: 8, height: 45, fontFamily: "Inter", flex: 1 }} style={{ borderRadius: 8, height: 45, fontFamily: theme?.typography?.fontFamily, flex: 1 }}
InputProps={{ InputProps={{
style: { style: {
borderRadius: 8, borderRadius: 8,
@@ -608,6 +671,9 @@ const SearchBox = ({ refine, searchQuery, setSearchQuery }) => {
onClick={() => { onClick={() => {
setLocalQuery(''); setLocalQuery('');
debouncedRefine(''); 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} onChange={handleChange}
displayEmpty displayEmpty
multiple multiple
style={{ borderRadius: 8, height: 45, fontFamily: "Inter", flex: 1 }} style={{ borderRadius: 8, height: 45, fontFamily: theme?.typography?.fontFamily, flex: 1 }}
renderValue={(selected) => selected.length ? selected.join(', ') : 'All Categories'} 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 All Categories
</MenuItem> </MenuItem>
{items.map(item => ( {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)} /> <Checkbox checked={currentRefinement.includes(item.label)} />
{item.label} ({item.count}) {item.label} ({item.count})
</MenuItem> </MenuItem>
@@ -686,15 +764,27 @@ const LabelDropdown = ({ items, currentRefinement, refine }) => {
onChange={handleChange} onChange={handleChange}
displayEmpty displayEmpty
multiple multiple
style={{ borderRadius: 8, height: 45, fontFamily: "Inter", flex: 1 }} style={{ borderRadius: 8, height: 45, fontFamily: theme?.typography?.fontFamily, flex: 1 }}
renderValue={(selected) => selected.length ? selected.join(', ') : 'All Labels'} 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=""> <MenuItem disabled value="">
All Labels All Labels
</MenuItem> </MenuItem>
{items.map(item => ( {items.map(item => (
<MenuItem key={item.label} value={item.label} style={{ <MenuItem key={item.label} value={item.label} style={{
fontFamily: "Inter", fontFamily: theme?.typography?.fontFamily,
fontSize: 16 fontSize: 16
}}> }}>
<Checkbox checked={currentRefinement.includes(item.label)} /> <Checkbox checked={currentRefinement.includes(item.label)} />
@@ -835,7 +925,7 @@ const LoadingGrid = () => {
// Main Apps Component // Main Apps Component
const Apps2 = (props) => { const Apps2 = (props) => {
const { globalUrl, isLoaded, serverside, userdata, isLoggedIn, checkLogin, isCloud } = props; const { globalUrl, isLoaded, serverside, userdata, isLoggedIn, checkLogin } = props;
let navigate = useNavigate(); let navigate = useNavigate();
const { leftSideBarOpenByClick } = useContext(Context); const { leftSideBarOpenByClick } = useContext(Context);
const location = useLocation(); const location = useLocation();
@@ -869,9 +959,16 @@ const Apps2 = (props) => {
const [field1, setField1] = useState(""); const [field1, setField1] = useState("");
const [field2, setField2] = useState(""); const [field2, setField2] = useState("");
const [validation, setValidation] = useState(null); const [validation, setValidation] = useState(null);
const [createAppModalOpen, setCreateAppModalOpen] = useState(false);
const baseRepository = "https://github.com/frikky/shuffle-apps"; 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 // Set the current tab based on the query parameter
useEffect(() => { useEffect(() => {
const queryParams = new URLSearchParams(location.search); const queryParams = new URLSearchParams(location.search);
@@ -931,14 +1028,13 @@ const Apps2 = (props) => {
const fetchApps = async () => { const fetchApps = async () => {
const baseUrl = globalUrl; const baseUrl = globalUrl;
let url; let url;
setIsLoading(true);
const userId = userdata?.id; const userId = userdata?.id;
if (currTab === 1 && userId) { if (currTab === 1 && userId) {
url = `${baseUrl}/api/v1/users/${userId}/apps`; url = `${baseUrl}/api/v1/users/${userId}/apps`;
} else if (currTab === 0) { } else if (currTab === 0) {
url = `${baseUrl}/api/v1/apps`; url = `${baseUrl}/api/v1/apps`;
} }
setIsLoading(true);
try { try {
const response = await fetch(url, { const response = await fetch(url, {
method: "GET", method: "GET",
@@ -1262,7 +1358,7 @@ const Apps2 = (props) => {
borderRadius: 2, borderRadius: 2,
border: "1px solid #494949", border: "1px solid #494949",
minWidth: '440px', minWidth: '440px',
fontFamily: "Inter", fontFamily: theme?.typography?.fontFamily,
backgroundColor: "#212121", backgroundColor: "#212121",
zIndex: 1000, zIndex: 1000,
'& .MuiDialogContent-root': { '& .MuiDialogContent-root': {
@@ -1272,10 +1368,10 @@ const Apps2 = (props) => {
backgroundColor: "#212121", backgroundColor: "#212121",
}, },
'& .MuiTypography-root': { '& .MuiTypography-root': {
fontFamily: 'Inter, sans-serif', fontFamily: theme?.typography?.fontFamily,
}, },
'& .MuiButton-root': { '& .MuiButton-root': {
fontFamily: 'Inter, sans-serif', fontFamily: theme?.typography?.fontFamily,
}, },
} }
}} }}
@@ -1393,7 +1489,8 @@ const Apps2 = (props) => {
/> />
</div> </div>
</DialogContent> </DialogContent>
<DialogActions sx={{ p: 3, <DialogActions sx={{
p: 3,
backgroundColor: "#212121" backgroundColor: "#212121"
}}> }}>
<Button <Button
@@ -1406,7 +1503,7 @@ const Apps2 = (props) => {
py: 1, py: 1,
px: 3, px: 3,
color: "#fff", color: "#fff",
fontFamily: "Inter" fontFamily: theme?.typography?.fontFamily
}} }}
onClick={() => setLoadAppsModalOpen(false)} onClick={() => setLoadAppsModalOpen(false)}
> >
@@ -1423,7 +1520,7 @@ const Apps2 = (props) => {
py: 1, py: 1,
px: 3, px: 3,
color: "#fff", color: "#fff",
fontFamily: "Inter" fontFamily: theme?.typography?.fontFamily
}} }}
disabled={openApi.length === 0 || !openApi.includes("http")} disabled={openApi.length === 0 || !openApi.includes("http")}
onClick={() => handleGithubValidation(true)} onClick={() => handleGithubValidation(true)}
@@ -1441,7 +1538,7 @@ const Apps2 = (props) => {
py: 1, py: 1,
px: 3, px: 3,
color: "black", color: "black",
fontFamily: "Inter" fontFamily: theme?.typography?.fontFamily
}} }}
disabled={openApi.length === 0 || !openApi.includes("http")} disabled={openApi.length === 0 || !openApi.includes("http")}
onClick={() => handleGithubValidation(false)} onClick={() => handleGithubValidation(false)}
@@ -1510,6 +1607,7 @@ const Apps2 = (props) => {
const handleCreateApp = (e) => { const handleCreateApp = (e) => {
e.preventDefault(); e.preventDefault();
setCreateAppModalOpen(true);
// setOpenModal(true); // setOpenModal(true);
}; };
@@ -1553,37 +1651,28 @@ const Apps2 = (props) => {
navigate(`${location.pathname}?${queryParams.toString()}`); navigate(`${location.pathname}?${queryParams.toString()}`);
}; };
// Update useEffect to handle initial load and URL search params // Update useEffect for filtering without URL manipulation
useEffect(() => {
const queryParams = new URLSearchParams(location.search);
const searchParam = queryParams.get('q');
if (searchParam) {
setSearchQuery(searchParam);
}
}, []);
// Update useEffect for filtering to handle both tabs
useEffect(() => { useEffect(() => {
if (currTab === 2) return; // Skip for "Discover Apps" tab as it uses Algolia if (currTab === 2) return; // Skip for "Discover Apps" tab as it uses Algolia
const apps = currTab === 1 ? userApps : orgApps; const apps = currTab === 1 ? userApps : orgApps;
const filteredApps = filterApps(apps, searchQuery, selectedCategory, selectedLabel); const filteredApps = filterApps(apps, searchQuery, selectedCategory, selectedLabel);
setAppsToShow(filteredApps); 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); const queryParams = new URLSearchParams(location.search);
if (searchQuery) { if (newSearchQuery) {
queryParams.set('q', searchQuery); queryParams.set('q', newSearchQuery);
} else { } else {
queryParams.delete('q'); queryParams.delete('q');
} }
window.history.replaceState({}, '', `${location.pathname}?${queryParams.toString()}`); 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 = { const boxStyle = {
@@ -1594,7 +1683,7 @@ const Apps2 = (props) => {
width: "100%", width: "100%",
margin: "auto", margin: "auto",
maxWidth: "70%", maxWidth: "70%",
fontFamily: "Inter", fontFamily: theme?.typography?.fontFamily,
// padding: '20px 380px', // padding: '20px 380px',
}; };
@@ -1619,7 +1708,7 @@ const Apps2 = (props) => {
} }
return ( 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"> <InstantSearch searchClient={searchClient} indexName="appsearch">
<AppModal <AppModal
open={openModal} open={openModal}
@@ -1628,10 +1717,17 @@ const Apps2 = (props) => {
userdata={userdata} userdata={userdata}
globalUrl={globalUrl} globalUrl={globalUrl}
/> />
<AppCreationModal
open={createAppModalOpen}
onClose={() => setCreateAppModalOpen(false)}
theme={theme}
globalUrl={globalUrl}
isCloud={isCloud}
/>
{appsModalLoad} {appsModalLoad}
<div style={boxStyle}> <div style={boxStyle}>
<div style={{ display: "flex", flexDirection: "row", width: "100%", justifyContent: "space-between" }}> <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 Apps
</Typography> </Typography>
{isCloud ? null : ( {isCloud ? null : (
@@ -1648,7 +1744,7 @@ const Apps2 = (props) => {
color: "rgba(241, 241, 241, 1)", color: "rgba(241, 241, 241, 1)",
fontSize: 14, fontSize: 14,
border: "1px solid rgba(73, 73, 73, 1)", 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)", color: "rgba(241, 241, 241, 1)",
fontSize: 14, fontSize: 14,
border: "1px solid rgba(73, 73, 73, 1)", border: "1px solid rgba(73, 73, 73, 1)",
fontFamily: "Inter" fontFamily: theme?.typography?.fontFamily,
} }
} }
}} }}
@@ -1720,22 +1816,25 @@ const Apps2 = (props) => {
</span> </span>
)} )}
</div> </div>
<div style={{ borderBottom: '1px solid gray', marginBottom: 30, marginRight: 10 }}> <div style={{ borderBottom: '1px solid gray', marginBottom: 30 }}>
<Tabs <Tabs
value={currTab} value={currTab}
onChange={(event, newTab) => handleTabChange(event, newTab)} onChange={(event, newTab) => handleTabChange(event, newTab)}
TabIndicatorProps={{ style: { height: '3px', borderRadius: 10 } }} TabIndicatorProps={{ style: { height: '3px', borderRadius: 10, backgroundColor: "#FF8544" } }}
style={{ fontFamily: "Inter" }} style={{ fontFamily: theme?.typography?.fontFamily }}
> >
<Tab label="Organization Apps" style={{ textTransform: 'none', marginRight: 20, 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: "Inter" }} /> <Tab label="My Apps" style={{ textTransform: 'none', marginRight: 20, fontFamily: theme?.typography?.fontFamily }} />
<Tab label="Discover Apps" style={{ textTransform: 'none', fontFamily: "Inter" }} /> <Tab label="Discover Apps" style={{ textTransform: 'none', fontFamily: theme?.typography?.fontFamily }} />
</Tabs> </Tabs>
</div> </div>
<div style={{ display: "flex", justifyContent: "space-between", gap: 10, marginBottom: 20, height: 45 }}> <div style={{ display: "flex", justifyContent: "space-between", gap: 10, marginBottom: 20, height: 45, paddingRight: 25 }}>
<div style={{ flex: 1, width: '100%', borderRadius: '7px' }}> <div style={{
{ width: "25%",
(currTab === 0 || currTab === 1) && minWidth: "25%",
maxWidth: "25%"
}}>
{(currTab === 0 || currTab === 1) ? (
<TextField <TextField
fullWidth fullWidth
variant="outlined" variant="outlined"
@@ -1756,13 +1855,14 @@ const Apps2 = (props) => {
}, },
endAdornment: ( endAdornment: (
<InputAdornment position="end"> <InputAdornment position="end">
{searchQuery.length === 0 ? ( {searchQuery.length === 0 ? <Search /> : (
<Search />
) : (
<ClearIcon <ClearIcon
style={{ cursor: "pointer" }} style={{ cursor: "pointer" }}
onClick={() => { 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} /> <CustomSearchBox searchQuery={searchQuery} setSearchQuery={setSearchQuery} />
} )}
</div> </div>
<div style={{ flex: 1, width: '100%', borderRadius: '7px', position: 'relative' }}> <div style={{
width: "25%",
minWidth: "25%",
maxWidth: "25%",
position: 'relative'
}}>
{currTab === 2 ? ( {currTab === 2 ? (
<CustomCategoryDropdown attribute="categories" /> <CustomCategoryDropdown attribute="categories" />
) : ( ) : (
@@ -1788,14 +1891,30 @@ const Apps2 = (props) => {
onChange={handleCategoryChange} onChange={handleCategoryChange}
displayEmpty displayEmpty
multiple multiple
style={{ borderRadius: 8, height: 45, fontFamily: "Inter" }} style={{
renderValue={(selected) => selected.length ? selected.join(', ') : 'All Categories'} 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 All Categories
</MenuItem> </MenuItem>
{categories?.map((category) => ( {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)} /> <Checkbox checked={selectedCategory.includes(category.category)} />
{category.category} {category.category}
</MenuItem> </MenuItem>
@@ -1819,7 +1938,12 @@ const Apps2 = (props) => {
</> </>
)} )}
</div> </div>
<div style={{ flex: 1, width: '100%', borderRadius: '7px', position: 'relative' }}> <div style={{
width: "25%",
minWidth: "25%",
maxWidth: "25%",
position: 'relative'
}}>
{currTab === 2 ? ( {currTab === 2 ? (
<CustomLabelDropdown attribute="action_labels" /> <CustomLabelDropdown attribute="action_labels" />
) : ( ) : (
@@ -1831,14 +1955,30 @@ const Apps2 = (props) => {
onChange={handleLabelChange} onChange={handleLabelChange}
displayEmpty displayEmpty
multiple multiple
style={{ borderRadius: 8, height: 45, fontFamily: "Inter" }} style={{
renderValue={(selected) => selected.length ? selected.join(', ') : 'All Labels'} 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 All Labels
</MenuItem> </MenuItem>
{labels?.map((tag) => ( {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)} /> <Checkbox checked={selectedLabel.includes(tag.tag)} />
{tag.tag} {tag.tag}
</MenuItem> </MenuItem>
@@ -1862,14 +2002,28 @@ const Apps2 = (props) => {
</> </>
)} )}
</div> </div>
<div style={{ flex: 1 }}> <div style={{
width: "25%",
minWidth: "25%",
maxWidth: "25%"
}}>
<Button <Button
variant="contained" variant="contained"
color="primary" color="primary"
onClick={handleCreateApp} 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 Create an App
</Button> </Button>
</div> </div>
@@ -1892,7 +2046,8 @@ const Apps2 = (props) => {
gap: "20px", gap: "20px",
justifyContent: "space-between", justifyContent: "space-between",
alignItems: "start", alignItems: "start",
padding: "0 10px" padding: "0 10px",
paddingBottom: 40
}}> }}>
{appsToShow.map((data, index) => ( {appsToShow.map((data, index) => (
<AppCard <AppCard
@@ -1931,7 +2086,7 @@ const Apps2 = (props) => {
} }
{ {
currTab === 1 && ( currTab === 1 && (
<div style={{ minHeight: 570, overflowY: "auto", overflowX: "hidden" }}> <div style={{ minHeight: 570 }}>
{isLoading ? ( {isLoading ? (
<LoadingGrid /> <LoadingGrid />
) : ( ) : (
@@ -1945,7 +2100,8 @@ const Apps2 = (props) => {
gap: "20px", gap: "20px",
justifyContent: "space-between", justifyContent: "space-between",
alignItems: "start", alignItems: "start",
padding: "0 10px" padding: "0 10px",
paddingBottom: 40
}}> }}>
{appsToShow.map((data, index) => ( {appsToShow.map((data, index) => (
<AppCard key={index} data={data} index={index} mouseHoverIndex={mouseHoverIndex} setMouseHoverIndex={setMouseHoverIndex} globalUrl={globalUrl} deactivatedIndexes={deactivatedIndexes} currTab={currTab} userdata={userdata} <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>
) : ( ) : (
<div style={{ width: "100%", marginTop: 60, textAlign: "center", display: "flex", justifyContent: "center", alignItems: "center" }}> <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> </div>
)} )}
</> </>
@@ -1976,6 +2141,7 @@ const Apps2 = (props) => {
searchQuery={searchQuery} searchQuery={searchQuery}
mouseHoverIndex={mouseHoverIndex} mouseHoverIndex={mouseHoverIndex}
setMouseHoverIndex={setMouseHoverIndex} setMouseHoverIndex={setMouseHoverIndex}
currTab={currTab}
leftSideBarOpenByClick={leftSideBarOpenByClick} leftSideBarOpenByClick={leftSideBarOpenByClick}
/> />
} }
+2 -1
View File
@@ -274,7 +274,8 @@ const CodeWorkflow = (defaultprops) => {
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
height: '100vh', // Set to full viewport height height: '100vh', // Set to full viewport height
backgroundColor: '#252526' backgroundColor: '#252526',
marginLeft: '100px',
}}> }}>
{/* IDE-like toolbar */} {/* IDE-like toolbar */}
<Toolbar <Toolbar
+16 -4
View File
@@ -337,11 +337,23 @@ const Dashboard = (props) => {
const [, setUpdate] = useState(0); const [, setUpdate] = useState(0);
let navigate = useNavigate(); let navigate = useNavigate();
const isCloud = const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
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(() => { useEffect(() => {
// Look for the path. If it is automation, go to usecases
// Handles redirect
const widgetnames = ["app_executions_cloud"] const widgetnames = ["app_executions_cloud"]
for (let widgetkey in widgetnames) { for (let widgetkey in widgetnames) {
const widgetName = widgetnames[widgetkey] const widgetName = widgetnames[widgetkey]
@@ -859,7 +871,7 @@ const Dashboard = (props) => {
); );
const dataWrapper = ( const dataWrapper = (
<div style={{ maxWidth: 1366, margin: "auto" }}>{data}</div> <div style={{ maxWidth: 1366, margin: "auto", paddingTop: 10, }}>{data}</div>
); );
return dataWrapper; return dataWrapper;
+8 -8
View File
@@ -251,7 +251,7 @@ export const CodeHandler = (props) => {
} }
const Docs = (defaultprops) => { const Docs = (defaultprops) => {
const { globalUrl, selectedDoc, serverside, serverMobile, userdata } = defaultprops; const { globalUrl, selectedDoc, serverside, serverMobile, isLoggedIn, isLoaded } = defaultprops;
let navigate = useNavigate(); let navigate = useNavigate();
// Quickfix for react router 5 -> 6 // Quickfix for react router 5 -> 6
const params = useParams(); const params = useParams();
@@ -1266,7 +1266,7 @@ const Docs = (defaultprops) => {
// Padding and zIndex etc set because of footer in cloud. // Padding and zIndex etc set because of footer in cloud.
const loadedCheck = ( const loadedCheck = (
<DocsWrapper userdata={userdata}> <DocsWrapper isLoggedIn={isLoggedIn} isLoaded={isLoaded}>
<DocsContent postDataBrowser={postDataBrowser} postDataMobile={postDataMobile}/> <DocsContent postDataBrowser={postDataBrowser} postDataMobile={postDataMobile}/>
</DocsWrapper> </DocsWrapper>
); );
@@ -1285,18 +1285,18 @@ const DocsContent = memo(({postDataBrowser, postDataMobile}) => {
</div> </div>
)}) )})
const DocsWrapper = memo(({userdata, children })=>{ const DocsWrapper = memo(({isLoggedIn, isLoaded, children })=>{
const { leftSideBarOpenByClick, windowWidth } = useContext(Context); const { leftSideBarOpenByClick, windowWidth } = useContext(Context);
return ( return (
<div style={{ <div style={{
minHeight: 1000, zIndex: 1, minHeight: 1000, zIndex: 1,
maxWidth: Math.min(leftSideBarOpenByClick ? windowWidth - 300 : windowWidth - 200, 1920), maxWidth: Math.min(!(isLoggedIn && isLoaded) ? 1920 : leftSideBarOpenByClick ? windowWidth - 300 : windowWidth - 200, 1920),
minWidth: isMobile ? null : leftSideBarOpenByClick ? 800 : 900, margin: "auto", minWidth: isMobile ? null : (isLoggedIn && isLoaded) ? leftSideBarOpenByClick ? 800 : 900 : null, margin: "auto",
position: leftSideBarOpenByClick ? "relative" : "static", position: (isLoggedIn && isLoaded) && leftSideBarOpenByClick ? "relative" : "static",
left: leftSideBarOpenByClick ? 120 : !leftSideBarOpenByClick ? 80 : 0, left: (isLoggedIn && isLoaded) && leftSideBarOpenByClick ? 120 : (isLoggedIn && isLoaded) && !leftSideBarOpenByClick ? 80 : 0,
marginLeft: windowWidth < 1920 ? leftSideBarOpenByClick ? 160 : !leftSideBarOpenByClick ? 80 : 0 : "auto", width: "100%", 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" 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} {children}
+13 -6
View File
@@ -108,7 +108,6 @@ const RunWorkflow = (defaultprops) => {
const boxStyle = { const boxStyle = {
color: "white", color: "white",
padding: "25px 50px 50px 50px", padding: "25px 50px 50px 50px",
backgroundColor: theme.palette.surfaceColor,
borderRadius: 25, borderRadius: 25,
minHeight: 500, minHeight: 500,
} }
@@ -1116,7 +1115,7 @@ const RunWorkflow = (defaultprops) => {
const ExplorerUi = () => { const ExplorerUi = () => {
return ( 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 ? {forms !== undefined && forms !== null && forms.length > 0 ?
<div> <div>
@@ -1126,9 +1125,14 @@ const RunWorkflow = (defaultprops) => {
<FormList /> <FormList />
</div> </div>
: :
<Typography variant="h6" style={{marginTop: 100, marginBottom: 20, }}> <div>
No Form Found <Typography variant="h4" style={{marginTop: 125, marginBottom: 25, }}>
No Forms Found
</Typography> </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> </div>
) )
@@ -1474,7 +1478,7 @@ const RunWorkflow = (defaultprops) => {
{workflowQuestion !== "" ? null : {workflowQuestion !== "" ? null :
<Typography variant="body2" color="textSecondary" align="center" style={{marginTop: 10, }} > <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> </Typography>
} }
</div> </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 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 ? const loadedCheck = isLoaded ?
<div style={{marginTop: 30, }}> <div style={{paddingTop: 60, }}>
{editWorkflowModalOpen === true ? {editWorkflowModalOpen === true ?
<EditWorkflow <EditWorkflow
saveWorkflow={saveWorkflow} saveWorkflow={saveWorkflow}
@@ -1570,6 +1574,7 @@ const RunWorkflow = (defaultprops) => {
<div style={{position: "fixed", top: 10, right: 20, }}> <div style={{position: "fixed", top: 10, right: 20, }}>
<Button <Button
disabled={workflow.id === undefined || workflow.id === null}
variant={"outlined"} variant={"outlined"}
color={"secondary"} color={"secondary"}
style={{marginRight: 10, }} style={{marginRight: 10, }}
@@ -1582,6 +1587,7 @@ const RunWorkflow = (defaultprops) => {
</Button> </Button>
<Button <Button
disabled={workflow.id === undefined || workflow.id === null}
variant={workflow.sharing === "form" ? "outlined" : "contained"} variant={workflow.sharing === "form" ? "outlined" : "contained"}
color={"secondary"} color={"secondary"}
style={{marginRight: 10, }} style={{marginRight: 10, }}
@@ -1603,6 +1609,7 @@ const RunWorkflow = (defaultprops) => {
</Button> </Button>
<Button <Button
disabled={workflow.id === undefined || workflow.id === null}
variant={"contained"} variant={"contained"}
color={"primary"} color={"primary"}
style={{}} style={{}}
+1 -1
View File
@@ -91,7 +91,7 @@ const Search = (props) => {
userdata={userdata} userdata={userdata}
/>, [curTab]); />, [curTab]);
const MemoizedDiscordChat = useMemo(() => <DiscordChat isMobile={isMobile} />) const MemoizedDiscordChat = useMemo(() => <DiscordChat isMobile={isMobile} />, [curTab])
const useStyles = makeStyles({ const useStyles = makeStyles({
hideIndicator: { hideIndicator: {
+2 -2
View File
@@ -1220,7 +1220,7 @@ const Usecases2 = (props) => {
) : null ) : null
const data = 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 <UsecaseListComponent
userdata={userdata} userdata={userdata}
@@ -1241,7 +1241,7 @@ const Usecases2 = (props) => {
const dataWrapper = const dataWrapper =
<Fade in={true} timeout={1250}> <Fade in={true} timeout={1250}>
<div style={{ maxWidth: 1366, margin: "auto", }}> <div style={{ maxWidth: 1366, margin: "auto", zoom: 0.8, }}>
{data} {data}
</div> </div>
</Fade> </Fade>
+319 -321
View File
@@ -58,6 +58,7 @@ import {
AvatarGroup, AvatarGroup,
Zoom, Zoom,
Collapse, Collapse,
Skeleton,
} from "@mui/material"; } from "@mui/material";
// Material UI Icons // Material UI Icons
@@ -430,14 +431,17 @@ export const GetIconInfo = (action) => {
}; };
const chipStyle = { const chipStyle = {
backgroundColor: "#3d3f43", backgroundColor: "#2F2F2F",
marginRight: 5, marginRight: 5,
paddingLeft: 5, paddingLeft: 5,
paddingRight: 5, paddingRight: 5,
height: 28, height: 35,
cursor: "pointer", cursor: "pointer",
borderColor: "#3d3f43", borderColor: "#2F2F2F",
color: "white", color: "#C8C8C8",
fontSize: "14px",
fontFamily: theme?.typography?.fontFamily,
borderRadius: "17.5px"
}; };
export const collapseField = (field) => { export const collapseField = (field) => {
@@ -618,9 +622,11 @@ const useDropzoneStyles = () => {
const { leftSideBarOpenByClick } = useContext(Context); const { leftSideBarOpenByClick } = useContext(Context);
return { return {
maxWidth: window.innerWidth > 1366 ? 1366 : isMobile ? "100%" : 1200, paddingTop: 70,
margin: "auto", // minHeight: 1000,
padding: 20, backgroundColor: "#1A1A1A",
fontFamily: theme?.typography?.fontFamily,
// maxWidth: window.innerWidth > 1366 ? 1366 : isMobile ? "100%" : 1200,
paddingLeft: leftSideBarOpenByClick ? 200 : 0, paddingLeft: leftSideBarOpenByClick ? 200 : 0,
transition: "padding-left 0.3s ease", transition: "padding-left 0.3s ease",
}; };
@@ -640,6 +646,7 @@ const DropzoneWrapper = memo(({ onDrop, WorkflowView }) => {
const Workflows2 = (props) => { const Workflows2 = (props) => {
const { globalUrl, isLoggedIn, isLoaded, userdata, checkLogin } = props; const { globalUrl, isLoggedIn, isLoaded, userdata, checkLogin } = props;
const { leftSideBarOpenByClick } = useContext(Context);
const location = useLocation(); const location = useLocation();
const navigate = useNavigate(); const navigate = useNavigate();
const [currTab, setCurrTab] = useState(0); const [currTab, setCurrTab] = useState(0);
@@ -708,7 +715,6 @@ const Workflows2 = (props) => {
const [apps, setApps] = React.useState([]); const [apps, setApps] = React.useState([]);
document.title = "Shuffle - Workflows"; document.title = "Shuffle - Workflows";
const handleTabChange = (event, newValue) => { const handleTabChange = (event, newValue) => {
// Set loading when switching to public workflows tab // Set loading when switching to public workflows tab
@@ -1516,24 +1522,25 @@ const Workflows2 = (props) => {
}; };
const paperAppStyle = { const paperAppStyle = {
minHeight: 130, minHeight: 146,
maxHeight: 130, maxHeight: 146,
overflow: "hidden", overflow: "hidden",
width: "100%", width: "100%",
color: "white", color: "white",
padding: "12px 12px 0px 15px", padding: "12px 12px 0px 15px",
display: "flex", display: "flex",
fontFamily: theme?.typography?.fontFamily,
boxSizing: "border-box", boxSizing: "border-box",
position: "relative", position: "relative",
borderRadius: theme.palette?.borderRadius, borderRadius: "8px",
backgroundColor: theme.palette.surfaceColor, // backgroundColor: "#212121",
}; };
const gridContainer = { const gridContainer = {
height: "auto", height: "auto",
color: "white", color: "white",
margin: "10px", margin: "10px",
backgroundColor: theme.palette.surfaceColor, backgroundColor: "#212121",
position: "relative", position: "relative",
}; };
@@ -1542,6 +1549,7 @@ const Workflows2 = (props) => {
width: 160, width: 160,
height: 44, height: 44,
justifyContent: "space-between", justifyContent: "space-between",
fontFamily: theme?.typography?.fontFamily,
}; };
const exportAllWorkflows = (allWorkflows) => { const exportAllWorkflows = (allWorkflows) => {
@@ -2107,6 +2115,80 @@ const Workflows2 = (props) => {
return appsFound 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 WorkflowPaper = (props) => {
const { data, type = "org" } = props; const { data, type = "org" } = props;
const [open, setOpen] = React.useState(false); const [open, setOpen] = React.useState(false);
@@ -2283,8 +2365,9 @@ const Workflows2 = (props) => {
marginRight: 10, marginRight: 10,
}; };
image = image =
foundOrg.image === "" ? ( foundOrg.image === "" || foundOrg.image === null || foundOrg.image === undefined ? (
<img <img
alt={foundOrg.name} alt={foundOrg.name}
src={theme.palette.defaultImage} 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 ( 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}> <Paper square style={paperAppStyle}>
{selectedCategory !== "" ? {selectedCategory !== "" ?
<Tooltip title={`Usecase Category: ${selectedCategory}`} placement="bottom"> <Tooltip title={`Usecase Category: ${selectedCategory}`} placement="bottom">
@@ -2351,6 +2456,7 @@ const Workflows2 = (props) => {
width: 3, width: 3,
backgroundColor: boxColor, backgroundColor: boxColor,
borderRadius: "0 100px 0 0", borderRadius: "0 100px 0 0",
fontFamily: theme?.typography?.fontFamily,
}} }}
onClick={() => { onClick={() => {
addFilter(selectedCategory) addFilter(selectedCategory)
@@ -2360,7 +2466,7 @@ const Workflows2 = (props) => {
: null} : null}
<Grid <Grid
item 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 }}> <Grid item style={{ display: "flex", maxHeight: 34 }}>
<Tooltip title={`Org "${orgName}". Click to edit image.`} placement="bottom"> <Tooltip title={`Org "${orgName}". Click to edit image.`} placement="bottom">
@@ -2389,7 +2495,7 @@ const Workflows2 = (props) => {
maxWidth: 310, maxWidth: 310,
padding: "12px 0", padding: "12px 0",
}}> }}>
{data.image !== undefined && data.image !== null && data.image.length > 0 ? ( {(data?.image !== undefined || data?.image_url !== undefined) ? (
<div style={{ <div style={{
marginBottom: 15, marginBottom: 15,
borderRadius: theme.palette?.borderRadius, borderRadius: theme.palette?.borderRadius,
@@ -2397,8 +2503,8 @@ const Workflows2 = (props) => {
border: `1px solid ${theme.palette.inputColor}`, border: `1px solid ${theme.palette.inputColor}`,
}}> }}>
<img <img
src={data.image} src={data?.image || data?.image_url}
alt={data.name} alt={data?.name}
style={{ style={{
backgroundColor: theme.palette.surfaceColor, backgroundColor: theme.palette.surfaceColor,
width: "100%", width: "100%",
@@ -2411,6 +2517,8 @@ const Workflows2 = (props) => {
<Typography style={{ <Typography style={{
color: "rgba(255,255,255,0.9)", color: "rgba(255,255,255,0.9)",
fontSize: "16px",
fontFamily: theme?.typography?.fontFamily,
}}> }}>
Edit: {data.name} Edit: {data.name}
</Typography> </Typography>
@@ -2443,10 +2551,14 @@ const Workflows2 = (props) => {
paddingBottom: 0, paddingBottom: 0,
maxHeight: 30, maxHeight: 30,
flex: 10, flex: 10,
fontFamily: theme?.typography?.fontFamily,
fontWeight: 500,
}} }}
> >
<Link <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" }} style={{ textDecoration: "none", color: "inherit" }}
> >
{parsedName} {parsedName}
@@ -2620,8 +2732,9 @@ const Workflows2 = (props) => {
style={{ style={{
justifyContent: "left", justifyContent: "left",
overflow: "hidden", overflow: "hidden",
marginTop: 5, marginTop: 8,
maxHeight: 28, maxHeight: 35,
fontFamily: theme?.typography?.fontFamily,
}} }}
> >
{data.tags !== undefined && data.tags !== null {data.tags !== undefined && data.tags !== null
@@ -3079,11 +3192,7 @@ const Workflows2 = (props) => {
</Typography> </Typography>
</span> </span>
</Tooltip> </Tooltip>
<Tooltip <Tooltip color="primary" title="Subflows used" placement="bottom">
color="primary"
title="Subflows used"
placement="bottom"
>
<span <span
style={{ style={{
marginLeft: 15, marginLeft: 15,
@@ -3235,7 +3344,7 @@ const Workflows2 = (props) => {
); );
} }
return ( return (
<div style={gridContainer}> <div style={{ ...gridContainer, backgroundColor: "#212121" }}>
<Tooltip title={`New Workflow`} placement="bottom"> <Tooltip title={`New Workflow`} placement="bottom">
<IconButton <IconButton
style={{ position: "absolute", top: 10, right: 50, zIndex: 1000 }} style={{ position: "absolute", top: 10, right: 50, zIndex: 1000 }}
@@ -3797,9 +3906,9 @@ const Workflows2 = (props) => {
// } // }
useEffect(() => { useEffect(() => {
if (userdata !== undefined && userdata !== null) { if (userdata !== undefined && userdata !== null && currTab !== 2) {
var filteredWorkflows = []
setIsLoadingWorkflow(true); setIsLoadingWorkflow(true);
var filteredWorkflows = []
if (currTab === 0) { if (currTab === 0) {
filteredWorkflows = workflows.filter(workflow => workflow?.org_id === userdata?.active_org?.id) 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) filteredWorkflows = workflows.filter(workflow => workflow?.org_id === userdata?.active_org?.id && workflow?.owner === userdata?.id)
} }
setFilteredWorkflows(filteredWorkflows) setFilteredWorkflows(filteredWorkflows)
setTimeout(() => {
setIsLoadingWorkflow(false); setIsLoadingWorkflow(false);
}, 500);
} }
}, [currTab, workflows, userdata]) }, [currTab, workflows, userdata])
@@ -3819,25 +3931,29 @@ const Workflows2 = (props) => {
var counted = 0 var counted = 0
console.log("Public workflows", hits) console.log("Public workflows", hits)
if (isLoadingPublicWorkflow) {
return ( return (
<div style={{ textAlign: "center", marginTop: 50 }}>
<CircularProgress style={{ color: "#f85a3e" }} />
</div>
);
}
return ( isLoadingPublicWorkflow ?
(
<LoadingWorkflowGrid />
) : (
<div style={{ <div style={{
display: "grid", marginTop: 16,
gridTemplateColumns: "repeat(3, 1fr)", // Creates 3 equal columns
gap: "20px", // Adds consistent spacing between items
width: "100%", 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) => { {hits.map((data, index) => {
return <WorkflowPaper key={index} data={data} type="public" /> return <WorkflowPaper key={index} data={data} type="public" />
})} })}
</div> </div>
)
) )
} }
@@ -3849,6 +3965,21 @@ const Workflows2 = (props) => {
setSelectedCategory(e.target.value) 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(() => { const WorkflowView = memo(() => {
if (workflows.length === 0) { 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) const foundPriority = userdata === undefined || userdata === null || userdata.priorities === undefined || userdata.priorities === null ? null : userdata.priorities.find(prio => prio.type === "usecase" && prio.active === true)
return ( return (
<div style={viewStyle}> <>
<div style={workflowViewStyle}> <div style={{
<div style={{ display: "flex", marginTop: 25, }}> color: "white",
{/* <div style={{ flex: 1 }}> display: "flex",
<Typography variant="h1" style={{fontSize: 30}}> flexDirection: "column",
width: "100%",
maxWidth: "70%",
margin: "auto",
}}>
<Typography variant="h4" style={{ marginBottom: 20, paddingLeft: 15, textTransform: 'none', fontFamily: theme?.typography?.fontFamily }}>
Workflows Workflows
</Typography> </Typography>
</div> */}
{/* <div style={{ borderBottom: '1px solid gray', marginBottom: 30 }}>
<div style={{ flex: 1 }}> <Tabs
<Typography style={{ marginTop: 7, marginBottom: "auto" }}> value={currTab}
<a onChange={handleTabChange}
rel="noopener noreferrer" TabIndicatorProps={{ style: { height: '3px', borderRadius: 10, backgroundColor: "#FF8544" } }}
target="_blank" style={{ fontFamily: theme?.typography?.fontFamily }}
href="https://shuffler.io/docs/workflows"
style={{ textDecoration: "none", color: "#f85a3e" }}
> >
Learn more about Workflows <Tab label="Organization Workflows" style={{ textTransform: 'none', marginRight: 20, fontFamily: theme?.typography?.fontFamily }} />
</a> <Tab label="My Workflows" style={{ textTransform: 'none', marginRight: 20, fontFamily: theme?.typography?.fontFamily }} />
</Typography> <Tab label="Discover Workflows" style={{ textTransform: 'none', fontFamily: theme?.typography?.fontFamily }} />
</Tabs>
</div> </div>
*/}
{/* {isMobile ? null :
<div style={{ display: "flex", margin: "0px 0px 20px 0px" }}> <div style={{ display: "flex", justifyContent: "space-between", gap: 10, marginBottom: 20, paddingRight: 25, minHeight: 47 }}>
<div style={{ flex: 1, float: "right" }}>
<MuiChipsInput <MuiChipsInput
style={{}} style={{
width: "25%",
maxWidth: "25%",
minWidth: "25%",
height: 43,
maxHeight: "fit-content",
backgroundColor: "#212121",
zIndex: 1000,
}}
disabled={currTab === 2}
InputProps={{ InputProps={{
style: { style: {
color: "white", color: "white",
maxWidth: 275, height: "fit-content",
minWidth: 275, 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} clearInputOnBlur={false}
placeholder="Filter Workflows" sx={{
color="primary" // Container styling
fullWidth '& .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} value={filters}
onChange={(chips) => { onChange={(chips) => {
console.log("CHANGE: ", chips);
setFilters(chips); setFilters(chips);
findWorkflow(chips); findWorkflow(chips);
}} }}
@@ -3911,180 +4093,6 @@ const Workflows2 = (props) => {
// removeFilter(index); // 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 <Select
fullWidth fullWidth
@@ -4092,8 +4100,23 @@ const Workflows2 = (props) => {
value={selectedCategory} value={selectedCategory}
onChange={handleCategoryChange} onChange={handleCategoryChange}
displayEmpty displayEmpty
disabled={currTab === 2}
multiple 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'} renderValue={(selected) => selected.length ? selected.join(', ') : 'All Categories'}
> >
<MenuItem disabled value="" style={{}}>All Categories</MenuItem> <MenuItem disabled value="" style={{}}>All Categories</MenuItem>
@@ -4174,62 +4197,50 @@ const Workflows2 = (props) => {
})} })}
</Select> </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"> <Tooltip title="Explore Workflow Runs" placement="top">
<IconButton <IconButton
style={{ style={iconButtonStyle}
color: 'white',
backgroundColor: '#212121',
borderRadius: '8px',
padding: '6px',
width: '55px',
height: '55px',
}}
onClick={() => navigate("/workflows/debug")} onClick={() => navigate("/workflows/debug")}
disabled={currTab === 2}
> >
<QueryStatsIcon /> <QueryStatsIcon style={{ color: "#F1F1F1" }} />
</IconButton> </IconButton>
</Tooltip> </Tooltip>
<Tooltip title={view === "grid" ? "Grid view" : "List view"} placement="top">
<Tooltip title={view === "grid" ? "List view" : "Grid view"} placement="top">
<IconButton <IconButton
style={{ style={iconButtonStyle}
color: 'white',
backgroundColor: '#212121',
borderRadius: '8px',
padding: '6px',
width: '55px',
height: '55px',
}}
onClick={() => { onClick={() => {
if (view === "grid") { const newView = view === "grid" ? "list" : "grid";
localStorage.setItem("workflowView", "list"); localStorage.setItem("workflowView", newView);
setView("list"); setView(newView);
} else {
localStorage.setItem("workflowView", "grid");
setView("grid");
}
}} }}
disabled={currTab === 2}
> >
{ {view === "grid" ? <ListIcon /> : <GridOnIcon />}
view === "list" ? <ListIcon /> : <GridOnIcon />
}
</IconButton> </IconButton>
</Tooltip> </Tooltip>
<Tooltip title="Import workflows" placement="top"> <Tooltip title="Import workflows" placement="top">
<IconButton <IconButton
style={{ style={iconButtonStyle}
color: 'white',
backgroundColor: '#212121',
borderRadius: '8px',
padding: '6px',
width: '55px',
height: '55px',
}}
onClick={() => upload.click()} onClick={() => upload.click()}
disabled={currTab === 2}
> >
{submitLoading ? <CircularProgress color="secondary" /> : <PublishIcon />} {submitLoading ? <CircularProgress color="secondary" /> : <PublishIcon />}
</IconButton> </IconButton>
</Tooltip> </Tooltip>
<input <input
hidden hidden
type="file" type="file"
@@ -4237,62 +4248,60 @@ const Workflows2 = (props) => {
ref={(ref) => (upload = ref)} ref={(ref) => (upload = ref)}
onChange={importFiles} onChange={importFiles}
/> />
<Tooltip title={`Download ALL workflows (${workflows.length})`}
placement="top"> <Tooltip title={`Download ALL workflows (${workflows.length})`} placement="top">
<IconButton <IconButton
style={{ style={{ ...iconButtonStyle, cursor: "pointer" }}
color: 'white', disabled={isCloud || currTab === 2}
backgroundColor: '#212121', onClick={() => exportAllWorkflows(workflows)}
borderRadius: '8px',
padding: '6px',
width: '55px',
height: '55px',
}}
disabled={isCloud}
variant="text"
onClick={() => {
exportAllWorkflows(workflows);
}}
> >
<GetAppIcon /> <GetAppIcon />
</IconButton> </IconButton>
</Tooltip> </Tooltip>
</div> </div>
<Button <Button
variant="contained" variant="contained"
color="primary" color="primary"
onClick={handleCreateWorkflow} onClick={handleCreateWorkflow}
style={{ style={{
height: "56px", borderRadius: 4,
borderRadius: 8,
flex: 0.8, flex: 0.8,
textTransform: 'none', 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 Create Workflow
</Button> </Button>
</div>
</div> </div>
<div style={{ <div style={{
width: "100%", width: "100%",
position: "relative",
zIndex: 1
}}> }}>
{ {
isLoadingWorkflow ? ( isLoadingWorkflow ? (
<div style={{ textAlign: "center", marginTop: 50 }}> <LoadingWorkflowGrid/>
<CircularProgress style={{ color: "#f85a3e" }} />
</div>
) : ( ) : (
view === "grid" && currTab !== 2 ? ( view === "grid" && currTab !== 2 ? (
<> <>
<div style={{ <div style={{
display: "grid", marginTop: 16,
gridTemplateColumns: "repeat(3, 1fr)", // Creates 3 equal columns
gap: "20px", // Adds consistent spacing between items
width: "100%", 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"> <InstantSearch searchClient={searchClient} indexName="workflows">
<Configure clickAnalytics /> <Configure clickAnalytics />
<CustomHits hitsPerPage={5} /> <CustomHits hitsPerPage={5} type="public" />
</InstantSearch> </InstantSearch>
) )
} }
@@ -4338,19 +4347,8 @@ const Workflows2 = (props) => {
</div> </div>
</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 // Maybe use gridview or something, idk
return <div>{loadedCheck}</div>; return <div style={{zoom: 0.8, }}>{loadedCheck}</div>;
}; };