Lightmode/Darkmode merge including many fixes since 2.0.2

This commit is contained in:
Frikky
2025-05-19 00:04:37 +02:00
parent 7bc246c003
commit a4ebf3e558
59 changed files with 5931 additions and 2961 deletions
+1 -1
View File
@@ -23,7 +23,7 @@ require (
github.com/h2non/filetype v1.1.3
github.com/satori/go.uuid v1.2.0
github.com/shuffle/shuffle-shared v0.8.50
golang.org/x/crypto v0.36.0
golang.org/x/crypto v0.37.0
google.golang.org/api v0.228.0
google.golang.org/grpc v1.71.1
gopkg.in/yaml.v3 v3.0.1
+5
View File
@@ -0,0 +1,5 @@
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 0L-1.48522e-08 13.3913L4.40052 13.3913L4.40052 4.46465L22 4.46465L22 2.44001e-08L0 0Z" fill="#FF8444"/>
<path d="M17.5995 8.60864L17.5995 17.5353L-9.90052e-09 17.5353L-1.48522e-08 22L22 22L22 8.60864L17.5995 8.60864Z" fill="#FF8444"/>
<path d="M13.3915 8.60864L8.60889 8.60864L8.60889 13.3913L13.3915 13.3913L13.3915 8.60864Z" fill="#FF8444"/>
</svg>

After

Width:  |  Height:  |  Size: 459 B

+256 -26
View File
@@ -19,17 +19,97 @@ import {
FmdGoodOutlined as FmdGoodOutlinedIcon,
GroupOutlined as GroupOutlinedIcon
} from '@mui/icons-material';
import theme from '../theme.jsx';
import { Button, Tooltip } from '@mui/material';
import theme, { getTheme } from '../theme.jsx';
import { Button, Skeleton, Tooltip } from '@mui/material';
import { Index } from 'react-instantsearch-dom';
import { Context } from '../context/ContextApi.jsx';
import { toast } from 'react-toastify';
const AdminNavBar = (props) => {
const location = useLocation();
const { globalUrl, userdata, isCloud, isLoaded,removeCookie, handleStatusChange, selectedStatus, setSelectedStatus, handleEditOrg, serverside, notifications, handleGetOrg, orgId, checkLogin, setNotifications, stripeKey, setSelectedOrganization, selectedOrganization } = props;
const { globalUrl, userdata, isCloud,isOrgLoaded, isLoaded,removeCookie, handleStatusChange, selectedStatus, setSelectedStatus, handleEditOrg, serverside, notifications, handleGetOrg, orgId, checkLogin, setNotifications, stripeKey, setSelectedOrganization, selectedOrganization } = props;
const [selectedItem, setSelectedItem] = useState("Organization");
const [isSelectedFiles, setIsSelectedFiles] = useState(true);
const [isSelectedDataStore, setIsSelectedDataStore] = useState(true);
const [isIntegrationPartner, setIsIntegrationPartner] = useState(false);
const [isChildOrg, setIsChildOrg] = useState(false);
const [isGlobalUser, setIsGlobalUser] = useState(false);
const [visibleItems, setVisibleItems] = useState([]);
const [isUserDataLoaded, setIsUserDataLoaded] = useState(false);
const items = [
{ iconSrc: <BusinessIcon />, alt: "Organization Icon", text: "Organization", component: OrganizationTab, props: { isIntegrationPartner, isChildOrg, isGlobalUser, globalUrl,removeCookie, selectedStatus, isLoaded, setSelectedStatus, handleStatusChange, handleEditOrg, handleGetOrg, userdata, isCloud, serverside, notifications, checkLogin, setNotifications, stripeKey, setSelectedOrganization, selectedOrganization } },
{ iconSrc: <PermIdentityIcon />, alt: "Users Icon", text: "Users", component: UserManagmentTab, props: { globalUrl, userdata, serverside, isCloud, selectedOrganization, setSelectedOrganization, handleEditOrg } },
{ iconSrc: <HttpsOutlinedIcon />, alt: "App Auth Icon", text: "App_auth", component: AppAuthTab, props: { globalUrl, userdata, isCloud, selectedOrganization } },
{ iconSrc: <StorageOutlinedIcon />, alt: "Datastore Icon", text: "Datastore", component: CacheView, props: { globalUrl, userdata, selectedOrganization, serverside, isSelectedDataStore, orgId , isCloud} },
{ iconSrc: <InsertDriveFileOutlinedIcon />, alt: "Files Icon", text: "Files", component: Files, props: { isCloud, globalUrl, userdata, serverside, selectedOrganization, isSelectedFiles } },
{ iconSrc: <AccessTimeOutlinedIcon />, alt: "Trigger Icon", text: "Triggers", component: SchedulesTab, props: { globalUrl, userdata, isCloud, serverside } },
{ iconSrc: <FmdGoodOutlinedIcon />, alt: "Environments Icon", text: "Locations", component: EnvironmentTab, props: { globalUrl, userdata, isCloud, selectedOrganization } },
{ iconSrc: <GroupOutlinedIcon />, alt: "Tenants Icon", text: "Tenants", component: TenantsTab, props: {isCloud, globalUrl, userdata, serverside, selectedOrganization, setSelectedOrganization, checkLogin } }
];
useEffect(() => {
if (userdata && userdata?.active_org?.id?.length > 0) {
setIsUserDataLoaded(true);
}
}, [userdata]);
const { themeMode, brandColor } = React.useContext(Context);
const theme = getTheme(themeMode, brandColor);
const HandlePartnerChange = () => {
if (userdata?.id?.length > 0) {
const isIntegrationPartner = userdata?.org_status?.includes("integration_partner") || false;
setIsIntegrationPartner(isIntegrationPartner);
const isChildOrg = userdata?.org_status?.includes("sub_org") || false;
setIsChildOrg(isChildOrg);
const isGlobalUser = userdata?.active_org?.branding?.global_user || false;
setIsGlobalUser(isGlobalUser);
} else {
setIsIntegrationPartner(false);
setIsChildOrg(false);
setIsGlobalUser(false);
}
}
useEffect(() => {
if (userdata && userdata?.id?.length > 0) {
HandlePartnerChange();
}
}, [userdata]);
const HandleVisibleTabs = () => {
if (userdata?.id?.length > 0) {
if (userdata?.active_org?.role === "admin" || userdata?.support) {
setVisibleItems(items);
}else {
const filteredItems = items.filter(item => item.text !== "Users" && item.text !== "Files" && item.text !== "Datastore" && item.text !== "Triggers" && item.text !== "Locations");
setVisibleItems(filteredItems);
}
}
}
useEffect(() => {
if (isIntegrationPartner && isChildOrg && !isGlobalUser) {
// Filter out Users and Tenants tabs
if (userdata?.active_org?.role === "admin" || userdata?.support) {
const filteredItems = items.filter(item =>
item.text !== "Users" && item.text !== "Tenants"
);
setVisibleItems(filteredItems);
}else {
const filteredItems = items.filter(item =>
item.text !== "Users" && item.text !== "Tenants" && item.text !== "Files" && item.text !== "Datastore" && item.text !== "Triggers" && item.text !== "Locations"
);
setVisibleItems(filteredItems);
}
} else {
HandleVisibleTabs();
}
}, [isIntegrationPartner, isChildOrg, isGlobalUser, selectedOrganization, userdata]);
const navigate = useNavigate();
@@ -52,18 +132,6 @@ const AdminNavBar = (props) => {
setSelectedItem("Organization");
}
}, [location.search]);
const items = [
{ iconSrc: <BusinessIcon />, alt: "Organization Icon", text: "Organization", component: OrganizationTab, props: { globalUrl,removeCookie, selectedStatus, isLoaded, setSelectedStatus, handleStatusChange, handleEditOrg, handleGetOrg, userdata, isCloud, serverside, notifications, checkLogin, setNotifications, stripeKey, setSelectedOrganization, selectedOrganization } },
{ iconSrc: <PermIdentityIcon />, alt: "Users Icon", text: "Users", component: UserManagmentTab, props: { globalUrl, userdata, serverside, isCloud, selectedOrganization, setSelectedOrganization, handleEditOrg } },
{ iconSrc: <HttpsOutlinedIcon />, alt: "App Auth Icon", text: "App_auth", component: AppAuthTab, props: { globalUrl, userdata, isCloud, selectedOrganization } },
{ iconSrc: <StorageOutlinedIcon />, alt: "Datastore Icon", text: "Datastore", component: CacheView, props: { globalUrl, userdata, selectedOrganization, serverside, isSelectedDataStore, orgId , isCloud} },
{ iconSrc: <InsertDriveFileOutlinedIcon />, alt: "Files Icon", text: "Files", component: Files, props: { isCloud, globalUrl, userdata, serverside, selectedOrganization, isSelectedFiles } },
{ iconSrc: <AccessTimeOutlinedIcon />, alt: "Trigger Icon", text: "Triggers", component: SchedulesTab, props: { globalUrl, userdata, isCloud, serverside } },
{ iconSrc: <FmdGoodOutlinedIcon />, alt: "Environments Icon", text: "Locations", component: EnvironmentTab, props: { globalUrl, userdata, isCloud, selectedOrganization } },
{ iconSrc: <GroupOutlinedIcon />, alt: "Tenants Icon", text: "Tenants", component: TenantsTab, props: {isCloud, globalUrl, userdata, serverside, selectedOrganization, setSelectedOrganization, checkLogin } }
];
const setConfig = (newValue) => {
setSelectedItem(newValue);
@@ -76,12 +144,65 @@ const AdminNavBar = (props) => {
}
};
useEffect(() => {
if (isIntegrationPartner && isChildOrg && !isGlobalUser && isOrgLoaded && isUserDataLoaded) {
const queryParams = new URLSearchParams(location.search);
const tabName = queryParams?.get('admin_tab')?.toLowerCase();
if (tabName === "sso" || tabName === "branding") {
toast.info("You are not allowed to access this tab. Please contact your admin for more information. Redirecting to Organization Configuration tab.");
setTimeout(() => {
setSelectedItem("Organization");
navigate(`?admin_tab=org_config`, { replace: true });
window.location.reload();
}
, 3000);
}
const params = new URLSearchParams(location.search);
const tab = params?.get('tab')?.toLowerCase();
if (tab === "users" || tab === "tenants") {
toast.info("You are not allowed to access this tab. Please contact your admin for more information. Redirecting to Organization Configuration tab.");
setTimeout(() => {
setSelectedItem("Organization");
navigate(`?admin_tab=org_config`, { replace: true });
window.location.reload();
}
, 3000);
}
} else if (userdata && isOrgLoaded && isUserDataLoaded && userdata?.active_org?.role !== "admin" && !userdata?.support) {
const queryParams = new URLSearchParams(location.search);
const tabName = queryParams?.get('admin_tab')?.toLowerCase();
if (tabName === "sso") {
toast.info("You are not allowed to access this tab. Please contact your admin for more information. Redirecting to Organization Configuration tab.");
setTimeout(() => {
setSelectedItem("Organization");
navigate(`?admin_tab=org_config`, { replace: true });
window.location.reload();
}
, 3000);
}
const params = new URLSearchParams(location.search);
const tab = params?.get('tab')?.toLowerCase();
if (tab === "users" || tab === "locations" || tab === "environments" || tab === "files" || tab === "datastore" || tab === "triggers") {
toast.info("You are not allowed to access this tab. Please contact your admin for more information. Redirecting to Organization Configuration tab.");
setTimeout(() => {
setSelectedItem("Organization");
navigate(`?admin_tab=org_config`, { replace: true });
window.location.reload();
}
, 3000);
}
}
}, [isIntegrationPartner, isChildOrg, isGlobalUser, location.search, userdata, isOrgLoaded, isUserDataLoaded]);
const renderComponent = () => {
const selectedItemData = items.find(item => item.text === selectedItem);
const selectedItemData = visibleItems.find(item => item.text === selectedItem);
if (!selectedItemData) {
setSelectedItem("Organization");
// If no tab is specified, default to "Organization" tab
return <OrganizationTab globalUrl={globalUrl} removeCookie={removeCookie} selectedStatus={selectedStatus} isLoaded={isLoaded} setSelectedStatus={setSelectedStatus} handleStatusChange={handleStatusChange} handleEditOrg={handleEditOrg} handleGetOrg={handleGetOrg} userdata={userdata} isCloud={isCloud} serverside={serverside} notifications={notifications} checkLogin={checkLogin} setNotifications={setNotifications} stripeKey={stripeKey} setSelectedOrganization={setSelectedOrganization} selectedOrganization={selectedOrganization}/>;
return <OrganizationTab isIntegrationPartner={isIntegrationPartner} isChildOrg={isChildOrg} isGlobalUser={isGlobalUser} globalUrl={globalUrl} removeCookie={removeCookie} selectedStatus={selectedStatus} isLoaded={isLoaded} setSelectedStatus={setSelectedStatus} handleStatusChange={handleStatusChange} handleEditOrg={handleEditOrg} handleGetOrg={handleGetOrg} userdata={userdata} isCloud={isCloud} serverside={serverside} notifications={notifications} checkLogin={checkLogin} setNotifications={setNotifications} stripeKey={stripeKey} setSelectedOrganization={setSelectedOrganization} selectedOrganization={selectedOrganization}/>;
};
const ComponentToRender = selectedItemData.component;
@@ -97,15 +218,16 @@ const AdminNavBar = (props) => {
: selectedOrganization?.image;
return (
!isOrgLoaded && !isUserDataLoaded ? <Loader /> :
<Wrapper>
<div style={{ flexDirection: 'column', width: 220, }}>
<nav style={{ padding: '25px 25px 3px 25px', height: isCloud ? "calc(100% - 60px)" : "calc(100% - 30px)" , fontSize: '16px', borderTopLeftRadius: 8, borderBottomLeftRadius: 8, background: '#212121', color: '#9CA3AF' }}>
<nav style={{ padding: '25px 25px 3px 25px', height: isCloud ? "calc(100% - 60px)" : "calc(100% - 30px)" , fontSize: '16px', borderTopLeftRadius: 8, borderBottomLeftRadius: 8, background: theme.palette.platformColor, color: '#9CA3AF' }}>
<div style={{ display: 'flex', alignItems: 'center', }}>
<img loading="lazy" src={imageData} alt="Logo" style={{ width: '30px', borderRadius: 8, height: '30px', marginRight: '8px' }} />
<div style={{
fontFamily: theme?.typography?.fontFamily,
fontSize: '16px',
color: "#FFFFFF",
color: theme.palette.text.primary,
fontWeight: 400,
overflow: 'hidden',
textOverflow: 'ellipsis',
@@ -114,8 +236,8 @@ const AdminNavBar = (props) => {
marginLeft: 5,
}}>{selectedOrganization?.name}</div>
</div>
<div style={{ borderTop: '1px solid #494949', marginTop: 23 }} />
{items.map((item, index) => (
<div style={{ borderTop: theme.palette.defaultBorder, marginTop: 23 }} />
{visibleItems.map((item, index) => (
<Tooltip
key={index}
title={
@@ -132,11 +254,10 @@ const AdminNavBar = (props) => {
color="primary"
sx={{
gap: 1,
"&:hover": {
backgroundColor: "#323232 !important",
},
"&.MuiButton-root": {
color: selectedItem === item.text ? "#FFFFFF" : "#9E9E9E",
color: selectedItem === item.text
? theme.palette.text.primary
: theme.palette.text.secondary,
fontSize: 16,
backgroundColor: "transparent",
textTransform: "none",
@@ -149,13 +270,16 @@ const AdminNavBar = (props) => {
justifyContent: "flex-start",
borderLeft:
selectedItem === item.text
? "3px solid rgba(255, 132, 68, 1)"
? `3px solid ${theme.palette.primary.main}`
: "none",
borderTopLeftRadius: selectedItem === item.text ? "2.5px" : null,
borderBottomLeftRadius: selectedItem === item.text ? "2.5px" : null,
paddingLeft: selectedItem === item.text ? "15px" : "10px",
fontWeight: selectedItem === item.text ? 200 : "normal",
flex: 1,
"&:hover": {
backgroundColor: theme.palette.hoverColor,
},
},
"&.Mui-disabled": {
color: "#6F6F6F",
@@ -183,6 +307,112 @@ const AdminNavBar = (props) => {
export default AdminNavBar;
const Loader = () => {
const dummyItems = Array.from({ length: 6 });
const dummyNavItems = Array.from({ length: 6 });
const dummyTabItems = ['Org Configuration', 'SSO', 'Notifications', 'Billing & Stats', 'Branding'];
const { leftSideBarOpenByClick, windowWidth, themeMode } = useContext(Context);
const theme = getTheme(themeMode);
return (
<div style={{
display: 'flex',
width: '100%',
height: '100%',
minHeight: '100vh',
maxWidth: '1200px',
fontFamily: 'Arial, sans-serif',
paddingLeft: leftSideBarOpenByClick ? windowWidth <= 1300 ? 220 : 200 : 80,
transition: "padding-left 0.3s ease",
}}>
<div style={{
width: '220px',
backgroundColor: theme.palette.platformColor,
borderTopLeftRadius: '8px',
borderBottomLeftRadius: '8px',
padding: '25px 25px 3px 25px',
display: 'flex',
flexDirection: 'column'
}}>
<div style={{ display: 'flex', alignItems: 'center', marginBottom: '20px' }}>
<Skeleton
width="30px"
height="30px"
sx={{ borderRadius: '8px', marginRight: '8px', }}
/>
<Skeleton width="120px" height="24px" />
</div>
{/* Divider */}
<Skeleton
width="100%"
height="1px"
sx={{ marginBottom: '15px' }}
/>
{/* Nav Items */}
<div style={{ display: 'flex', flexDirection: 'column', gap: '5px', width: '100%' }}>
{dummyNavItems.map((_, index) => (
<div key={index} style={{ display: 'flex', alignItems: 'center', padding: '5px 0' }}>
<Skeleton
width="18px"
height="18px"
sx={{ marginRight: '10px' }}
/>
<Skeleton width={`${100 + Math.random() * 40}px`} height="36px" />
</div>
))}
</div>
</div>
<div style={{
flex: 1,
backgroundColor: theme.palette.platformColor,
borderTopRightRadius: '8px',
borderBottomRightRadius: '8px',
borderLeft: theme.palette.defaultBorder,
display: 'flex',
flexDirection: 'column'
}}>
<div style={{
display: 'flex',
borderBottom: theme.palette.defaultBorder,
padding: '0 16px'
}}>
{dummyTabItems.map((_, index) => (
<div
key={index}
style={{
flex: 1,
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
padding: '28px 0',
borderBottom: index === 0 ? '2px solid #FF8444' : 'none'
}}
>
<Skeleton width={`${80 + Math.random() * 30}px`} height="24px" />
</div>
))}
</div>
<div style={{ flex: 1, padding: '24px' }}>
<div style={{ display: 'flex', flexDirection: 'column', width: '100%', margin: '0 auto', alignItems: 'flex-start' }}>
<Skeleton variant='square' width="200px" height="200px" sx={{ marginBottom: '20px' }} />
<div style={{ display: 'flex', flexDirection: 'column', gap: '10px', width: '100%', marginTop: '50px' }}>
{dummyItems.map((_, index) => (
<div key={index} style={{ display: 'flex', alignItems: 'center', width: '100%' }}>
<Skeleton width={'400px'} height="36px" />
</div>
))}
</div>
</div>
</div>
</div>
</div>
);
};
const PaddingWrapper2 = memo(({ children }) => {
return (
+48 -34
View File
@@ -29,7 +29,7 @@ import {
Tooltip,
} from "@mui/material";
import throttle from "lodash/throttle";
import theme from "../theme.jsx";
import {getTheme} from "../theme.jsx";
import { validateJson, collapseField, } from "../views/Workflows.jsx";
import DeleteIcon from "@mui/icons-material/Delete";
@@ -37,6 +37,8 @@ import { Context } from "../context/ContextApi.jsx";
function CustomTabPanel(props) {
const { children, value, index, ...other } = props;
const {themeMode} = useContext(Context);
const theme = getTheme(themeMode)
return (
<div
@@ -47,7 +49,7 @@ function CustomTabPanel(props) {
{...other}
>
{value === index && (
<Box sx={{ p: 3, padding: 0, backgroundColor: "#1a1a1a" }}>
<Box sx={{ p: 3, padding: 0, backgroundColor: theme.palette.backgroundColor}}>
{children}
</Box>
)}
@@ -100,6 +102,8 @@ const ApiExplorer = memo(({ openapi, globalUrl, userdata, HandleApiExecution, se
const [selectedActionIndex, setSelectedActionIndex] = useState(0);
const [ExampleBody, setExampleBody] = useState({});
const [filteredActions, setFilteredActions] = useState([]);
const {themeMode} = useContext(Context);
const theme = getTheme(themeMode)
const [firstSendDone, setFirstSendDone] = useState(false)
@@ -1391,6 +1395,8 @@ const ActionsList = memo(({
const [searchQuery, setSearchQuery] = useState("");
const [visibleActions, setVisibleActions] = useState([]);
const {themeMode} = useContext(Context);
const theme = getTheme(themeMode);
useEffect(() => {
@@ -1463,7 +1469,7 @@ const ActionsList = memo(({
alt="app logo"
style={{ marginLeft: 20, borderRadius: 8 }}
/>
<Typography style={{ fontSize: 24, fontWeight: 'bold', marginLeft: 20, overflow: 'hidden', color: '#F1F1F1'
<Typography style={{ fontSize: 24, fontWeight: 'bold', marginLeft: 20, overflow: 'hidden', color: theme.palette.textColor
}}>
{info.title}
</Typography>
@@ -1471,7 +1477,7 @@ const ActionsList = memo(({
</Tooltip>
</a>
) : (
<Typography style={{ fontSize: 24, fontWeight: 'bold', marginLeft: 20, overflow: 'hidden', color: '#F1F1F1'
<Typography style={{ fontSize: 24, fontWeight: 'bold', marginLeft: 20, overflow: 'hidden', color: theme.palette.textColor
}}>
Api Explorer
</Typography>
@@ -1504,7 +1510,7 @@ const ActionsList = memo(({
style={{
marginLeft: 20,
marginTop: 15,
backgroundColor: "#1a1a1a",
backgroundColor: theme.palette.backgroundColor,
overflowY: "auto",
height: (isLoaded && isLoggedIn) ? "calc(100vh - 190px)" : "calc(100vh - 260px)",
paddingRight: 5,
@@ -1522,7 +1528,7 @@ const ActionsList = memo(({
textTransform: "none",
backgroundColor:
selectedActionIndex === actionIndex
? "#3f3f3f"
? theme.palette.hoverColor
: "transparent",
border: "none",
justifyContent: "flex-start",
@@ -1535,7 +1541,7 @@ const ActionsList = memo(({
textWrap: "nowrap",
textOverflow: "ellipsis",
"&:hover": {
backgroundColor: "#2f2f2f",
backgroundColor: theme.palette.hoverColor,
},
}}
onClick={() => handleActionClick(actionIndex, action)}
@@ -1554,7 +1560,7 @@ const ActionsList = memo(({
</span>
<span
style={{
color: "white",
color: theme.palette.textColor,
textOverflow: "ellipsis",
overflow: "hidden",
}}
@@ -1564,7 +1570,7 @@ const ActionsList = memo(({
</Button>
))
) : (
<div style={{ padding: "15px", color: "white", textAlign: "center" }}>
<div style={{ padding: "15px", color: theme.palette.textColor, textAlign: "center" }}>
No actions found
</div>
)}
@@ -1602,6 +1608,8 @@ const Action = memo((
const [disableExecuteButton, setDisableExecuteButton] = useState(false);
const [showResponseLoader, setShowResponseLoader] = useState(false);
const [appAuthentication, setAppAuthentication] = useState([])
const {themeMode} = useContext(Context);
const theme = getTheme(themeMode);
const parseHeaders = (headersString) => {
if (headersString?.length > 0) {
const headersArray = headersString.split("\n");
@@ -2082,7 +2090,7 @@ const Action = memo((
fontWeight: 700,
marginLeft: 40,
marginBottom: 5,
color: "rgba(241, 241, 241, 1)",
color: theme.palette.textColor
}}
>
{actionname}
@@ -2095,7 +2103,7 @@ const Action = memo((
borderRadius: 6,
marginLeft: "40px",
marginTop: 2,
backgroundColor: "#212121",
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
height: 51,
alignItems: 'center',
}}
@@ -2109,7 +2117,7 @@ const Action = memo((
backgroundColor: "transparent",
"& .MuiSelect-select": {
color: RequestMethods.find((method) => method.value === selectedMethod)
?.color || "#212121",
?.color || theme.palette.textFieldStyle.backgroundColor,
},
}}
MenuProps={{
@@ -2117,14 +2125,14 @@ const Action = memo((
sx: {
padding: 0,
margin: 0,
backgroundColor: "#212121",
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
},
},
MenuListProps: {
sx: {
padding: 0,
margin: 0,
backgroundColor: "#212121",
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
},
},
}}
@@ -2135,20 +2143,20 @@ const Action = memo((
value={method.value}
sx={{
color: method.color,
backgroundColor: "#212121",
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
border: "none",
marginBottom: 0.25,
"&:hover": {
backgroundColor: method.color,
color: "#f9fcf5",
color: theme.palette.textFieldStyle.color,
},
"&.Mui-selected": {
backgroundColor: method.color,
color: "#f9fcf5",
color: theme.palette.textFieldStyle.color,
border: "none",
"&:hover": {
backgroundColor: method.color,
color: "#f9fcf5",
color: theme.palette.textFieldStyle.color,
},
},
"&.Mui-focusVisible": {
@@ -2167,9 +2175,8 @@ const Action = memo((
inputProps={{
style: {
margin: "auto",
backgroundColor: "transparent",
border: "none",
color: "rgba(241, 241, 241, 1)",
color: theme.palette.textFieldStyle.color,
display: 'flex',
height: '100%',
alignItems: 'center',
@@ -2331,7 +2338,7 @@ const Action = memo((
style={{
display: "flex",
justifyContent: "center",
background: "rgba(26, 26, 26, 1)",
background: theme.palette.backgroundColor,
}}
>
<Tabs
@@ -2386,7 +2393,7 @@ const Action = memo((
<TableContainer
style={{
minWidth: 696,
backgroundColor: "#212121",
backgroundColor: theme.palette.platformColor,
borderRadius: 6,
border: "1px solid rgba(73, 73, 73, 1)",
}}
@@ -2436,7 +2443,7 @@ const Action = memo((
}
inputProps={{
style: {
backgroundColor: "rgba(33, 33, 33, 1)",
backgroundColor: theme.palette.platformColor,
padding: "4px 8px",
},
}}
@@ -2494,7 +2501,7 @@ const Action = memo((
</InputAdornment>
),
style: {
backgroundColor: "rgba(33, 33, 33, 1)",
backgroundColor: theme.palette.platformColor,
padding: "4px 8px",
},
}}
@@ -2600,7 +2607,7 @@ const Action = memo((
<TableContainer
style={{
minWidth: 696,
backgroundColor: "#212121",
backgroundColor: theme.palette.platformColor,
borderRadius: 6,
border: "1px solid rgba(73, 73, 73, 1)",
}}
@@ -2640,7 +2647,7 @@ const Action = memo((
type="text"
inputProps={{
style: {
backgroundColor: "rgba(33, 33, 33, 1)",
backgroundColor: theme.palette.platformColor ,
padding: "4px 8px",
},
}}
@@ -2694,7 +2701,7 @@ const Action = memo((
value={row.value}
inputProps={{
style: {
backgroundColor: "rgba(33, 33, 33, 1)",
backgroundColor: theme.palette.platformColor,
padding: "4px 8px",
},
}}
@@ -2794,10 +2801,10 @@ const Action = memo((
marginLeft: 10
}}
>
<span style={{ fontSize: 16, fontWeight: 600 }}>
<span style={{ fontSize: 16, fontWeight: 600, color: theme.palette.textColor }}>
{action.name.replaceAll("_", " ")}
</span>
<p >
<p style={{color: theme.palette.textColor}}>
{action.description
? action.description
: ""}
@@ -2813,6 +2820,8 @@ const ActionResponse = memo(({ apiResponse, ExampleBody, isLoggedIn, isLoaded })
const [responseTabIndex, setResponseTabIndex] = useState(0)
const [oldResponse, setOldResponse] = useState(apiResponse)
const [highlight, setHighlight] = useState(false)
const {themeMode } = useContext(Context)
const theme = getTheme(themeMode)
const MIN_HEIGHT = 50
@@ -2934,7 +2943,7 @@ const ActionResponse = memo(({ apiResponse, ExampleBody, isLoggedIn, isLoaded })
style={{
width: '100%',
height: height,
backgroundColor: '#1a1a1a',
backgroundColor: theme.palette.backgroundColor,
display: 'flex',
flexDirection: 'column',
borderTop: '1px solid rgba(255,255,255,0.2)',
@@ -2988,7 +2997,7 @@ const ActionResponse = memo(({ apiResponse, ExampleBody, isLoggedIn, isLoaded })
<ReactJson
src={formData(ExampleBody)}
theme={theme.palette.jsonTheme}
style={{ backgroundColor: '#1a1a1a', padding: 5 }}
style={{...theme.palette.reactJsonStyle, border: "none"}}
collapsed={false}
iconStyle={theme.palette.jsonIconStyle}
collapseStringsAfterLength={theme.palette.jsonCollapseStringsAfterLength}
@@ -3005,6 +3014,10 @@ const ActionResponse = memo(({ apiResponse, ExampleBody, isLoggedIn, isLoaded })
});
const ResponseTabWrapper = memo(({ apiResponse }) => {
const {themeMode } = useContext(Context)
const theme = getTheme(themeMode)
const handleReactJsonClipboard = (copy) => {
const elementName = "copy_element_shuffle";
let copyText = document.getElementById(elementName);
@@ -3036,7 +3049,7 @@ const ResponseTabWrapper = memo(({ apiResponse }) => {
<ReactJson
src={apiResponse}
theme={theme.palette.jsonTheme}
style={{ backgroundColor: "#1a1a1a", padding: 5 }}
style={{...theme.palette.reactJsonStyle, border: "none"}}
shouldCollapse={(jsonField) => {
return collapseField(jsonField)
}}
@@ -3049,7 +3062,8 @@ const ResponseTabWrapper = memo(({ apiResponse }) => {
)})
const PaddingWrapper = memo(({ isLoggedIn, isLoaded, children }) => {
const { leftSideBarOpenByClick, windowWidth } = useContext(Context);
const { leftSideBarOpenByClick, windowWidth, themeMode } = useContext(Context);
const theme = getTheme(themeMode)
return (
<div
style={{
@@ -3058,7 +3072,7 @@ const PaddingWrapper = memo(({ isLoggedIn, isLoaded, children }) => {
? windowWidth >= 1920 ? "calc(100% - 630px)" : "calc(100% - 570px)"
: windowWidth >= 1920 ? "calc(100vw - 460px)": "calc(100% - 410px)"
: windowWidth >= 1920 ? "calc(100% - 370px)" : "calc(100% - 320px)",
backgroundColor: "#1a1a1a",
backgroundColor: theme.palette.backgroundColor,
position: "fixed",
bottom: 0,
right: 0,
+68 -52
View File
@@ -13,7 +13,7 @@ import {
} from "@mui/icons-material";
import { useNavigate } from "react-router-dom";
import { toast } from "react-toastify";
import theme from "../theme.jsx";
import {getTheme} from "../theme.jsx";
import Markdown from "react-markdown";
import AuthenticationOauth2 from "../components/Oauth2Auth.jsx";
import { isMobile } from "react-device-detect"
@@ -88,7 +88,9 @@ const AppAuthTab = memo((props) => {
const [searchQuery, setSearchQuery] = React.useState("");
const [showAppModal, setShowAppModal] = useState(false)
const [showAuthenticationLoader, setShowAuthenticationLoader] = useState(true)
const [showAppAuthGroupLoader, setShowAppAuthGroupLoader] = useState(true)
const [showAppAuthGroupLoader, setShowAppAuthGroupLoader] = useState(true)
const { themeMode, supportEmail, brandColor } = useContext(Context)
const theme = getTheme(themeMode, brandColor)
const changeDistribution = (data) => {
//changeDistributed(data, !isDistributed)
editAuthenticationConfig(data.id, "suborg_distribute")
@@ -277,7 +279,7 @@ const AppAuthTab = memo((props) => {
}}
>
<DialogTitle>
<span style={{ color: "white" }}>
<span style={{ color: theme.palette.textColor }}>
Edit authentication for {selectedAuthentication.app.name.replaceAll("_", " ")} (
{selectedAuthentication.label})
</span>
@@ -297,7 +299,7 @@ const AppAuthTab = memo((props) => {
InputProps={{
style: {
height: 50,
color: "white",
color: theme.palette.textColor,
},
}}
color="primary"
@@ -349,7 +351,7 @@ const AppAuthTab = memo((props) => {
InputProps={{
style: {
height: 50,
color: "white",
color: theme.palette.textColor,
},
}}
color="primary"
@@ -568,7 +570,7 @@ const AppAuthTab = memo((props) => {
})
.then((responseJson) => {
if (responseJson.success === false) {
toast("Failed to create. Please try again, or contact support@shuffler.io")
toast(`Failed to create. Please try again, or contact ${supportEmail}`)
} else {
// Close the modal
setAppAuthenticationGroupModalOpen(false)
@@ -660,7 +662,7 @@ const AppAuthTab = memo((props) => {
}}
>
<DialogTitle>
<span style={{ color: "white" }}>App Authentication Groups</span>
<span style={{ color: theme.palette.textColor }}>App Authentication Groups</span>
</DialogTitle>
<DialogContent style={{marginLeft: 0, paddingLeft: 0, }}>
@@ -677,7 +679,7 @@ const AppAuthTab = memo((props) => {
InputProps={{
style: {
height: "50px",
color: "white",
color: theme.palette.textColor,
fontSize: "1em",
},
}}
@@ -853,22 +855,22 @@ const AppAuthTab = memo((props) => {
) : null;
return (
<div style={{width: "100%", minHeight: 1100, maxHeight: 1700, overflowY: "auto", scrollbarColor: '#494949 transparent', scrollbarWidth: 'thin',boxSizing: 'border-box', padding: "27px 10px 19px 27px", height:"100%", backgroundColor: '#212121',borderTopRightRadius: '8px', borderBottomRightRadius: 8, borderLeft: "1px solid #494949", }}>
<div style={{width: "100%", minHeight: 1100, maxHeight: 1700, overflowY: "auto", scrollbarColor: theme.palette.scrollbarColorTransparent, scrollbarWidth: 'thin',boxSizing: 'border-box', padding: "27px 10px 19px 27px", height:"100%", backgroundColor: theme.palette.platformColor,borderTopRightRadius: '8px', borderBottomRightRadius: 8, borderLeft: theme.palette.defaultBorder, }}>
{appModal}
<div style={{ height: "100%", width: "calc(100% - 20px)", scrollbarColor: '#494949 transparent', scrollbarWidth: 'thin'}}>
<div style={{ height: "100%", width: "calc(100% - 20px)", scrollbarColor: theme.palette.scrollbarColorTransparent, scrollbarWidth: 'thin'}}>
<div style={{ width: 'auto', display:'flex',}}>
<div style={{display: 'flex', flexDirection: 'column'}}>
<h2 style={{ marginBottom: 8, marginTop: 0, color: "#FFFFFF" }}>App Authentication</h2>
<div>
<span style={{}}>
<Typography variant='h5' style={{ marginBottom: 8, marginTop: 0, }}>App Authentication</Typography>
<div style={{display: 'flex', flexDirection: 'row', alignItems: 'center', }}>
<Typography variant='body2' color="textSecondary">
Control the authentication options for individual apps.
</span>
</Typography>
&nbsp;
<a
target="_blank"
rel="noopener noreferrer"
href="/docs/organizations#app_authentication"
style={{ color: "#FF8444" }}
style={{ color: theme.palette.linkColor }}
>
Learn more about App Authentication
</a>
@@ -877,7 +879,7 @@ const AppAuthTab = memo((props) => {
{isCloud ?
<Button
style={{ color: '#1a1a1a', textTransform: 'none', backgroundColor: "#FF8444", marginLeft:"auto", borderRadius: 4,fontSize: 16, minWidth: 162, height: 40, boxShadow:'none', }}
style={{ textTransform: 'none', marginLeft:"auto", borderRadius: 4,fontSize: 16, minWidth: 162, height: 40, boxShadow:'none', }}
variant="contained"
color="primary"
disabled={!isCloud}
@@ -899,7 +901,7 @@ const AppAuthTab = memo((props) => {
style={{
borderRadius: 4,
marginTop: 24,
border: "1px solid #494949",
border: theme.palette.defaultBorder,
width: "100%",
overflowX: "auto",
paddingBottom: 0,
@@ -917,7 +919,7 @@ const AppAuthTab = memo((props) => {
}}>
<ListItem style={{ width: "100%", paddingTop: 10, paddingBottom: 10, paddingRight: 10, borderBottom: "1px solid #494949", display: 'table-row'}}>
<ListItem style={{ width: "100%", paddingTop: 10, paddingBottom: 10, paddingRight: 10, borderBottom: theme.palette.defaultBorder, display: 'table-row'}}>
{["Valid", "Label", "App Name", "Workflows", "Fields", "Edited", "Actions", "Distribution"].map((header, index) => (
<ListItemText
@@ -928,7 +930,7 @@ const AppAuthTab = memo((props) => {
padding: index === 0 ? "0px 8px 8px 15px": "0px 8px 8px 8px",
whiteSpace: "nowrap",
textOverflow: "ellipsis",
borderBottom: "1px solid #494949",
borderBottom: theme.palette.defaultBorder,
position: "sticky",
}}
/>
@@ -942,7 +944,7 @@ const AppAuthTab = memo((props) => {
key={rowIndex}
style={{
display: "table-row",
backgroundColor: "#212121",
backgroundColor: theme.palette.platformColor,
}}
>
{Array(8)
@@ -959,7 +961,7 @@ const AppAuthTab = memo((props) => {
variant="text"
animation="wave"
sx={{
backgroundColor: "#1a1a1a",
backgroundColor: theme.palette.loaderColor,
height: "20px",
borderRadius: "4px",
}}
@@ -970,14 +972,14 @@ const AppAuthTab = memo((props) => {
))
: authentication?.length === 0 ? (
<div style={{ textAlign: 'center'}}>
<Typography style={{ color: "#FFFFFF", textAlign: 'center', padding: 20}}>
<Typography color="textPrimary" style={{ textAlign: 'center', padding: 20}}>
No authentication found.
</Typography>
</div>
):authentication.map((data, index) => {
var bgColor = "#212121";
var bgColor = themeMode === "dark" ? "#212121" : "#FFFFFF";
if (index % 2 === 0) {
bgColor = "#1A1A1A";
bgColor = themeMode === "dark" ? "#1A1A1A" : "#EAEAEA";
}
//console.log("Auth data: ", data)
@@ -1059,7 +1061,7 @@ const AppAuthTab = memo((props) => {
<ListItemText
primary={
<Tooltip title={"Try the app in our API explorer"} placement="top">
<a href={`/apis/${data.app.id}`} style={{ color: "#FF8444", textDecoration: "none", cursor: "pointer", }} target="_blank" rel="noopener noreferrer">
<a href={`/apis/${data.app.id}`} style={{ color: theme.palette.linkColor, textDecoration: "none", cursor: "pointer", }} target="_blank" rel="noopener noreferrer">
{data?.app?.name?.replaceAll("_", " ")}
</a>
</Tooltip>
@@ -1112,14 +1114,14 @@ const AppAuthTab = memo((props) => {
style={{
overflow: "hidden",
display: "table-cell",
verticalAlign: 'middle'
verticalAlign: 'middle',
}}
primaryTypographyProps={{
style: {
padding: 8
}
}}
primary={new Date(data.edited * 1000).toISOString()}
primary={new Date(data.edited * 1000).toISOString()}
/>
<ListItemText
style={{
@@ -1129,13 +1131,24 @@ const AppAuthTab = memo((props) => {
primaryTypographyProps={{ style: { display: "flex", flexDirection: 'row', padding: 8 } }}
>
<IconButton
onClick={() => {
updateAppAuthentication(data);
}}
disabled={data.org_id !== selectedOrganization.id}
>
<img src="/icons/editIcon.svg" alt="Edit icon" color="secondary" />
</IconButton>
onClick={() => updateAppAuthentication(data)}
disabled={data.org_id !== selectedOrganization.id}
>
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M16.1038 4.66848C16.3158 4.45654 16.5674 4.28843 16.8443 4.17373C17.1212 4.05903 17.418 4 17.7177 4C18.0174 4 18.3142 4.05903 18.5911 4.17373C18.868 4.28843 19.1196 4.45654 19.3315 4.66848C19.5435 4.88041 19.7116 5.13201 19.8263 5.40891C19.941 5.68582 20 5.9826 20 6.28232C20 6.58204 19.941 6.87882 19.8263 7.15573C19.7116 7.43263 19.5435 7.68423 19.3315 7.89617L8.43807 18.7896L4 20L5.21038 15.5619L16.1038 4.66848Z"
stroke={themeMode=== "dark" ? "#F1F1F1" : "#333"}
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
</IconButton>
{data.defined ? (
<Tooltip
color="primary"
@@ -1151,7 +1164,7 @@ const AppAuthTab = memo((props) => {
editAuthenticationConfig(data.id);
}}
>
<SelectAllIcon color="secondary" />
<SelectAllIcon color="textSecondary" />
</IconButton>
</Tooltip>
) : (
@@ -1439,6 +1452,9 @@ const SearchBox = ({ currentRefinement, refine, isSearchStalled, searchQuery, se
refine(searchQuery.trim());
};
const { themeMode, supportEmail } = useContext(Context);
const theme = getTheme(themeMode);
return (
<form noValidate action="" role="search">
<TextField
@@ -1452,7 +1468,7 @@ const SearchBox = ({ currentRefinement, refine, isSearchStalled, searchQuery, se
}}
InputProps={{
style: {
color: "white",
color: theme.palette.textFieldStyle.color,
fontSize: "1em",
height: 50,
borderRadius: 4,
@@ -1468,7 +1484,7 @@ const SearchBox = ({ currentRefinement, refine, isSearchStalled, searchQuery, se
{searchQuery?.length > 0 && (
<ClearIcon
style={{
color: "white",
color: theme.palette.textColor,
cursor: "pointer",
marginRight: 10
}}
@@ -1485,7 +1501,7 @@ const SearchBox = ({ currentRefinement, refine, isSearchStalled, searchQuery, se
style={{
backgroundImage:
"linear-gradient(to right, rgb(248, 106, 62), rgb(243, 64, 121))",
color: "white",
color: theme.palette.textColor,
border: "none",
padding: "10px 20px",
width: 100,
@@ -1523,8 +1539,6 @@ const SearchBox = ({ currentRefinement, refine, isSearchStalled, searchQuery, se
const Hits = ({
hits,
insights,
setIsAnyAppActivated,
searchQuery,
isCloud,
globalUrl,
@@ -1548,6 +1562,8 @@ const Hits = ({
}
)
const navigate = useNavigate();
const { themeMode, supportEmail } = useContext(Context);
const theme = getTheme(themeMode);
const normalizedString = (name) => {
if (typeof name === 'string') {
@@ -1732,7 +1748,7 @@ const Hits = ({
})
.then((response) => {
if (response.status !== 200) {
toast.error("Failed to get app data or App doesn't. Please contact support@shuffler.io");
toast.error(`Failed to get app data or App doesn't. Please contact ${supportEmail}`);
return;
}
return response.json();
@@ -1950,7 +1966,7 @@ const Hits = ({
return (
<div>
<DialogTitle id="draggable-dialog-title" style={{ cursor: "move", }}>
<div style={{ color: "white" }}>
<div style={{ color: theme.palette.textColor }}>
Authentication for {selectedApp.name.replaceAll("_", " ", -1)}
</div>
</DialogTitle>
@@ -2020,7 +2036,7 @@ const Hits = ({
}}
style={{
backgroundColor: theme.palette.surfaceColor,
color: "white",
color: theme.palette.textColor,
height: 50,
}}
>
@@ -2028,7 +2044,7 @@ const Hits = ({
key={"false"}
style={{
backgroundColor: theme.palette.inputColor,
color: "white",
color: theme.palette.textColor,
}}
value={"false"}
>
@@ -2038,7 +2054,7 @@ const Hits = ({
key={"true"}
style={{
backgroundColor: theme.palette.inputColor,
color: "white",
color: theme.palette.textColor,
}}
value={"true"}
>
@@ -2117,7 +2133,7 @@ const Hits = ({
PaperProps={{
style: {
pointerEvents: "auto",
color: "white",
color: theme.palette.textColor,
minWidth: 1100,
minHeight: 700,
maxHeight: 700,
@@ -2399,7 +2415,7 @@ const Hits = ({
href={selectedMeta.link}
style={{ textDecoration: "none", color: "#f85a3e" }}
>
<Button style={{ color: "white", }} variant="outlined" color="secondary">
<Button style={{ color: theme.palette.textColor, }} variant="outlined" color="secondary">
<EditIcon /> &nbsp;&nbsp;Edit
</Button>
</a>
@@ -2410,7 +2426,7 @@ const Hits = ({
style={{
height: "100%",
width: 1,
backgroundColor: "white",
backgroundColor: theme.palette.textColor,
marginLeft: 50,
marginRight: 50,
}}
@@ -2505,7 +2521,7 @@ const Hits = ({
height: 480,
overflowY: "auto",
scrollbarWidth: "thin",
scrollbarColor: "#494949 #2f2f2f",
scrollbarColor: theme.palette.scrollbarColor,
width: "100%",
}}
>
@@ -2536,7 +2552,7 @@ const Hits = ({
elevation={0}
style={{
...paperStyle,
backgroundColor: mouseHoverIndex === index ? "#2F2F2F" : "rgba(26, 26, 26, 1)",
backgroundColor: mouseHoverIndex === index ? theme.palette.cardHoverColor : theme.palette.cardBackgroundColor,
width: "100%",
}}
onMouseEnter={() => setMouseHoverIndex(index)}
@@ -2595,7 +2611,7 @@ const Hits = ({
gap: 8,
textOverflow: "ellipsis",
whiteSpace: "nowrap",
color: "#F1F1F1",
color: theme.palette.textColor,
}}
>
{normalizedString(data.name)}
@@ -2638,7 +2654,7 @@ const Hits = ({
))}
</div>
</div>
<Button style={{position: 'relative', borderRadius: 6, bottom: 10, marginRight: 10, fontSize: 16, backgroundColor: '#ff8544', color: "#1a1a1a", textTransform:'none', marginLeft: 'auto'}} onClick={(e)=> {e.preventDefault();e.stopPropagation();handleAppAuthenticationNew(data)()}}>
<Button variant='contained' color='primary' style={{position: 'relative', borderRadius: 6, bottom: 10, marginRight: 10, fontSize: 16, textTransform:'none', marginLeft: 'auto'}} onClick={(e)=> {e.preventDefault();e.stopPropagation();handleAppAuthenticationNew(data)()}}>
Authenticate app
</Button>
</div>
+19 -19
View File
@@ -42,9 +42,8 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => {
const parsedStyle = {
flex: 1,
padding: 20,
padding: "30px 20px 20px",
margin: 12,
paddingTop: 30,
backgroundColor: hover && !makeFancy ? theme.palette.surfaceColor : "transparent",
cursor: hover ? "pointer" : "default",
textAlign: "center",
@@ -346,23 +345,23 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => {
// Common dialog styles
const dialogStyle = {
borderRadius: 2,
border: "1px solid #494949",
border: theme.palette.DialogStyle.border,
minWidth: '500px',
fontFamily: theme?.typography?.fontFamily,
backgroundColor: "#1A1A1A",
backgroundColor: theme.palette.DialogStyle.backgroundColor,
zIndex: 1000,
'& .MuiDialogContent-root': {
backgroundColor: "#1A1A1A",
backgroundColor: theme.palette.DialogStyle.backgroundColor,
padding: '24px',
fontFamily: theme?.typography?.fontFamily,
},
'& .MuiDialogTitle-root': {
backgroundColor: "#1A1A1A",
backgroundColor: theme.palette.DialogStyle.backgroundColor,
padding: '24px',
fontFamily: theme?.typography?.fontFamily,
},
'& .MuiDialogActions-root': {
backgroundColor: "#1A1A1A",
backgroundColor: theme.palette.DialogStyle.backgroundColor,
padding: '16px 24px',
fontFamily: theme?.typography?.fontFamily,
},
@@ -397,7 +396,7 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => {
pl: 4,
pr: 3,
}}>
<Typography variant="h5" sx={{ fontWeight: 500, color: "#F1F1F1" }}>
<Typography variant="h5"color="textPrimary" sx={{ fontWeight: 500, }}>
Create New App
</Typography>
<IconButton
@@ -409,8 +408,8 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => {
onClose()
}}
sx={{
color: 'rgba(255, 255, 255, 0.7)',
'&:hover': { bgcolor: 'rgba(255, 255, 255, 0.1)' }
color: theme.palette.text.primary,
'&:hover': { bgcolor: theme.palette.hoverColor },
}}
>
<CloseIcon />
@@ -477,7 +476,7 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => {
px: 4,
}}>
<Typography variant="h6" sx={{
color: '#F1F1F1',
color: theme.palette.text.primary,
fontWeight: 500,
fontFamily: theme?.typography?.fontFamily
}}>
@@ -502,7 +501,7 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => {
</DialogTitle>
<DialogContent sx={{ px: 4, py: 3 }}>
<div style={{ display: "flex", fontSize: '14px', gap: '5px', alignItems: 'center', marginBottom: '10px', fontFamily: theme?.typography?.fontFamily, marginTop: '15px' }}>
<Typography sx={{ color: 'rgba(255,255,255,0.85)', fontSize: '16px' }}>
<Typography sx={{ color: theme.palette.text.primary, fontSize: '16px' }}>
Paste in the URI for the OpenAPI or find out
</Typography>
<Link style={{
@@ -583,6 +582,7 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => {
py: 1,
'&:hover': {
borderColor: '#FF8544',
color: '#FF8544',
bgcolor: 'rgba(255,133,68,0.1)'
},
textTransform: 'none',
@@ -665,7 +665,7 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => {
px: 4,
}}>
<Typography variant="h6" sx={{
color: '#F1F1F1',
color: theme.palette.text.primary,
fontWeight: 500,
fontFamily: theme?.typography?.fontFamily,
}}>
@@ -681,8 +681,8 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => {
setValidation(false)
}}
sx={{
color: 'rgba(255,255,255,0.7)',
'&:hover': { bgcolor: 'rgba(255,255,255,0.1)' }
color: theme.palette.text.primary,
'&:hover': { bgcolor: theme.palette.hoverColor }
}}
>
<CloseIcon />
@@ -690,7 +690,7 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => {
</DialogTitle>
<DialogContent sx={{ px: 4, py: 3, pt: 0 }}>
<Typography sx={{
color: 'rgba(255,255,255,0.85)',
color: theme.palette.text.primary,
mb: 2,
fontSize: '14px',
mt: 2,
@@ -706,10 +706,10 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => {
variant="outlined"
placeholder="API Documentation URL"
sx={{
bgcolor: theme.palette.platformColor,
bgcolor: theme.palette.textFieldStyle.backgroundColor,
'& .MuiOutlinedInput-root': {
height: '40px',
color: 'white',
color: theme.palette.text.primary,
'& fieldset': {
borderWidth: '1px',
borderImage: "linear-gradient(to right, #ff8544 0%, #ec517c 50%, #9c5af2 100%) 1",
@@ -757,7 +757,7 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => {
{circularLoader}
{
!validation &&
<Typography sx={{ color: '#c5c5c5', fontSize: '14px', fontFamily: theme?.typography?.fontFamily, }}>
<Typography color="textSecondary" sx={{ fontSize: '14px', fontFamily: theme?.typography?.fontFamily, }}>
This may take multiple minutes based on the size of the documentation.
</Typography>
}
+22 -27
View File
@@ -1,4 +1,4 @@
import React, { memo, useCallback, useEffect, useState } from 'react';
import React, { memo, useCallback, useEffect, useState, useContext } from 'react';
import { useNavigate } from 'react-router';
import {
@@ -25,12 +25,13 @@ import LaunchIcon from '@mui/icons-material/Launch';
import CheckCircleIcon from '@mui/icons-material/CheckCircle';
import { CloudDownloadOutlined, Delete } from '@mui/icons-material';
import { findSpecificApp } from '../components/AppFramework.jsx';
import theme from "../theme.jsx";
import {getTheme} from "../theme.jsx";
import YAML from 'yaml';
import { toast } from 'react-toastify';
import { Link } from 'react-router-dom';
import { InstantSearch, connectHits, connectSearchBox } from 'react-instantsearch-dom';
import algoliasearch from "algoliasearch/lite";
import { Context } from '../context/ContextApi.jsx';
const searchClient = algoliasearch(
"JNSS5CFDZZ",
@@ -51,6 +52,9 @@ const AppModal = ({ open, onClose, app, globalUrl, getApps}) => {
const [deleteModalOpen, setDeleteModalOpen] = useState(false)
const [sharingConfiguration, setSharingConfiguration] = React.useState("you");
const navigate = useNavigate();
const {themeMode} = useContext(Context);
const theme = getTheme(themeMode);
const parseUsecase = (subcase) => {
const srcdata = findSpecificApp(frameworkData, subcase.type)
const dstdata = findSpecificApp(frameworkData, subcase.last)
@@ -516,12 +520,12 @@ const AppModal = ({ open, onClose, app, globalUrl, getApps}) => {
border: "1px solid #494949",
minWidth: '440px',
fontFamily: theme?.typography?.fontFamily,
backgroundColor: "#212121",
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
'& .MuiDialogContent-root': {
backgroundColor: "#212121",
backgroundColor: theme.palette.DialogStyle.backgroundColor,
},
'& .MuiDialogTitle-root': {
backgroundColor: "#212121",
backgroundColor: theme.palette.DialogStyle.backgroundColor,
},
'& .MuiTypography-root': {
fontFamily: theme?.typography?.fontFamily,
@@ -553,7 +557,7 @@ const AppModal = ({ open, onClose, app, globalUrl, getApps}) => {
<IconButton
onClick={onClose}
sx={{
color: 'rgba(255, 255, 255, 0.7)',
color: theme.palette.textColor,
'&:hover': { bgcolor: 'rgba(255, 255, 255, 0.1)' }
}}
style={{
@@ -633,16 +637,14 @@ const AppModal = ({ open, onClose, app, globalUrl, getApps}) => {
>
<Button
variant="contained"
color="secondary"
sx={{
bgcolor: '#494949',
'&:hover': { bgcolor: '#494949' },
textTransform: 'none',
borderRadius: 1,
minWidth: '45px',
width: '45px',
height: '40px',
padding: 2,
color: "#fff",
fontFamily: theme?.typography?.fontFamily
}}
onClick={(event) => {
@@ -658,19 +660,16 @@ const AppModal = ({ open, onClose, app, globalUrl, getApps}) => {
{(userdata?.id === app?.owner)? (
<Tooltip title={"Delete app (confirm box will show)"}>
<Button
variant="outlined"
variant="contained"
component="label"
color="primary"
color="secondary"
sx={{
bgcolor: '#494949',
'&:hover': { bgcolor: '#494949', border: 'none' },
textTransform: 'none',
borderRadius: 1,
minWidth: '45px',
width: '45px',
height: '40px',
padding: 2,
color: "#fff",
fontFamily: theme?.typography?.fontFamily,
border: 'none'
}}
@@ -687,15 +686,13 @@ const AppModal = ({ open, onClose, app, globalUrl, getApps}) => {
{(canEditApp && app?.generated) && (
<Button
variant="contained"
color="secondary"
sx={{
bgcolor: "#494949",
'&:hover': { bgcolor: '#494949' },
textTransform: 'none',
borderRadius: 1,
py: 1,
px: 3,
height: '40px',
color: "#fff",
fontFamily: theme?.typography?.fontFamily
}}
startIcon={canEditApp ? <EditIcon /> : <ForkRightIcon />}
@@ -729,7 +726,7 @@ const AppModal = ({ open, onClose, app, globalUrl, getApps}) => {
<Typography
variant="body2"
sx={{
color: 'rgba(255, 255, 255, 0.7)',
color: theme.palette.textColor,
fontFamily: theme?.typography?.fontFamily,
fontSize: '14px'
}}
@@ -740,7 +737,7 @@ const AppModal = ({ open, onClose, app, globalUrl, getApps}) => {
<div style={{
flex: 1,
textAlign: "start",
borderLeft: "1px solid rgba(255, 255, 255, 0.12)",
borderLeft: theme.palette.defaultBorder,
paddingLeft: "10px",
height: "100%",
}}>
@@ -752,7 +749,7 @@ const AppModal = ({ open, onClose, app, globalUrl, getApps}) => {
}}>
{Array.isArray(app?.actions) ? app.actions.length : app?.actions}
</Typography>
<Typography variant="body2" sx={{ color: 'rgba(255, 255, 255, 0.7)' }}>
<Typography variant="body2" sx={{ color: theme.palette.textColor }}>
Actions
</Typography>
</div>
@@ -762,14 +759,14 @@ const AppModal = ({ open, onClose, app, globalUrl, getApps}) => {
paddingLeft: "10px",
paddingTop: "5px"
}}>
<div style={{ display: 'flex', alignItems: 'center', gap: '4px', marginBottom: "5px", fontFamily: theme?.typography?.fontFamily, fontSize: "14px", fontWeight: 600, color: 'white' }}>
<div style={{ display: 'flex', alignItems: 'center', gap: '4px', marginBottom: "5px", fontFamily: theme?.typography?.fontFamily, fontSize: "14px", fontWeight: 600, color: theme.palette.text.primary }}>
{
app?.collection ? (
<>
<CheckCircleIcon sx={{ color: '#4CAF50' }} />
<Typography variant="body1" sx={{
fontWeight: 500,
color: '#fff',
color: theme.palette.textColor,
marginTop: "1px",
fontFamily: theme?.typography?.fontFamily,
fontSize: "16px"
@@ -783,7 +780,7 @@ const AppModal = ({ open, onClose, app, globalUrl, getApps}) => {
fontSize: "16px",
fontWeight: 500,
marginTop: "1px",
color: 'rgba(255, 255, 255, 0.7)',
color: theme.palette.textColor,
fontFamily: theme?.typography?.fontFamily
}}>
No collection yet
@@ -832,7 +829,7 @@ const AppModal = ({ open, onClose, app, globalUrl, getApps}) => {
)}
<Box sx={{
bgcolor: '#2F2F2F',
bgcolor: themeMode === "dark" ? "#1E1E1E" : "#F5F5F5",
p: 2,
borderRadius: 2,
display: 'flex',
@@ -895,16 +892,14 @@ const AppModal = ({ open, onClose, app, globalUrl, getApps}) => {
<div style={{ display: "flex", justifyContent: "center", fontFamily: theme?.typography?.fontFamily }}>
<Button
variant="contained"
color="primary"
sx={{
bgcolor: '#FF8544',
'&:hover': { bgcolor: '#FF8544' },
textTransform: 'none',
borderRadius: "4px",
py: 1,
px: 7,
fontSize: "14px",
letterSpacing: "0.5px",
color: "black",
fontFamily: theme?.typography?.fontFamily,
minWidth: '200px'
}}
+11 -8
View File
@@ -1,5 +1,5 @@
import React, { useState, useEffect, useRef } from "react";
import theme from '../theme.jsx';
import React, { useState, useEffect, useRef, useContext } from "react";
import {getTheme} from '../theme.jsx';
import ReactGA from 'react-ga4';
import { useNavigate, Link } from 'react-router-dom';
import { isMobile } from 'react-device-detect';
@@ -36,9 +36,12 @@ import {
IconButton,
} from '@mui/material';
import { Context } from "../context/ContextApi.jsx";
const AppSearchButtons = (props) => {
const { userdata, globalUrl, appFramework, moreButton, finishedApps, appType, totalApps, index, onNodeSelect, setDiscoveryData, appName, AppImage, setDefaultSearch, discoveryData, checkLogin, setMissing, getAppFramework, } = props
const { themeMode } = useContext(Context);
const theme = getTheme(themeMode);
const ref = useRef()
let navigate = useNavigate();
@@ -195,13 +198,13 @@ const AppSearchButtons = (props) => {
zIndex: 100,
borderRadius: 6,
border: "1px solid var(--Container-Stroke, #494949)",
background: "var(--Container, #212121)",
background: theme.palette.platformColor,
boxShadow: "8px 8px 32px 24px rgba(0, 0, 0, 0.16)",
}}
>
<div style={{ display: "flex" }}>
<div style={{ display: "flex", textAlign: "center", textTransform: "capitalize" }}>
<Typography style={{ padding: 16, color: "#FFFFFF", textTransform: "capitalize" }}> {discoveryData} </Typography>
<Typography color="textPrimary" style={{ padding: 16, textTransform: "capitalize" }}> {discoveryData} </Typography>
</div>
<div style={{ display: "flex" }}>
<Tooltip
@@ -288,17 +291,17 @@ const AppSearchButtons = (props) => {
</div>
) : null}
<div style={{
display: "flex", height: 70, border: isHover ? "1px solid #f85a3e" : "var(--Container, #212121)", borderRadius: 8, background: isHover ? "var(--Container, #212121)" : "var(--Container, #212121)",
display: "flex", height: 70, border: isHover ? "1px solid #f85a3e" : "var(--Container, #212121)", borderRadius: 4, background: isHover ? "var(--Container, #212121)" : "var(--Container, #212121)",
alignItems: "center", justifyContent: "center",
}}
>
<Button
fullWidth
color="secondary"
style={{
height: "100%",
width: "100%",
display: "grid"
display: "grid",
backgroundColor: themeMode === "dark" ? "#212121" : "#F5F5F5",
}}
onClick={(event) => {
if (onNodeSelect !== undefined) {
+8 -3
View File
@@ -1,7 +1,7 @@
import React, { useState, useEffect } from 'react';
import React, { useState, useEffect, useContext } from 'react';
import classNames from "classnames";
import theme from '../theme.jsx';
import {getTheme} from '../theme.jsx';
import {
Tooltip,
@@ -14,6 +14,7 @@ import {
Chip,
Checkbox,
} from "@mui/material";
import { Context } from '../context/ContextApi.jsx';
import {
BarChart,
@@ -73,6 +74,8 @@ const inputdata = {
const LineChartWrapper = ({keys, inputname, height, width}) => {
const [hovered, setHovered] = useState("");
const {themeMode} = useContext(Context);
const theme = getTheme(themeMode)
//console.log("Date: ", new Date("2019-11-14T08:00:00.000Z"))
//var inputdata = keys.data
@@ -166,6 +169,8 @@ const AppStats = (defaultprops) => {
const [searches, setSearches] = useState([]);
const [clickData, setClickData] = useState(undefined);
const [conversionData, setConversionData] = useState(undefined);
const {themeMode} = useContext(Context);
const theme = getTheme(themeMode)
const handleDataSetting = (inputdata, grouping) => {
var newlist = []
@@ -292,7 +297,7 @@ const AppStats = (defaultprops) => {
textAlign: "center",
padding: 40,
margin: 5,
backgroundColor: theme.palette.inputColor,
backgroundColor: theme.palette.surfaceColor,
}
console.log("Widget: ", widgetData)
+8 -7
View File
@@ -1,10 +1,10 @@
import React, { useState, useEffect } from 'react';
import React, { useState, useEffect, useContext } from 'react';
import ReactGA from 'react-ga4';
import theme from '../theme.jsx';
import {getTheme} from '../theme.jsx';
import {Link} from 'react-router-dom';
import { Search as SearchIcon, CloudQueue as CloudQueueIcon, Code as CodeIcon } from '@mui/icons-material';
import { toast } from 'react-toastify';
import { Context } from '../context/ContextApi.jsx';
//import algoliasearch from 'algoliasearch/lite';
import algoliasearch from 'algoliasearch';
import { InstantSearch, connectSearchBox, connectHits } from 'react-instantsearch-dom';
@@ -23,7 +23,8 @@ import aa from 'search-insights'
const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240")
const Appsearch = props => {
const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs, newSelectedApp, setNewSelectedApp, defaultSearch, showSearch, ConfiguredHits, userdata, cy, isCreatorPage, actionImageList, setActionImageList, setUserSpecialzedApp } = props
const { themeMode } = useContext(Context)
const theme = getTheme(themeMode)
const isCloud = (window.location.host === "localhost:3002" || window.location.host === "shuffler.io") ? true : (process.env.IS_SSR === "true");
const rowHandler = maxRows === undefined || maxRows === null ? 50 : maxRows
const xs = parsedXs === undefined || parsedXs === null ? 12 : parsedXs
@@ -54,10 +55,10 @@ const Appsearch = props => {
autoComplete="off"
autocomplete="off"
fullWidth
style={{backgroundColor: "#2F2F2F", borderRadius: borderRadius, width: "100%",}}
style={{backgroundColor: theme.palette.textFieldStyle.backgroundColor, borderRadius: borderRadius, width: "100%",}}
InputProps={{
style:{
color: "white",
color: theme.palette.textFieldStyle.color,
fontSize: "1em",
height: 50,
},
@@ -93,7 +94,7 @@ const Appsearch = props => {
<Grid container spacing={0} style={{border: "1px solid rgba(255,255,255,0.2)", maxHeight: 250, minHeight: 250, overflowY: "auto", overflowX: "hidden", }}>
{hits.map((data, index) => {
const paperStyle = {
backgroundColor: index === mouseHoverIndex ? "rgba(255,255,255,0.8)" : "#2F2F2F",
backgroundColor: index === mouseHoverIndex ? theme.palette.hoverColor : theme.palette.textFieldStyle.backgroundColor,
color: index === mouseHoverIndex ? theme.palette.inputColor : "rgba(255,255,255,0.8)",
// border: newSelectedApp.objectID !== data.objectID ? `1px solid rgba(255,255,255,0.2)` : "2px solid #f86a3e",
textAlign: "left",
+591 -58
View File
@@ -1,8 +1,8 @@
import React, { useState, useEffect, useContext, memo, useMemo } from "react";
import React, { useState, useEffect, memo, useMemo, useContext } from "react";
import ReactGA from 'react-ga4';
import theme from "../theme.jsx";
import { getTheme } from "../theme.jsx";
import countries from "../components/Countries.jsx";
import {
Box,
Paper,
@@ -26,7 +26,10 @@ import {
DialogContentText,
DialogActions,
LinearProgress,
Slider
Slider,
Tabs,
Tab,
CircularProgress,
} from "@mui/material";
import { useNavigate, Link, json } from "react-router-dom";
@@ -43,6 +46,8 @@ import {
Cloud,
CheckCircle,
Padding,
Edit,
Search as SearchIcon
} from "@mui/icons-material";
//import { useAlert
@@ -52,11 +57,14 @@ import { handlePayasyougo } from "../views/HandlePaymentNew.jsx"
import DeleteIcon from '@mui/icons-material/Delete';
import { Context } from "../context/ContextApi.jsx";
import LicencePopup from "./LicencePopup.jsx";
import { DataGrid } from "@mui/x-data-grid";
const Billing = memo((props) => {
const { globalUrl, userdata, serverside, billingInfo, stripeKey,isLoaded, selectedOrganization, handleGetOrg, clickedFromOrgTab, removeCookie} = props;
//const alert = useAlert();
let navigate = useNavigate();
const { themeMode, brandColor,supportEmail } = useContext(Context);
const theme = getTheme(themeMode, brandColor);
const [isLoggedIn, setIsLoggedIn] = useState(false)
const [selectedDealModalOpen, setSelectedDealModalOpen] = React.useState(false);
const [dealList, setDealList] = React.useState([]);
@@ -78,6 +86,9 @@ const Billing = memo((props) => {
const [deleteAlertIndex, setDeleteAlertIndex] = useState(-1);
const [deleteAlertVerification, setDeleteAlertVerification] = useState(false);
const [isScale, setIsScale] = useState(false);
const [currentTab, setCurrentTab] = useState(0)
const [allChildOrgs, setAllChildOrgs] = useState([])
const [allChildOrgsStats, setAllChildOrgsStats] = useState([])
useEffect(() => {
if (userdata.app_execution_limit !== undefined && userdata.app_execution_usage !== undefined) {
@@ -85,6 +96,7 @@ const Billing = memo((props) => {
setCurrentAppRunsInPercentage(Math.round(percentage));
setCurrentAppRunsInNumber(userdata.app_execution_limit - userdata.app_execution_usage);
}
if (userdata?.id?.length > 0 && isLoggedIn === false){
setIsLoggedIn(true)
}
@@ -92,6 +104,17 @@ const Billing = memo((props) => {
const [BillingEmail, setBillingEmail] = useState(selectedOrganization?.Billing?.Email);
useEffect(() => {
const urlIncludesProfessionalServices = window.location.href.includes("professional-services");
if (props?.isCloud && urlIncludesProfessionalServices) {
const professionalServicesSection =
document.getElementById("professional-services");
if (professionalServicesSection) {
professionalServicesSection.scrollIntoView({ behavior: "smooth" });
}
}
}, []);
useEffect(() => {
if (BillingEmail !== selectedOrganization?.Billing?.Email) {
setBillingEmail(selectedOrganization?.Billing?.Email);
@@ -337,6 +360,7 @@ const Billing = memo((props) => {
const [tosChecked, setTosChecked] = React.useState(subscription.eula_signed)
const [hovered, setHovered] = React.useState(false)
const [newBillingEmail, setNewBillingEmail] = useState('');
const {supportEmail} = useContext(Context);
var top_text = "Base Cloud Access"
if (subscription.limit === undefined && subscription.level === undefined || subscription.level === null || subscription.level === 0) {
@@ -537,7 +561,7 @@ const Billing = memo((props) => {
Accept
</Typography>
<Typography variant="body2" style={{ display: "inline-block", marginLeft: 10, }} color="textSecondary">
By clicking the accept button, you are signing the document, electronically agreeing that it has the same legal validity and effects as a handwritten signature, and that you have the competent authority to represent and sign on behalf an entity. Need support or have questions? Contact us at support@shuffler.io.
By clicking the accept button, you are signing the document, electronically agreeing that it has the same legal validity and effects as a handwritten signature, and that you have the competent authority to represent and sign on behalf an entity. Need support or have questions? Contact us at {supportEmail}
</Typography>
<div style={{ display: "flex", marginTop: 25, }}>
@@ -1066,12 +1090,12 @@ const Billing = memo((props) => {
padding: 20,
// maxWidth: 400,
width: "100%",
height: 480,
backgroundColor: hovered ? "#2b2b2b" : "#1e1e1e",
height: 500,
backgroundColor: hovered ? theme.palette.cardHoverColor : theme.palette.cardBackgroundColor,
borderRadius: theme.palette?.borderRadius * 2,
border: "1px solid rgba(255,255,255,0.3)",
marginRight: 10,
marginTop: 15,
marginTop: 15,
}}
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}>
@@ -1138,12 +1162,12 @@ const Billing = memo((props) => {
Features
</Typography>
<ul>
<li>
<li style={{color : theme.palette.text.primary}}>
<Typography variant="body2" color="textPrimary" style={{}}>
Build custom apps, integrations, and worklows for your specific use cases or applications
</Typography>
</li>
<li>
<li style={{color : theme.palette.text.primary}}>
<Typography variant="body2" color="textPrimary" style={{}}>
Help solve / debug / update / add features and capabilities of the platform
</Typography>
@@ -1154,15 +1178,13 @@ const Billing = memo((props) => {
<Button
fullWidth
disabled={false}
variant="outlined"
variant="contained"
color="primary"
style={{
marginTop: userdata.support ? 0 : 10,
marginTop: userdata.support ? 3 : 15,
borderRadius: 4,
height: 40,
fontSize: 16,
color: "#1A1A1A",
backgroundColor: "#FF8544",
textTransform: 'none',
}}
onClick={() => {
@@ -1228,7 +1250,7 @@ const Billing = memo((props) => {
variant="outlined"
color="primary"
style={{
marginTop: userdata.support ? 5 : 10,
marginTop: userdata.support ? 10 : 15,
borderRadius: 4,
height: 40,
fontSize: 16,
@@ -1354,7 +1376,7 @@ const Billing = memo((props) => {
toast.success("Your request for private training has been submitted successfully. We will get back to you soon.")
setOpenPrivateTraining(false)
} else {
toast.error("Failed sending request for private training. Please try again later or contact support@shuffler.io for help.")
toast.error(`Failed sending request for private training. Please try again later or contact support@shuffler.io for help.`)
}
})
}
@@ -1363,10 +1385,10 @@ const Billing = memo((props) => {
<div
style={{
padding: 20,
height: 480,
height: 500,
// maxWidth: 400,
width: "100%",
backgroundColor: hovered ? "#2b2b2b" : "#1e1e1e",
backgroundColor: hovered ? theme.palette.cardHoverColor : theme.palette.cardBackgroundColor,
borderRadius: theme.palette?.borderRadius * 2,
border: "1px solid rgba(255,255,255,0.3)",
marginRight: 10,
@@ -1387,12 +1409,12 @@ const Billing = memo((props) => {
Public Training
</Typography>
<ul>
<li>
<li style={{color : theme.palette.text.primary}}>
<Typography variant="body2" color="textPrimary">
Public course on Automation for Security Professionals
</Typography>
</li>
<li>
<li style={{color : theme.palette.text.primary}}>
<Typography variant="body2" color="textPrimary">
Covers Shuffle Platform, Apps, Workflows, Usecases, JSON, Liquid Formatting, and more.
</Typography>
@@ -1402,12 +1424,12 @@ const Billing = memo((props) => {
Private Training
</Typography>
<ul>
<li>
<li style={{color : theme.palette.text.primary}}>
<Typography variant="body2" color="textPrimary">
Everything from Public Training
</Typography>
</li>
<li>
<li style={{color : theme.palette.text.primary}}>
<Typography variant="body2" color="textPrimary" >
Customized for your teams usecases, date and time, location, and more.
</Typography>
@@ -1425,9 +1447,7 @@ const Billing = memo((props) => {
borderRadius: 4,
height: 40,
fontSize: 16,
color: "#1A1A1A",
textTransform: 'none',
backgroundColor: "#FF8544",
width: "100%",
}}
onClick={() => {
@@ -1971,16 +1991,18 @@ const Billing = memo((props) => {
<div style={{ width: "100%",}}>
{addDealModal}
{clickedFromOrgTab ?
<Typography style={{fontSize: 24, fontWeight: "bold", marginBottom: 8, marginTop: 0, color: "#ffffff" }}>Billing & Licensing</Typography> :
<Typography variant="h5" style={{fontSize: 24, fontWeight: 500, marginBottom: 8, marginTop: 0, }}>Billing & Licensing</Typography> :
<Typography variant="h4" style={{ marginTop: 20, marginBottom: 10 }}>
Billing & Licensing
</Typography>}
{clickedFromOrgTab ?
<span style={{ color: "#9E9E9E", fontSize: 16 }}>{isCloud ?
{userdata?.org_status?.includes("integration_partner") && userdata?.org_status?.includes("sub_org") ? null :
<>
{clickedFromOrgTab ?
<Typography variant="body2" color="textSecondary" style={{ fontSize: 16 }}>{isCloud ?
"Get more out of Shuffle by adding your credit card, such as no App Run limitations, and priority support from our team. We use Stripe to manage subscriptions and do not store any of your billing information. You can manage your subscription and billing information below."
:
"Shuffle is an Open Source automation platform, and no license is required. We do however offer a Scale license with HA guarantees, along with support hours. By buying a license on https://shuffler.io, you can get access to the license immediately, and if Cloud Syncronisation is enabled, the UI in your local instance will also update."
}</span> :
}</Typography> :
<Typography variant="body1" color="textSecondary" style={{ marginTop: 0, marginBottom: 10, fontSize: 16 }}>
{isCloud ?
"Get more out of Shuffle by adding your credit card, such as no App Run limitations, and priority support from our team. We use Stripe to manage subscriptions and do not store any of your billing information. You can manage your subscription and billing information below."
@@ -1988,9 +2010,10 @@ const Billing = memo((props) => {
"Shuffle is an Open Source automation platform, and no license is required. We do however offer a Scale license with HA guarantees, along with support hours. By buying a license on https://shuffler.io, you can get access to the license immediately, and if Cloud Syncronisation is enabled, the UI in your local instance will also update."
}
</Typography>}
</> }
{userdata.support === true ?
<div style={{ marginBottom: 10, marginTop: clickedFromOrgTab ? 16 : null, color: clickedFromOrgTab ? "#F1F1F1" : null }}>
<Typography style={{ marginBottom: 10, marginTop: clickedFromOrgTab ? 16 : null, color: clickedFromOrgTab ? theme.palette.text.primary : null }}>
For sales: Create&nbsp;
<a href={"https://docs.google.com/document/d/1N-ZJNn8lWaqiXITrqYcnTt53oXGLNYFEzc5PU-tdAps/copy"} target="_blank" rel="noopener noreferrer" style={{ textDecoration: "none", color: "#FF8444" }}>
EU contract
@@ -2007,7 +2030,7 @@ const Billing = memo((props) => {
<a href={"https://github.com/Shuffle/Shuffle-docs/tree/master/handbook/Sales"} target="_blank" rel="noopener noreferrer" style={{ textDecoration: "none", color: "#FF8444" }}>
Sales Process (old)
</a>
</div>
</Typography>
:
null
}
@@ -2015,11 +2038,11 @@ const Billing = memo((props) => {
{isChildOrg ?
<Typography variant="h6" style={{ marginBottom: 50, }}>
Licensing 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 {supportEmail} if you have questions about this.
</Typography>
: null}
<div style={{ display: "flex", width: clickedFromOrgTab ? "100%" : "auto", overflowX: 'auto', overflowY: 'hidden', scrollbarWidth: 'thin', scrollbarColor: '#494949 #2f2f2f', height: isChildOrg ? 0 : "100%", marginTop: 20}} >
<div style={{ display: "flex", width: clickedFromOrgTab ? "100%" : "auto", overflowX: 'auto', overflowY: 'hidden', scrollbarWidth: 'thin', scrollbarColor: theme.palette.scrollbarColor, height: isChildOrg ? 0 : "100%", marginTop: 20}} >
<div style={{ display: "flex", flexDirection: "column", width: "100%", }}>
{/* {isCloud &&
selectedOrganization.subscriptions !== undefined &&
@@ -2354,13 +2377,13 @@ const Billing = memo((props) => {
</div>
) : null*/}
{!isChildOrg && isCloud && (
<div style={{ display: 'flex', flexDirection: 'column', marginTop: 50, maxWidth: 860 }}>
<Typography style={{ marginBottom: 5, fontSize: 24, fontWeight: "bold" }}>
<div style={{ display: 'flex', flexDirection: 'column', marginTop: 50, maxWidth: 860 }} id="professional-services">
<Typography variant="h6" style={{ marginBottom: 5, fontSize: 24, fontWeight: 500 }}>
Professional Services
</Typography>
<Typography color="textSecondary" style={{fontSize: 16,}}>
We offer priority support through consultations and training to help you make the most of our product. If you have any questions, please reach out to us at support@shuffler.io.
</Typography>
<Typography variant="body2" 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 {supportEmail}.
</Typography>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
<div style={{ display: 'flex', flexDirection: 'row', marginTop: 5, }}>
{billingInfo.subscription !== undefined && billingInfo.subscription !== null ? (
isChildOrg ? null : (
@@ -2426,14 +2449,16 @@ const Billing = memo((props) => {
<TextField
style={{
marginTop: 10,
backgroundColor: "#212121",
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
color: theme.palette.textFieldStyle.color,
borderRadius: theme.palette.textFieldStyle.borderRadius,
width: 250,
height: 50,
}}
InputProps={{
style: {
height: 50,
color: 'white',
color: theme.palette.textFieldStyle.color,
},
endAdornment: '%',
}}
@@ -2456,14 +2481,16 @@ const Billing = memo((props) => {
style={{
marginTop: 10,
height: 50,
backgroundColor: "#212121",
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
color: theme.palette.textFieldStyle.color,
width: 250,
marginLeft: 15,
marginLeft: 15,
borderRadius: theme.palette.textFieldStyle.borderRadius,
}}
InputProps={{
style: {
height: 50,
color: 'white',
color: theme.palette.textFieldStyle.color,
},
}}
InputLabelProps={{
@@ -2505,7 +2532,7 @@ const Billing = memo((props) => {
setDeleteAlertIndex(index);
}}
>
<DeleteIcon sx={{ color: theme.palette.secondary.main }} />
<DeleteIcon sx={{ color: themeMode === "dark" ? "rgba(255,255,255,0.7)" : "#666666" }} />
</Button>
)}
<Dialog
@@ -2525,8 +2552,9 @@ const Billing = memo((props) => {
Cancel
</Button>
<Button
style={{ textTransform: 'none', fontSize: 16 }}
color="secondary"
style={{ textTransform: 'none', fontSize: 16 }}
variant="outlined"
color="primary"
onClick={() => {
handleDeleteAlertThreshold(deleteAlertIndex);
setDeleteAlertVerification(false);
@@ -2569,13 +2597,64 @@ const Billing = memo((props) => {
Utilization & Stats
</Typography>
</div>
<BillingStats
isCloud={isCloud}
clickedFromOrgTab={clickedFromOrgTab}
globalUrl={globalUrl}
selectedOrganization={selectedOrganization}
userdata={userdata}
/>
{isChildOrg ? (
<BillingStats
isCloud={isCloud}
clickedFromOrgTab={clickedFromOrgTab}
globalUrl={globalUrl}
selectedOrganization={selectedOrganization}
userdata={userdata}
/>
): (
<>
<Tabs
value={currentTab}
onChange={(event, newValue) => setCurrentTab(newValue)}
style={{ marginTop: 20 }}
TabIndicatorProps={{
style: {
height: 3,
backgroundColor: theme.palette.primary.main,
marginLeft: 12,
marginRight: 12,
}
}}
>
<Tab
label="Parent Organization"
style={{ textTransform: 'none', fontSize: 16, minWidth: 'auto', paddingLeft: 12, paddingRight: 12 }}
/>
<Tab
label="Child Organization"
style={{ textTransform: 'none', fontSize: 16, minWidth: 'auto', paddingLeft: 12, paddingRight: 12 }}
/>
</Tabs>
{currentTab === 0 ? (
<div style={{ marginTop: 30,}}>
<BillingStats
isCloud={isCloud}
clickedFromOrgTab={clickedFromOrgTab}
globalUrl={globalUrl}
selectedOrganization={selectedOrganization}
userdata={userdata}
/>
</div>
): (
<BillingStatsChildOrg
isCloud={isCloud}
clickedFromOrgTab={clickedFromOrgTab}
globalUrl={globalUrl}
selectedOrganization={selectedOrganization}
userdata={userdata}
allChildOrgs={allChildOrgs}
setAllChildOrgs={setAllChildOrgs}
allChildOrgsStats={allChildOrgsStats}
setAllChildOrgsStats={setAllChildOrgsStats}
/>
)}
</>
)}
</div>
</Wrapper>
)
@@ -2583,19 +2662,473 @@ const Billing = memo((props) => {
export default memo(Billing);
const PaddingWrapper = memo(({ clickedFromOrgTab, children }) => {
const BillingStatsChildOrg = memo(({ userdata, globalUrl, selectedOrganization, allChildOrgs, setAllChildOrgs, allChildOrgsStats, setAllChildOrgsStats }) => {
const [subOrgStats, setSubOrgStats] = useState([]);
const [subOrgs, setSubOrgs] = useState([]);
const [subOrgStatsRows, setSubOrgStatsRows] = useState([]);
const [subOrgStatsColumns, setSubOrgStatsColumns] = useState([]);
const [allOrgLoaded, setAllOrgLoaded] = useState(false);
const [allOrgStatsLoaded, setAllOrgStatsLoaded] = useState(false);
const [page, setPage] = useState(0);
const [rowsPerPage, setRowsPerPage] = useState(10);
const [open, setOpen] = useState(false);
const [editing, setEditing] = useState("")
const [editingOrgId, setEditingOrgId] = useState("")
const [limit, setLimit] = useState("")
const [tableCreated, setTableCreated] = useState(false)
const [searchQuery, setSearchQuery] = useState("");
const [filteredRows, setFilteredRows] = useState([]);
const { themeMode, brandColor, supportEmail } = useContext(Context);
const theme = getTheme(themeMode, brandColor);
// Handle page change
const handleChangePage = (event, newPage) => {
setPage(newPage);
};
const handleChangeRowsPerPage = (event) => {
setRowsPerPage(parseInt(event.target.value, 10));
setPage(0);
};
const HanldeLoadStats = async () => {
const childOrgs = selectedOrganization.child_orgs;
if (allChildOrgsStats.length > 0){
setSubOrgStats(allChildOrgsStats)
setAllOrgStatsLoaded(true)
if (allChildOrgsStats.length > 0 && allChildOrgs.length > 0 && subOrgStatsRows.length === 0 && subOrgStatsColumns.length === 0) {
HandleCreateTable(allChildOrgsStats, allChildOrgs)
}
return
}
const promises = childOrgs.map((org) => {
// get org stats base on region url
const baseUrl = org?.region_url?.length > 0 && !window?.location?.origin?.includes("localhost") ? org?.region_url : globalUrl;
const url = `${baseUrl}/api/v1/orgs/${org.id}/stats`;
return fetch(url, {
method: "GET",
credentials: "include",
headers: {
"Content-Type": "application/json",
},
}).then((res) => res.json());
});
try {
const responses = await Promise.all(promises);
setSubOrgStats(responses);
setAllChildOrgsStats(responses);
setAllOrgStatsLoaded(true);
} catch (error) {
console.error("Error loading stats:", error);
}
};
const HandleGetSuborg = async () => {
const childOrgs = selectedOrganization.child_orgs
if (allChildOrgs.length > 0){
setSubOrgs(allChildOrgs)
setAllOrgLoaded(true)
if (allChildOrgsStats.length > 0 && allChildOrgs.length > 0 && subOrgStatsRows.length === 0 && subOrgStatsColumns.length === 0) {
HandleCreateTable(allChildOrgsStats, allChildOrgs)
}
return
}
const promises = childOrgs.map((org) => {
const baseUrl = org?.region_url?.length > 0 && !window?.location?.origin?.includes("localhost") ? org?.region_url : globalUrl;
const url = `${baseUrl}/api/v1/orgs/${org.id}`;
return fetch(url, {
method: "GET",
credentials: "include",
headers: {
"Content-Type": "application/json",
},
}).then((res) => res.json());
});
try {
const responses = await Promise.all(promises);
setSubOrgs(responses);
setAllChildOrgs(responses);
setAllOrgLoaded(true);
} catch (error) {
console.error("Error loading suborgs:", error);
}
};
useEffect(() => {
if (subOrgStats.length === 0 && selectedOrganization && selectedOrganization?.child_orgs?.length > 0) {
HanldeLoadStats()
}
if (subOrgs.length === 0 && selectedOrganization && selectedOrganization?.child_orgs?.length > 0) {
HandleGetSuborg()
}
}, [selectedOrganization?.child_orgs]);
useEffect(() => {
if (allOrgLoaded && allOrgStatsLoaded && !tableCreated) {
HandleCreateTable(subOrgStats, subOrgs)
}
}
, [allOrgLoaded, allOrgStatsLoaded, tableCreated])
const HandleCreateTable = (subOrgStats, subOrgs) => {
if (subOrgStats.length === 0 || subOrgs.length === 0) return;
// check whether all of the suborg.success is false
const allSubOrgStatsSuccess = subOrgStats.every((stat) => stat.success === false);
const allSubOrgsSuccess = subOrgs.every((org) => org.success === false);
if (allSubOrgStatsSuccess || allSubOrgsSuccess) {
setSubOrgStats([])
setSubOrgs([])
setTableCreated(true)
return
}
const rows = subOrgStats.map((stat, index) => {
const subOrg = subOrgs[index]
if (!subOrg) return null;
return {
id: index,
name: subOrg.name,
orgId: subOrg.id,
limit: subOrg?.sync_features?.app_executions?.limit || "N/A",
usage: stat?.monthly_app_executions || "N/A",
workflows_usage: stat?.total_workflow_executions || "N/A",
workflow_usage_limit: subOrg?.sync_features?.workflow_executions?.limit || "N/A",
}
})
setSubOrgStatsRows(rows)
const columns = [
{ field: "id", headerName: "ID", width: 100 },
{ field: "name", headerName: "Name", width: 200 },
{ field: "usage", headerName: "App Execution Usage", width: 200 },
{
field: "limit", headerName: "App Execution Limit", width: 200, renderCell: (params) => {
return (
<>
<Typography style={{ fontSize: 16 }}>
{params.value}
<IconButton
style={{ color: theme.palette.primary.main }}
onClick={() => {
setOpen(true)
setEditingOrgId(params.row.orgId)
setEditing("app_executions")
if (params.value === "N/A") {
setLimit("")
} else {
setLimit(params.value)
}
}}
>
<Edit/>
</IconButton>
</Typography>
</>
)
}
},
{ field: "workflows_usage", headerName: "Workflow Execution Usage", width: 200 },
{ field: "workflow_usage_limit", headerName: "Workflow Execution Limit", width: 200, renderCell: (params) => {
return (
<>
<Typography style={{ fontSize: 16 }}>
{params.value}
</Typography>
<IconButton
style={{ color: theme.palette.primary.main }}
onClick={() => {
setOpen(true)
setEditingOrgId(params.row.orgId)
setEditing("workflow_executions")
if (params.value === "N/A") {
setLimit("")
} else {
setLimit(params.value)
}
}}
>
<Edit/>
</IconButton>
</>
)}
},
]
setSubOrgStatsColumns(columns)
if (allOrgLoaded && allOrgStatsLoaded && !tableCreated) {
setTableCreated(true)
}
}
const HandleEditLimit = (orgId, editing, limit) => {
// change limit as number if string
if (typeof limit === "string") {
limit = parseInt(limit, 10)
}
if (isNaN(limit)) {
toast.error("Please enter a valid number")
return
}
if (selectedOrganization.sync_features.app_executions.limit <= 10000) {
toast.error("Insufficient app execution limit to increase child org limit")
return
}
// check whether limit is greater than than parent org limit
if (editing === "app_executions" && limit > selectedOrganization.sync_features.app_executions.limit && !userdata.support) {
toast.error("App execution limit cannot be greater than parent org limit")
return
}
if (editing === "workflow_executions" && limit > selectedOrganization.sync_features.workflow_executions.limit && !userdata.support) {
toast.error("Workflow execution limit cannot be greater than parent org limit")
return
}
// find the org in the subOrgs array
const orgIndex = subOrgs.findIndex((org) => org.id === orgId)
if (orgIndex === -1) {
toast.error("Organization not found")
return
}
const org = subOrgs[orgIndex]
org.sync_features[editing].limit = limit
org.sync_features.editing = true
const sync_features = org.sync_features
const data = {
org_id: orgId,
sync_features: sync_features,
}
const url = `${globalUrl}/api/v1/orgs/${orgId}`;
fetch(url, {
method: "POST",
credentials: "include",
crossDomain: true,
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data),
}).then((response) =>
response.json().then((responseJson) => {
if (responseJson["success"] === false) {
toast("Failed updating org: ", responseJson.reason);
} else {
toast("Successfully change suborg limit!");
if (editing === "app_executions") {
setSubOrgStatsRows((prevRows) => {
const newRows = [...prevRows];
newRows[orgIndex].limit = limit;
return newRows;
});
}else if (editing === "workflow_executions") {
setSubOrgStatsRows((prevRows) => {
const newRows = [...prevRows];
newRows[orgIndex].workflow_usage_limit = limit;
return newRows;
});
}
}
})
)
.catch((error) => {
toast("Err: " + error.toString());
});
}
const HandleClosePopUP = () => {
setOpen(false)
setEditing("")
setEditingOrgId("")
setLimit("")
}
return (
<div style={{ display: "flex", flexDirection: "column", marginTop: 30, minHeight: 300, marginBottom: 200, }}>
{open && (
<IncreaseLimitPopUp open={open} onClose={HandleClosePopUP} limit={limit} HandleEditLimit={HandleEditLimit} setLimit={setLimit} editing={editing} setEditing={setEditing} editingOrgId={editingOrgId} setEditingOrgId={setEditingOrgId}/>
)}
<Typography style={{ marginBottom: 5, fontSize: 24, fontWeight: "bold" }}>
Child Organizations
</Typography>
<div style={{display: 'flex', flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginBottom: 10}}>
<Typography color="textSecondary" style={{ marginTop: 10, fontSize: 16 }}>
View and configure execution limits for child organizations. Click the edit icon to modify app and workflow execution limits.
</Typography>
</div>
{tableCreated ? (
subOrgStatsRows.length > 0 && subOrgStatsColumns.length > 0 ? (
<>
<TextField
style={{ marginBottom: 10, width: 500, marginTop: 20 }}
variant="outlined"
placeholder="Search organizations by name or ID"
value={searchQuery}
fullWidth
InputProps={{
style: {
fontSize: "1em",
height: 51,
width: 693,
borderRadius: 4,
},
startAdornment: (
<InputAdornment position="start">
<SearchIcon style={{ marginLeft: 5}} />
</InputAdornment>
),
}}
onChange={(e) => {
setSearchQuery(e.target.value.toLowerCase());
const filtered = subOrgStatsRows.filter((row) =>
row.name.toLowerCase().includes(e.target.value.toLowerCase().trim()) ||
row.orgId.toLowerCase().includes(e.target.value.toLowerCase().trim())
);
setFilteredRows(filtered);
}}
/>
<DataGrid
rows={searchQuery ? filteredRows : subOrgStatsRows}
columns={subOrgStatsColumns}
pageSize={rowsPerPage}
rowsPerPageOptions={[5, 10, 25, 50]}
onPageChange={handleChangePage}
onPageSizeChange={handleChangeRowsPerPage}
autoHeight
style={{ height: "300px", width: "100%", backgroundColor: theme.palette.platformColor, color: theme.palette.text.primary }}
/>
</>
) : (
<Typography variant="h6" color="secondary" style={{ margin: "auto",}}>
{selectedOrganization.child_orgs.length === 0 ? "No child organizations exist." : "Unable to load child organization stats. Statistics may not be initialized yet." }
</Typography>
)
) : (
<div style={{ display: "flex", justifyContent: "center", alignItems: "center", height: "100%", margin: 'auto'}}>
<CircularProgress style={{ color: "#FF8444" }} />
</div>
)}
</div>
);
});
const IncreaseLimitPopUp = memo(({ open, onClose, limit, setLimit, HandleEditLimit, editingOrgId, editing}) => {
const [currentLimit, setCurrentLimit] = useState(limit)
const { themeMode, brandColor } = useContext(Context);
const theme = getTheme(themeMode, brandColor);
return(
<Dialog open={open} onClose={onClose}
PaperProps={{
sx: {
borderRadius: theme?.palette?.DialogStyle?.borderRadius,
border: theme?.palette?.DialogStyle?.border,
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,
},
}
}}
>
<DialogTitle style={{ fontSize: 24, fontWeight: "bold" }}>
Increase {editing.replaceAll("_", " ").replace(/\b\w/g, c => c.toUpperCase())} Limit
</DialogTitle>
<DialogContent>
<TextField
value={currentLimit}
onChange={(e) => setCurrentLimit(e.target.value)}
label={`${editing.replaceAll("_", " ").replace(/\b\w/g, c => c.toUpperCase())} Limit`}
type="string"
variant="outlined"
fullWidth
InputProps={{
style: {
color: theme.palette.text.primary,
},
}}
InputLabelProps={{
style: {
color: theme.palette.text.primary,
},
}}
margin="normal"
onKeyUp={(e) => {
if (e.key === "Enter") {
HandleEditLimit(editingOrgId, editing, currentLimit)
setLimit(currentLimit)
onClose()
}}
}
></TextField>
</DialogContent>
<DialogActions>
<Button onClick={onClose} style={{ borderRadius: "2px", textTransform: 'none', fontSize:16, marginRight: 5, color: theme.palette.primary.main }}>
Cancel
</Button>
<Button variant="contained" color="primary" onClick={() => {
HandleEditLimit(editingOrgId, editing, currentLimit)
setLimit(currentLimit)
onClose()
}} style={{ borderRadius: "2px", textTransform: 'none', fontSize:16, marginRight: 10 }}>
Save
</Button>
</DialogActions>
</Dialog>
)
})
const PaddingWrapper = memo(({ clickedFromOrgTab, children }) => {
const { themeMode, brandColor } = useContext(Context);
const theme = getTheme(themeMode, brandColor);
const wrapperStyle = useMemo(() => ({
width: clickedFromOrgTab
? "100%"
: "auto",
padding: "27px 10px 19px 27px",
backgroundColor: '#212121',
backgroundColor: theme.palette.platformColor,
height: '100%',
boxSizing: 'border-box',
overflow: 'hidden',
maxHeight: "1700px", overflowY: "auto",scrollbarColor: '#494949 transparent', scrollbarWidth: 'thin'
}), [clickedFromOrgTab]);
maxHeight: "1700px",
overflowY: "auto",
scrollbarColor: theme.palette.scrollbarColorTransparent,
scrollbarWidth: 'thin'
}), [clickedFromOrgTab, theme]);
return (
<div style={wrapperStyle}>
@@ -2604,9 +3137,9 @@ const PaddingWrapper = memo(({ clickedFromOrgTab, children }) => {
);
});
const Wrapper = memo(({ children, clickedFromOrgTab }) => {
const Wrapper = memo(({ children, clickedFromOrgTab }) => {
return (
<PaddingWrapper clickedFromOrgTab={clickedFromOrgTab}>
<PaddingWrapper clickedFromOrgTab={clickedFromOrgTab}>
{children}
</PaddingWrapper>
);
+25 -25
View File
@@ -1,6 +1,6 @@
import React, { useState, useEffect, useContext, memo, useMemo } from 'react';
import theme from '../theme.jsx';
import {getTheme} from '../theme.jsx';
import classNames from "classnames";
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'
import { DataGrid, GridColDef, GridValueGetterParams } from '@mui/x-data-grid'
@@ -28,6 +28,7 @@ import {
Paper,
Chip,
Checkbox,
Box,
} from "@mui/material";
import {
@@ -42,6 +43,8 @@ import { Context } from '../context/ContextApi.jsx';
const LineChartWrapper = ({keys, inputname, height, width}) => {
const [hovered, setHovered] = useState("");
const inputdata = keys.data === undefined ? keys : keys.data
const {themeMode} = useContext(Context)
const theme = getTheme(themeMode)
return (
<div style={{color: "white", border: "1px solid rgba(255,255,255,0.3)", borderRadius: theme.palette?.borderRadius, padding: 30, marginTop: 15, backgroundColor: theme.palette.platformColor, overflow: "hidden", }}>
@@ -83,6 +86,8 @@ const AppStats = (defaultprops) => {
const [workflows, setWorkflows] = useState(inputWorkflows === undefined ? [] : inputWorkflows)
const [resultRows, setResultRows] = useState([])
const [resultLoading, setResultLoading] = useState(true)
const { themeMode, brandColor } = useContext(Context);
const theme = getTheme(themeMode, brandColor)
const includedExecutions = selectedOrganization?.sync_features?.app_executions !== undefined ? selectedOrganization?.sync_features?.app_executions?.limit : 0
@@ -529,11 +534,14 @@ const AppStats = (defaultprops) => {
const paperStyle = {
textAlign: "center",
padding: 40,
margin: 5,
backgroundColor: theme.palette.platformColor,
border: "1px solid rgba(255,255,255,0.3)",
maxWidth: 300,
padding: "40px",
margin: "5px",
backgroundColor: theme.palette.cardBackgroundColor,
border: theme.palette.defaultBorder,
maxWidth: "300px",
"&:hover": {
backgroundColor: theme.palette.cardHoverColor,
},
}
const columns: GridColDef[] = [
@@ -646,9 +654,9 @@ const AppStats = (defaultprops) => {
<div className="content" style={{width: "100%", margin: "auto", }}>
<Typography style={{margin: "auto", marginLeft: 10, marginBottom: 20, fontSize: 16}} color="textSecondary">
All shown statistics are gathered from <a
href={`${globalUrl}/api/v1/orgs/${selectedOrganization.id}/stats`}
href={`${globalUrl}/api/v1/orgs/${selectedOrganization?.id}/stats`}
target="_blank"
style={{ textDecoration: "none", color: "#FF8444",}}
style={{ textDecoration: "none", color: theme.palette.linkColor,}}
>Your Organisation Statistics. </a>
It exists to give you more insight into your workflows, and to understand your utilization of the Shuffle platform. <b>The billing tracker is in Beta, and is always calculated manually before being invoiced.</b>
</Typography>
@@ -662,9 +670,9 @@ const AppStats = (defaultprops) => {
The cost of app runs in the selected period based on {filteredStatistics.monthly_app_executions} App Runs. These numbers do not exclude your included 10.000/month or {includedExecutions} App Runs per month. App Run cost: ${invocationCost}.
</Typography>
}>
<Paper style={paperStyle}>
<Box sx={paperStyle}>
<Typography variant="h4">
${selectedOrganization.lead_info.customer === false && selectedOrganization.lead_info.pov === false ?
${selectedOrganization?.lead_info?.customer === false && selectedOrganization?.lead_info?.pov === false ?
0
:
apprunCost
@@ -673,57 +681,49 @@ const AppStats = (defaultprops) => {
<Typography variant="h6">
Period Cost
</Typography>
</Paper>
</Box>
</Tooltip>
<Tooltip title={
<Typography variant="body1" style={{padding: 10, }}>
App runs in the selected period
</Typography>
}>
<Paper style={paperStyle}>
<Box sx={paperStyle}>
<Typography variant="h4">
{filteredStatistics.monthly_app_executions === null || filteredStatistics.monthly_app_executions === undefined ? 0 : filteredStatistics.monthly_app_executions}
</Typography>
<Typography variant="h6">
App Runs
</Typography>
</Paper>
</Box>
</Tooltip>
<Tooltip title={
<Typography variant="body1" style={{padding: 10, }}>
Workflow runs in the selected period
</Typography>
}>
<Paper style={paperStyle}>
<Box sx={paperStyle}>
<Typography variant="h4">
{filteredStatistics.monthly_workflow_executions === null || filteredStatistics.monthly_workflow_executions === undefined ? 0 : filteredStatistics.monthly_workflow_executions}
</Typography>
<Typography variant="h6">
Workflow Runs
</Typography>
</Paper>
</Box>
</Tooltip>
<Tooltip title={
<Typography variant="body1" style={{padding: 10, }}>
Estimated cost to be billed at the end of the current month. Subtracted contractually included app runs. Actual cost month to date: ${monthToDateCost}. App Run cost: ${invocationCost}.
</Typography>
}>
<Paper style={{
textAlign: "center",
padding: 40,
margin: 5,
marginLeft: clickedFromOrgTab? null:90,
backgroundColor: theme.palette.platformColor,
border: "1px solid rgba(255,255,255,0.3)",
maxWidth: 300,
}}>
<Box sx={paperStyle}>
<Typography variant="h4">
${monthTotalCost}
</Typography>
<Typography variant="h6">
Estimated cost
</Typography>
</Paper>
</Box>
</Tooltip>
</div>
: null}
+60 -41
View File
@@ -1,8 +1,7 @@
import React, { useState, useEffect, useContext, memo } from "react";
import theme from "../theme.jsx";
import { getTheme } from "../theme.jsx";
import { toast } from 'react-toastify';
import ReactJson from "react-json-view-ssr";
import {
Typography,
Tooltip,
@@ -103,7 +102,10 @@ const CacheView = memo((props) => {
const [selectedFileId, setSelectedFileId] = React.useState("");
const [updateToThisCategory, setUpdateToThisCategory] = useState("")
const [showFileCategoryPopup, setShowFileCategoryPopup] = React.useState(false);
const [selectedFiles, setSelectedFiles] = useState([]);
const [selectedFiles, setSelectedFiles] = useState([]);
const { themeMode, brandColor } = useContext(Context);
const theme = getTheme(themeMode, brandColor);
useEffect(() => {
if (orgId?.length > 0) {
@@ -357,12 +359,12 @@ const CacheView = memo((props) => {
}}
>
<DialogTitle>
<span style={{ color: "white" }}>
<span style={{ color: theme.palette.text.primary }}>
{ editCache ? "Edit Key" : "Add Key"}{selectedCategory === "" || selectedCategory === "default" ? "" : ` in category '${selectedCategory}'`}
</span>
</DialogTitle>
<div style={{ paddingLeft: "30px", paddingRight: '30px', backgroundColor: "#212121", }}>
<div style={{ paddingLeft: "30px", paddingRight: '30px', backgroundColor: theme.palette.DialogStyle.backgroundColor, }}>
Key
<TextField
color="primary"
@@ -372,7 +374,7 @@ const CacheView = memo((props) => {
InputProps={{
style: {
height: "50px",
color: "white",
color: theme.palette.textFieldStyle.color,
fontSize: "1em",
},
}}
@@ -387,7 +389,7 @@ const CacheView = memo((props) => {
onChange={(e) => setKey(e.target.value)}
/>
</div>
<div style={{ paddingLeft: 30, paddingRight: 30, backgroundColor: "#212121" }}>
<div style={{ paddingLeft: 30, paddingRight: 30, backgroundColor: theme.palette.DialogStyle.backgroundColor }}>
<div style={{display: "flex", }}>
<Typography style={{marginTop: 25, marginBottom: 0, flex: 20, }}>
Value - ({isValidJson.valid === true ? "Valid" : "Invalid"} JSON)
@@ -408,7 +410,7 @@ const CacheView = memo((props) => {
style={{ backgroundColor: theme.palette.textFieldStyle.backgroundColor, marginTop: 0, }}
InputProps={{
style: {
color: "white",
color: theme.palette.textFieldStyle.color,
fontSize: "1em",
},
}}
@@ -429,20 +431,19 @@ const CacheView = memo((props) => {
</div>
<DialogActions style={{ paddingLeft: "30px", paddingRight: '30px' }}>
<Button
style={{ borderRadius: "2px", fontSize: 16, color: "#ff8544", textTransform:"none" }}
style={{ borderRadius: "2px", fontSize: 16, color: theme.palette.primary.main, textTransform:"none" }}
onClick={() => {
setModalOpen(false)
setKey("")
setValue("")
setDataValue({})
}}
color="primary"
>
Cancel
</Button>
<Button
variant="contained"
style={{ borderRadius: "2px", backgroundColor: "#ff8544",color: "#1a1a1a", textTransform:"none" }}
style={{ borderRadius: "2px", textTransform:"none" }}
onClick={() => {
if (value === "") {
toast("Key or Value can not be empty");
@@ -563,9 +564,9 @@ const CacheView = memo((props) => {
}}
>
<DialogTitle>
<div style={{ color: "rgba(255,255,255,0.9)" }}>
<Typography variant="h5" color="textPrimary">
Select sub-org to distribute Datastore key
</div>
</Typography>
</DialogTitle>
<DialogContent style={{ color: "rgba(255,255,255,0.65)" }}>
<MenuItem value="none" onClick={()=> {handleSelectSubOrg(null, "none")}}>None</MenuItem>
@@ -608,7 +609,7 @@ const CacheView = memo((props) => {
<div style={{ display: "flex", marginTop: 20 }}>
<Button
style={{ borderRadius: "2px", textTransform: 'none', fontSize:16, color: "#ff8544" }}
style={{ borderRadius: "2px", textTransform: 'none', fontSize:16, color: theme.palette.primary.main }}
onClick={() => setShowDistributionPopup(false)}
color="primary"
>
@@ -616,7 +617,7 @@ const CacheView = memo((props) => {
</Button>
<Button
variant="contained"
style={{ borderRadius: "2px", textTransform: 'none', fontSize:16, color: "#1a1a1a", backgroundColor: "#ff8544", marginLeft: 10 }}
style={{ borderRadius: "2px", textTransform: 'none', fontSize:16, marginLeft: 10 }}
onClick={() => {
changeDistribution(selectedCacheKey, selectedSubOrg);
}}
@@ -630,27 +631,27 @@ const CacheView = memo((props) => {
) : null;
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, 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? theme.palette.platformColor :null, borderTopRightRadius: isSelectedDataStore?'8px':null, borderBottomRightRadius: isSelectedDataStore?'8px':null, borderLeft: theme.palette.defaultBorder }}>
{modalView}
{cacheDistributionModal}
<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={{height: "100%", maxHeight: 1700, overflowY: "auto", scrollbarColor: theme.palette.scrollbarColorTransparent, scrollbarWidth: 'thin'}}>
<div style={{ height: "100%", width: "calc(100% - 20px)", scrollbarColor: theme.palette.scrollbarColorTransparent, scrollbarWidth: 'thin' }}>
<div style={{ marginTop: isSelectedDataStore?null:20, marginBottom: 20 }}>
<h2 style={{ display: isSelectedDataStore?null: "inline" }}>Shuffle Datastore {selectedCategory === "" || selectedCategory === "default" ? "" : `- Category '${selectedCategory}'`}</h2>
<span style={{ marginLeft: isSelectedDataStore?null:25, color:isSelectedDataStore?"#9E9E9E":null}}>
<Typography variant="h5" style={{ display: isSelectedDataStore?null: "inline", fontWeight: 500 }}>Shuffle Datastore {selectedCategory === "" || selectedCategory === "default" ? "" : `- Category '${selectedCategory}'`}</Typography>
<Typography variant="body2" color="textSecondary" style={{ marginLeft: isSelectedDataStore?null:25, marginTop: 10}}>
Datastore is a permanent key-value database for storing data that can be used cross-workflow. <br/>You can store anything from lists of IPs to complex configurations.&nbsp;
<a
target="_blank"
rel="noopener noreferrer"
href="/docs/organizations#datastore"
style={{ textDecoration: isSelectedDataStore?null:"none", color: isSelectedDataStore?"#FF8444":"#f85a3e" }}
style={{ textDecoration: isSelectedDataStore?null:"none", color: theme.palette.linkColor }}
>
Learn more
</a>
</span>
</Typography>
</div>
<Button
style={{backgroundColor: isSelectedDataStore? "#ff8544":null, fontSize: 16, boxShadow: isSelectedDataStore ? "none":null,textTransform: isSelectedDataStore ? 'capitalize':null, color:isSelectedDataStore?"#1a1a1a":null, borderRadius:isSelectedDataStore?4:null, width:isSelectedDataStore?162:null, height:isSelectedDataStore?40:null}}
style={{fontSize: 16, textTransform: isSelectedDataStore ? 'capitalize':null, borderRadius:isSelectedDataStore?4:null, width:isSelectedDataStore?162:null, height:isSelectedDataStore?40:null}}
variant="contained"
color="primary"
onClick={() =>{
@@ -663,9 +664,9 @@ const CacheView = memo((props) => {
Add Key
</Button>
<Button
style={{ marginLeft: 16, marginRight: 15, backgroundColor: isSelectedDataStore?"#2F2F2F":null, boxShadow: isSelectedDataStore ? "none":null,textTransform: isSelectedDataStore ? 'capitalize':null,borderRadius:isSelectedDataStore?4:null, width:isSelectedDataStore?81:null, height:isSelectedDataStore?40:null, }}
style={{ marginLeft: 16, marginRight: 15, borderRadius:isSelectedDataStore?4:null, width:isSelectedDataStore?81:null, height:isSelectedDataStore?40:null, }}
variant="contained"
color="primary"
color="secondary"
onClick={() => listOrgCache(orgId,selectedCategory)}
>
<CachedIcon />
@@ -767,7 +768,9 @@ const CacheView = memo((props) => {
//handleUpdateFileCategory(updateToThisCategory)
toast.error("Not implemented.")
}}
style={{fontSize: 16, textTransform: 'none', color: "#1a1a1a", backgroundColor: "#ff8544"}}
style={{fontSize: 16, textTransform: 'none', }}
color="primary"
variant="contained"
>
Update
</Button>
@@ -780,8 +783,9 @@ const CacheView = memo((props) => {
{renderTextBox ?
<Tooltip title={"Close"} style={{}} aria-label={""}>
<Button
style={{ marginLeft: 5, marginRight: 15, height: 35, borderRadius: 4, backgroundColor: "#494949", textTransform: 'none', fontSize: 16, color: "#f1f1f1" }}
color="primary"
style={{ marginLeft: 5, marginRight: 15, height: 35, borderRadius: 4, textTransform: 'none', fontSize: 16,}}
color="secondary"
variant="contained"
onClick={() => {
setRenderTextBox(false);
console.log(" close clicked")
@@ -793,9 +797,10 @@ const CacheView = memo((props) => {
:
<Tooltip title={"Add new file category"} style={{}} aria-label={""}>
<Button
style={{ marginLeft: 5, marginRight: 15, width: 169, height: 35, borderRadius: 4, backgroundColor: "#494949", textTransform: 'none', fontSize: 16, color: "#f1f1f1" }}
color="primary"
onClick={() => {
style={{ marginLeft: 5, whiteSpace: "nowrap", marginRight: 15, width: 169, height: 35, borderRadius: 4, textTransform: 'none', fontSize: 16, }}
variant="contained"
color="secondary"
onClick={() => {
setRenderTextBox(true);
}}
>
@@ -845,7 +850,7 @@ const CacheView = memo((props) => {
style={{
borderRadius: 4,
marginTop: 24,
border: "1px solid #494949",
border: theme.palette.defaultBorder,
width: "100%",
overflowX: "auto",
paddingBottom: 0,
@@ -861,7 +866,7 @@ const CacheView = memo((props) => {
minWidth: 800,
overflowX: "auto",
}}>
<ListItem style={{width: isSelectedDataStore?"100%":null, borderBottom:isSelectedDataStore?"1px solid #494949":null, display: "table-row"}}>
<ListItem style={{width: isSelectedDataStore?"100%":null, borderBottom:isSelectedDataStore? theme.palette.defaultBorder :null, display: "table-row"}}>
{["Key", "Value", "Actions", "Updated", "Distribution"].map((header, index) => (
<ListItemText
key={index}
@@ -872,7 +877,7 @@ const CacheView = memo((props) => {
whiteSpace: "nowrap",
overflow: "hidden",
textOverflow: "ellipsis",
borderBottom: "1px solid #494949"
borderBottom: theme.palette.defaultBorder,
}}
/>
))}
@@ -883,7 +888,7 @@ const CacheView = memo((props) => {
key={rowIndex}
style={{
display: "table-row",
backgroundColor: "#212121",
backgroundColor: theme.palette.platformColor,
}}
>
{Array(5)
@@ -900,7 +905,7 @@ const CacheView = memo((props) => {
variant="text"
animation="wave"
sx={{
backgroundColor: "#1a1a1a",
backgroundColor: theme.palette.loaderColor,
height: "20px",
borderRadius: "4px",
}}
@@ -936,9 +941,9 @@ const CacheView = memo((props) => {
return null
}
var bgColor = isSelectedDataStore? "#212121":"#27292d";
var bgColor = isSelectedDataStore? themeMode === "dark" ? "#212121" : "#FFFFFF" :"#27292d";
if (index % 2 === 0) {
bgColor = isSelectedDataStore? "#1A1A1A":"#1f2023";
bgColor = isSelectedDataStore? themeMode === "dark" ? "#1A1A1A" : "#EAEAEA" :"#1f2023";
}
const validate = validateJson(data.value);
@@ -972,11 +977,12 @@ const CacheView = memo((props) => {
src={validate.result}
theme={theme.palette.jsonTheme}
style={{
...theme.palette.reactJsonStyle,
backgroundColor: theme.palette.platformColor,
border: theme.palette.defaultBorder,
padding: 5,
maxHeight: 300,
overflowY: "auto",
backgroundColor: "#151515",
border: "1px solid rgba(255,255,255,0.7)",
}}
collapsed={true}
enableClipboard={(copy) => {
@@ -1022,7 +1028,20 @@ const CacheView = memo((props) => {
setModalOpen(true)
}}
>
<img src="/icons/editIcon.svg" alt="edit" />
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M16.1038 4.66848C16.3158 4.45654 16.5674 4.28843 16.8443 4.17373C17.1212 4.05903 17.418 4 17.7177 4C18.0174 4 18.3142 4.05903 18.5911 4.17373C18.868 4.28843 19.1196 4.45654 19.3315 4.66848C19.5435 4.88041 19.7116 5.13201 19.8263 5.40891C19.941 5.68582 20 5.9826 20 6.28232C20 6.58204 19.941 6.87882 19.8263 7.15573C19.7116 7.43263 19.5435 7.68423 19.3315 7.89617L8.43807 18.7896L4 20L5.21038 15.5619L16.1038 4.66848Z"
stroke={themeMode=== "dark" ? "#F1F1F1" : "#333"}
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
</IconButton>
</span>
</Tooltip>
+846
View File
@@ -0,0 +1,846 @@
import React, { useState, useEffect, useContext } from "react";
import { useParams, useNavigate, Link } from "react-router-dom";
import { v4 as uuidv4 } from "uuid";
import theme from '../theme.jsx';
import Markdown from 'react-markdown'
import { isMobile } from "react-device-detect";
import { Context } from '../context/ContextApi.jsx';
import AppSearch from "../components/AppSearch1.jsx";
import {
Divider,
ButtonGroup,
TextField,
Button,
IconButton,
Typography,
CircularProgress,
Card,
CardContent,
} from "@mui/material";
import {
Send as SendIcon,
} from "@mui/icons-material";
import AuthenticationOauth2 from "../components/Oauth2Auth.jsx";
import AuthenticationWindow from "../components/AuthenticationWindow.jsx";
const ChatBot = (props) => {
const { globalUrl } = props
const { supportEmail } = useContext(Context)
const [messages, setMessages] = useState([])
const [message, setMessage] = useState("");
const [loading, setLoading] = useState(false);
const [appAuthentication, setAppAuthentication] = React.useState([]);
const [inputAuth, setInputAuth] = useState([])
const [forceReauthentication, setForceReauthentication] = useState(false);
const [selectedType, setSelectedType] = useState("atomic");
const [appname, setAppname] = useState("");
const [threadId, setThreadId] = useState("");
const [runId, setRunId] = useState("");
const [showAppSearch, setShowAppSearch] = useState(false);
const waitingMsg = "Processing..."
const viewWidth = isMobile ? "92%" : 800
useEffect(() => {
// Check if loading and remove Waiting... from messages
const newmessages = messages
const foundmessages = messages.filter((msg) => msg.message !== waitingMsg)
if (foundmessages.length < newmessages.length) {
setMessages(foundmessages);
}
// Wait 0.5 second
const objDiv = document.getElementById("messages-window");
if (objDiv !== undefined && objDiv !== null) {
setTimeout(() => {
objDiv.scrollTop = objDiv.scrollHeight;
}, 250);
}
}, [messages]);
useEffect(() => {
if (appname === undefined || appname === null || appname === "") {
return
}
// Find the last message that was sent by us and reuse the same message content
// with added app stuff only
for (var i = messages.length-1; i >= 0; i--) {
const msg = messages[i]
if (msg.status === "sent") {
handleSubmit(undefined, msg.message)
break
}
}
}, [appname])
window.title = "Shuffle - New Chat"
let navigate = useNavigate();
// Automatic submit handler based on a lot of stuff :)
const handleSubmit = (e, inputmsg) => {
if (e !== undefined) {
e.preventDefault();
e.stopPropagation();
}
setLoading(true)
setMessage("");
const sentId = uuidv4();
var parsedData = {
"query": inputmsg,
"thread_id": threadId,
"run_id": runId,
}
if (appname !== undefined && appname !== null && appname !== "") {
parsedData["app_name"] = appname
}
if (inputAuth !== undefined && inputAuth.length > 0) {
// Forcing first auth app to be used in request
try {
parsedData["app_name"] = inputAuth[0].name
parsedData["app_id"] = inputAuth[0].id
parsedData["category"] = inputAuth[0].category
parsedData["action_name"] = inputAuth[0].action_name
} catch (e) {
}
try {
parsedData["app_name"] = inputAuth.apps[0].name
parsedData["app_id"] = inputAuth.apps[0].id
parsedData["category"] = inputAuth.apps[0].category
parsedData["action_name"] = inputAuth.apps[0].action_name
} catch (e) {
}
}
if (selectedType !== "default") {
if (selectedType == "workflow") {
parsedData["output_format"] = "workflow_suggestion"
} else {
parsedData["output_format"] = selectedType
}
}
console.log("INPUT: ", parsedData)
setInputAuth([])
var newmessages = messages;
newmessages.push({
"id": sentId,
"status": "sent",
"message": inputmsg,
})
newmessages.push({
"id": sentId,
"status": "received",
"message": waitingMsg,
})
setMessages(newmessages);
//fetch(`http://localhost:8080/api/v1/conversation`, {
fetch(`${globalUrl}/api/v1/conversation`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
credentials: "include",
body: JSON.stringify(parsedData),
})
.then((res) => res.text())
.then((resText) => {
setLoading(false)
var data = {}
// JSON parse
try {
data = JSON.parse(resText);
} catch (e) {
console.log("Error parsing response as JSON: ", e);
newmessages = newmessages.filter((msg) => msg.message !== waitingMsg);
newmessages.push({
"status": "received",
"message": resText,
"id": uuidv4(),
});
setMessages(newmessages);
return;
}
if (data.run_id !== undefined && data.run_id !== null && data.run_id !== "") {
setRunId(data.run_id)
}
if (data.thread_id !== undefined && data.thread_id !== null && data.thread_id !== "") {
setThreadId(data.thread_id)
}
if (data.success === undefined) {
newmessages = newmessages.filter((msg) => msg.message !== waitingMsg);
newmessages.push({
"status": "received",
"message": resText,
"id": uuidv4(),
});
setMessages(newmessages);
return;
}
// authentication for app
// app validation (choose one)
const defaultMessage = `Default output. The feature you're interacting with may not have been implemented yet. Contact ${supportEmail} with a screenshot of this and your input please.`
var outputmessage = defaultMessage;
var status = "received";
var action = ""
if (data.success === false) {
if (data.reason !== undefined) {
outputmessage = data.reason
}
status = "error"
} else {
if (data.reason !== undefined) {
outputmessage = data.reason
}
}
if (data.action !== undefined) {
//console.log("Action is defined: ", data.action);
action = data.action
if (data.action === "app_authentication") {
// If success & app auth -> say auth success and show available labels
// If !success & app auth -> do authentication
if (data.success === true) {
newmessages = newmessages.filter((msg) => msg.message !== waitingMsg);
// No action for this.
// "action": action,
var appname = ""
if (data.apps !== undefined && data.apps !== null && data.apps.length > 0) {
appname = data.apps[0].name.replaceAll("_", " ")
}
var outputmessage = `**Please specify which ${appname} action you want to use**: \n`
if (data.available_labels !== undefined && data.available_labels !== null && data.available_labels.length > 0) {
for (var i = 0; i < data.available_labels.length; i++) {
outputmessage += "* " + data.available_labels[i] + "\n"
}
outputmessage += "* Reauthenticate ([see auth](/admin?tab=app_auth))"
}
//Some opavailable actions: " + data.apps.map((app) => app.name).join(", ")
const parsedmessage = {
"status": status,
"message": outputmessage,
"id": uuidv4(),
"category": data.category,
"thread_id": data.thread_id,
"run_id": data.run_id,
}
newmessages.push(parsedmessage);
setMessages(newmessages);
return
} else {
if (data.apps !== undefined) {
setInputAuth(data.apps)
setMessage(inputmsg);
setForceReauthentication(true)
}
}
} else if (data.action === "select_category" || data.action === "select_app") {
console.log("[DEBUG] APP SELECTION! Should help them choose an app to use")
// Show a search field
setShowAppSearch(true)
}
}
newmessages = newmessages.filter((msg) => msg.message !== waitingMsg);
const parsedmessage = {
"status": status,
"message": outputmessage,
"id": uuidv4(),
"action": action,
"category": data.category,
"thread_id": data.thread_id,
"run_id": data.run_id,
}
newmessages.push(parsedmessage);
setMessages(newmessages);
console.log("New message: ", parsedmessage)
})
.catch((err) => {
setLoading(false)
console.log("Problem: ", err);
setMessage(message);
newmessages = newmessages.filter((msg) => msg.message !== waitingMsg);
// Find the message with the sentId and change the status to error
newmessages.push({
"status": "error",
"message": message,
"error_message": "Failed to send: "+err,
"id": sentId,
});
setMessages(newmessages);
});
};
// Used to verify if the user is logged in after auth is done
const getAppAuthentication = () => {
console.log("Continue chat from the previous stage!");
fetch(globalUrl + "/api/v1/apps/authentication", {
method: "GET",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
credentials: "include",
})
.then((response) => {
if (response.status !== 200) {
console.log("Status not 200 for app auth :O!");
}
return response.json();
})
.then((responseJson) => {
if (!responseJson.success) {
console.log("Failed to get app auth!");
return;
}
var newauth = [];
for (let authkey in responseJson.data) {
if (responseJson.data[authkey].defined === false) {
continue;
}
newauth.push(responseJson.data[authkey]);
}
if (newauth.length > appAuthentication.length) {
console.log("New auth is longer than old auth. Set new auth!");
setForceReauthentication(false)
// Check if last message contains "reauth"
if (messages.length > 0) {
const lastmessage = messages[messages.length-1];
if (lastmessage.message.toLowerCase().includes("re-auth")) {
console.log("Skipping resend due to reauth")
var newmessages = messages
newmessages.push({
"status": "received",
"message": "Authentication done. What do you want to do?",
"id": uuidv4(),
});
setMessages(newmessages);
} else {
handleSubmit(undefined, message)
}
} else {
handleSubmit(undefined, message)
}
}
setAppAuthentication(newauth)
})
.catch((err) => {
console.log("Error in getAppAuthentication: ", err);
})
}
const AuthWrapper = (props) => {
const { app } = props;
const [authenticationModalOpen, setAuthenticationModalOpen] = React.useState(false);
console.log("AUTH: ", app)
return (
<div style={{position: "absolute", right: 250, bottom: 150, }}>
{app.authentication.type === "oauth2" || app.authentication.type === "oauth2-app" ?
<AuthenticationOauth2
selectedApp={app}
selectedAction={{
"app_name": app.name,
"app_id": app.id,
"app_version": app.version,
"large_image": app.large_image,
}}
authenticationType={app.authentication}
isCloud={true}
authButtonOnly={true}
getAppAuthentication={getAppAuthentication}
/>
:
<Button
fullWidth
variant="contained"
style={{
marginBottom: 20,
marginTop: 20,
flex: 1,
textTransform: "none",
textAlign: "left",
justifyContent: "flex-start",
backgroundColor: "#ffffff",
color: "#2f2f2f",
borderRadius: theme.palette?.borderRadius,
minWidth: 300,
maxWidth: 300,
maxHeight: 50,
overflow: "hidden",
border: `1px solid ${theme.palette.inputColor}`,
}}
color="primary"
fullWidth
color="primary"
onClick={(e) => {
console.log("Click? ")
e.preventDefault();
setAuthenticationModalOpen(true);
}}
>
<span style={{display: "flex"}}>
<img
alt={app.name}
style={{ margin: 4, minHeight: 30, maxHeight: 30, borderRadius: theme.palette?.borderRadius, }}
src={app.large_image}
/>
<Typography style={{ margin: 0, marginLeft: 10, marginTop: 5,}} variant="body1">
Authenticate
</Typography>
</span>
</Button>
}
{app.authentication.type !== "oauth2" && authenticationModalOpen ?
<AuthenticationWindow
selectedApp={app}
globalUrl={globalUrl}
getAppAuthentication={getAppAuthentication}
appAuthentication={appAuthentication}
authenticationModalOpen={authenticationModalOpen}
setAuthenticationModalOpen={setAuthenticationModalOpen}
/>
: null}
</div>
)
}
var amountfinished = 0;
const showAuthentication = inputAuth.map((app, index) => {
const authexists = appAuthentication.find((auth) => auth.app.id === app.id);
if (authexists !== undefined && forceReauthentication === false) {
console.log("Auth exists: ", authexists);
amountfinished += 1
return null
}
return (
<div key={index}>
<AuthWrapper app={app} />
</div>
)
})
if (amountfinished === inputAuth.length && amountfinished > 0) {
setInputAuth([])
setAppAuthentication([])
}
const showSamples =
<div style={{marginLeft: 10, marginRight: 10, }}>
<Card style={{backgroundColor: theme.palette.surfaceColor, borderRadius: theme.palette?.borderRadius,}}>
<CardContent>
<Typography variant="h6">
How many incidents did we get last week?
</Typography>
</CardContent>
</Card>
<Card style={{backgroundColor: theme.palette.surfaceColor, borderRadius: theme.palette?.borderRadius, marginTop: 10, }}>
<CardContent>
<Typography variant="h6">
Answer the last email from Jim about the new project, and say we're on it
</Typography>
</CardContent>
</Card>
<Card style={{backgroundColor: theme.palette.surfaceColor, borderRadius: theme.palette?.borderRadius, marginTop: 10, }}>
<CardContent>
<Typography variant="h6">
Is the IP 1.2.3.4 blocked? If not, block it.
</Typography>
</CardContent>
</Card>
</div>
function OuterLink(props) {
return (
<a
target="_blank"
rel="noopener noreferrer"
href={props.href}
style={{ color: "#f85a3e", textDecoration: "none" }}
>
{props.children}
</a>
);
}
function Img(props) {
return <img style={{ borderRadius: theme.palette?.borderRadius, width: 750, maxWidth: "100%", marginTop: 15, marginBottom: 15, }} alt={props.alt} src={props.src} />;
}
function CodeHandler(props) {
//console.log("Codehandler PROPS: ", props)
const propvalue = props.value !== undefined && props.value !== null ? props.value : props.children !== undefined && props.children !== null && props.children.length > 0 ? props.children[0] : ""
return (
<div
style={{
minWidth: "50%",
maxWidth: "100%",
backgroundColor: theme.palette.inputColor,
overflowY: "auto",
// Check if props.inline === true, then do it inline
padding: props.inline ? 0 : 15,
display: props.inline ? "inline" : "block",
}}
>
<code
style={{
// Wrap if larger than X
whiteSpace: "pre-wrap",
overflow: "auto",
}}
>{propvalue}</code>
</div>
);
}
const markdownStyle = {
color: "rgba(255, 255, 255, 0.65)",
overflow: "hidden",
paddingBottom: 100,
margin: "auto",
maxWidth: "100%",
minWidth: "100%",
overflow: "hidden",
fontSize: isMobile ? "1.3rem" : "1.0rem",
}
const Heading = (props) => {
const element = React.createElement(
`h${props.level}`,
{ style: { marginTop: props.level === 1 ? 20 : 50 } },
props.children
);
const [hover, setHover] = useState(false);
var extraInfo = "";
return (
<Typography
onMouseOver={() => {
setHover(true);
}}
>
{props.level !== 1 ? (
<Divider
style={{
width: "90%",
marginTop: 40,
backgroundColor: theme.palette.inputColor,
}}
/>
) : null}
{element}
{/*hover ? <LinkIcon onMouseOver={() => {setHover(true)}} style={{cursor: "pointer", display: "inline", }} onClick={() => {
window.location.href += "#hello"
console.log(window.location)
//window.history.pushState('page2', 'Title', '/page2.php');
//window.history.replaceState('page2', 'Title', '/page2.php');
}} />
: ""
*/}
{extraInfo}
</Typography>
);
}
const OrderedList = (props) => {
var parsedchildren = []
for (var i = 0; i < props.children.length; i++) {
const child = props.children[i]
if (child === "\n") {
continue
}
if (child.props !== undefined && child.props.children !== undefined) {
// Remove <p> from the child wrapper
var parsedchild = []
for (var j = 0; j < child.props.children.length; j++) {
const childchild = child.props.children[j]
// print the raw childchild bytes, not string
if (childchild === "\n") {
continue
}
// If the childchild has <p> around it, remove it
parsedchild.push(childchild)
/*
// Not doing this as it breaks links
if (childchild.props !== undefined && childchild.props.children !== undefined) {
parsedchild.push(childchild.props.children)
} else {
parsedchild.push(childchild)
}
*/
}
parsedchildren.push(parsedchild)
}
}
return (
<ol style={{marginTop: 0, }}>
{parsedchildren.map((child, index) => {
return (
<li key={index} style={{minHeight: 0, display: "block", }}>
<p style={{marginTop: 0, marginBottom: 10, }}>
{index+1}. {child}
</p>
</li>
)
})}
</ol>
)
}
const Paragraph = (props) => {
return (
<p style={{marginTop: 15, marginBottom: 15, }}>
{props.children}
</p>
)
}
const markdownComponents = {
ol: OrderedList,
ul: OrderedList,
img: Img,
code: CodeHandler,
h1: Heading,
h2: Heading,
h3: Heading,
h4: Heading,
h5: Heading,
h6: Heading,
a: OuterLink,
p: Paragraph,
}
const chatWindow =
<div style={{minWidth: viewWidth, maxWidth: viewWidth, margin: "auto", textAlign: "left", minHeight: 1500, }}>
{messages.length === 0 ?
<span>
<h1>Shuffle AI</h1>
{showSamples}
</span>
: null}
<div
id="messages-window"
style={{
marginTop: 50,
display: "flex",
flexDirection: "column",
minHeight: 1500,
maxHeight: isMobile ? "85%" : "85%",
overflow: "auto", paddingBottom: 200,
}}
>
{messages.map((message, index) => {
const float = message.status === "sent" ? "left" : "right";
const border = message.status === "error" ? "red" : "rgba(255,255,255,0.3)"
const hasAction = message.action !== undefined && message.action !== null && message.action !== ""
return (
// Make a chat bubble component
<div key={index} style={{position: "relative", width: "100%", marginTop: 15, marginLeft: isMobile ? 10 : 0, }}>
<Typography variant="body1" style={{display: "flex", backgroundColor: theme.palette.surfaceColor, color: "white", padding: "0px 10px 0px 10px", borderRadius: theme.palette?.borderRadius, float: float, border: `1px solid ${border}`, "cursor": hasAction ? "pointer" : "default", maxWidth: viewWidth-30, overflowWrap: "break-word", whiteSpace: "pre-line" }} onClick={() => {
if (!hasAction) {
return
}
if (message.action === "login") {
navigate("/login?view=/conversation&message=You must log in to use ShuffleGPT")
} else if (message.action === "app_authentication") {
console.log("App auth action!")
//setAuthenticationModalOpen(true)
} else {
console.log("\n\nUnknown click action: ", message.action)
}
}}>
{message.message === waitingMsg ? <CircularProgress style={{height: 20, width: 20, marginTop: 20, marginRight: 10, }} /> : null}
<span>
<Markdown
components={markdownComponents}
id="markdown_wrapper"
style={{
minHeight: 20,
marginTop: 0,
display: "flex",
flexDirection: "row",
}}
>
{message.message}
</Markdown>
{message.thread_id !== undefined && message.thread_id !== null && message.thread_id !== "" ?
<Typography variant="body2" style={{color: "rgba(255,255,255,0.5)", marginTop: 5, }}>
Thread: {message.thread_id}
</Typography>
: null
}
</span>
</Typography>
{message.status === "error" && message.error_message ?
<Typography variant="body2" style={{color: "red", }}>
{message.error_message}
</Typography>
: null}
{(message.action === "select_category" || message.action === "select_app") && showAppSearch && index === messages.length-1 ?
<div style={{position: "absolute", right: 0, bottom: -100, }}>
<AppSearch
placeholder={"Find your "+message.category+" app"}
setNewSelectedApp={setAppname}
/>
</div>
: null}
</div>
)
})}
</div>
{showAuthentication}
<div style={{position: "fixed", bottom: 0, left: 0, width: "100%", zIndex: 100, backgroundColor: theme.palette.platformColor, }}>
<div style={{width: viewWidth, margin: "auto", }}>
{messages.length === 0 ?
<span>
<Typography variant="body2" color="textSecondary">
Query Type
</Typography>
<ButtonGroup
fullWidth
color="secondary"
style={{display: "flex", marginTop: 10, }}
>
{/*
<Button
fullWidth
disabled
variant={selectedType === "default" ? "contained" : "outlined"}
onClick={() => setSelectedType("default")}
>
Auto
</Button>
*/}
<Button
fullWidth
variant={selectedType === "atomic" ? "contained" : "outlined"}
onClick={() => setSelectedType("atomic")}
>
Auto-run action
</Button>
<Button
fullWidth
variant={selectedType === "support" ? "contained" : "outlined"}
onClick={() => setSelectedType("support")}
>
Support
</Button>
</ButtonGroup>
</span>
: null}
<form onSubmit={(e) => handleSubmit(e, message)} style={{bottom: 20, marginTop: 10, marginBottom: isMobile ? 0 : 10, maxWidth: viewWidth, minWidth: viewWidth, }}>
<TextField
id="message"
fullWidth
disabled={loading}
label="Send a message"
value={message}
onChange={(e) => setMessage(e.target.value)}
variant="outlined"
autoFocus
InputProps={{
endAdornment: (
<IconButton
aria-label="send message"
onClick={(e) => handleSubmit(e, message)}
>
<SendIcon color="primary" />
</IconButton>
)
}}
/>
</form>
{isMobile ? null :
<Typography variant="body2" color="textSecondary" align="center" style={{marginTop: 0,}} >
{`The Shuffle AI is a test system for automatic workflow generation and atomic functions for the future of Shuffle. Shuffle AI may use your organization info in the query, and attempts to auto-correct any failed behavior. If you have any questions, please contact us at ${supportEmail}`}
</Typography>
}
</div>
</div>
</div>
return (
<div style={{width: isMobile ? "100%" : 1000, margin: "auto", paddingTop: 50, }}>
{chatWindow}
</div>
)
}
export default ChatBot;
+27 -22
View File
@@ -26,7 +26,7 @@ import {
Visibility as VisibilityIcon,
VisibilityOff as VisibilityOffIcon,
} from "@mui/icons-material";
import theme from "../theme.jsx";
import { getTheme } from "../theme.jsx";
import { styled } from '@mui/styles';
import { Context } from "../context/ContextApi.jsx";
@@ -48,6 +48,8 @@ const CloudSyncTab = (props) => {
const [, forceUpdate] = React.useState();
const itemColor = "white";
const isCloud = window?.location?.host === "localhost:3002" || window?.location?.host === "shuffler.io";
const { themeMode, brandColor } = useContext(Context);
const theme = getTheme(themeMode, brandColor);
useEffect(() => { getSettings(); }, []);
const GridItem = (props) => {
const [expanded, setExpanded] = React.useState(false);
@@ -167,9 +169,9 @@ const CloudSyncTab = (props) => {
<div
style={{
margin: 4,
backgroundColor: "#1a1a1a",
backgroundColor: theme.palette.backgroundColor,
borderRadius: 8,
color: "white",
color: theme.palette.text.primary,
minHeight: expanded ? 250 : "inherit",
maxHeight: expanded ? 300 : "inherit",
boxShadow: "none",
@@ -188,13 +190,13 @@ const CloudSyncTab = (props) => {
<Avatar>{primaryIcon}</Avatar>
</ListItemAvatar>
<ListItemText
style={{ textTransform: "capitalize", color: "#F1F1F1", fontSize: 14, fontWeight: 400, }}
style={{ textTransform: "capitalize", color: theme.palette.text.primary, fontSize: 14, fontWeight: 400, }}
primary={primary}
/>
{isCloud && userdata.support === true ?
<Tooltip title="Edit features (support users only)">
<EditIcon
color="secondary"
color="textPrimary"
style={{ marginRight: 10, cursor: "pointer", }}
onClick={(e) => {
e.preventDefault();
@@ -527,20 +529,20 @@ const CloudSyncTab = (props) => {
return (
<div style={{padding: "27px 10px 19px 27px",}}>
<div style={{ marginBottom: 20 }}>
<h2
style={{ marginBottom: 8, marginTop: 0, color: "#ffffff" }}
<Typography variant="h5"
style={{ marginBottom: 8, marginTop: 0, fontWeight: 500}}
>
Cloud syncronization
</h2>
<span style={{ color: "#C8C8C8", fontSize: 16, fontWeight: 400, }}>
What does <a href="/docs/organizations#cloud_sync" target="_blank" rel="noopener noreferrer" style={{ color: "rgba(255, 132, 68, 1)", fontSize: 16, textDecoration: 'none', }}>cloud sync</a> do? Cloud synchronization is a way of getting more out of Shuffle. Shuffle will ALWAYS make every option open source, but features relying on other users can't be done without a collaborative approach.
</span>
</Typography>
<Typography variant="body2" style={{ color: theme.palette.text.secondary, fontSize: 16, fontWeight: 400, }}>
What does <a href="/docs/organizations#cloud_sync" target="_blank" rel="noopener noreferrer" style={{ color: theme.palette.linkColor, fontSize: 16, textDecoration: 'none', }}>cloud sync</a> do? Cloud synchronization is a way of getting more out of Shuffle. Shuffle will ALWAYS make every option open source, but features relying on other users can't be done without a collaborative approach.
</Typography>
</div>
{isCloud ? (
<div style={{ marginTop: 15, display: "flex" }}>
<div style={{ flex: 1 }}>
<Typography style={{fontWeight: 400, fontSize: 16, color: "#F1F1F1"}}>
<Typography style={{fontWeight: 400, fontSize: 16, color: theme.palette.text.secondary}}>
Currently syncronizing:{" "}
{selectedOrganization.cloud_sync_active === true
? <span style={{ color: "#4CFD72", fontSize: 16, marginLeft: 16}}>True</span>
@@ -561,28 +563,31 @@ const CloudSyncTab = (props) => {
marginRight: 10,
fontSize: 16,
fontWeight: 400,
color: theme.palette.text.primary,
fontFamily: theme.typography.fontFamily,
}}
>
Your Api key
</Typography>
{userSettings?.apikey === undefined || userSettings?.apikey === null || userSettings?.apikey?.length <=0 ? (
<Skeleton variant="rectangular" animation="wave" sx={{backgroundColor: '#212121', border: '1px solid #646464', width: 500, height: 50, marginTop: 2 }}/>
<Skeleton variant="rectangular" animation="wave" sx={{backgroundColor: theme.palette.loaderColor, border: '1px solid #646464', width: 500, height: 50, marginTop: 2 }}/>
):
<div style={{ display: "flex" }}>
<TextField
color="primary"
style={{
backgroundColor: theme.palette.inputColor,
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
borderRadius: theme.palette.textFieldStyle.borderRadius,
color: theme.palette.textFieldStyle.color,
maxWidth: 500,
height: 35
}}
InputProps={{
sx: {
height: "35px",
color: "white",
color: theme.palette.textFieldStyle.color,
fontSize: "1em",
backgroundColor: '#212121',
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
borderRadius: theme.palette.textFieldStyle.borderRadius,
},
endAdornment: (
<InputAdornment position="end">
@@ -701,12 +706,12 @@ const CloudSyncTab = (props) => {
</div>
)}
<h2 style={{ marginLeft: 5, marginTop: 40, marginBottom: 5 }}>
<Typography variant="h5" style={{ marginLeft: 5, marginTop: 40, marginBottom: 5 }}>
Features
</h2>
<Typography variant="body2" color="textSecondary" style={{ marginBottom: 24, fontSize: 16, fontWeight: 400, marginLeft: 5, color: "#C8C8C8" }}>
</Typography>
<Typography variant="body2" color="textSecondary" style={{ marginBottom: 24, fontSize: 16, fontWeight: 400, marginLeft: 5, color: theme.palette.text.secondary }}>
Features and Limitations that are currently available to you in your Cloud or Hybrid Organization. App Executions (App Runs) reset monthly. If the organization is a customer or in a trial, these features limitations are not always enforced. </Typography>
<Grid container style={{ width: "100%", marginBottom: 15, }}>
<Grid container style={{ width: "100%", marginBottom: 15, }}>
{selectedOrganization.sync_features === undefined ||
selectedOrganization.sync_features === null
@@ -728,7 +733,7 @@ const CloudSyncTab = (props) => {
variant="rectangular"
height={50}
width={343}
sx={{ backgroundColor: '#1a1a1a', display: 'flex', borderRadius: 1 }}
sx={{ backgroundColor: theme.palette.loaderColor, display: 'flex', borderRadius: 1 }}
animation="wave"
/>
</div>
@@ -1,7 +1,7 @@
import React, { useState, useEffect } from "react";
import React, { useState, useEffect, useContext } from "react";
import { useInterval } from "react-powerhooks";
import { toast } from 'react-toastify';
import theme from "../theme.jsx";
import {getTheme} from "../theme.jsx";
import WorkflowValidationTimeline from "../components/WorkflowValidationTimeline.jsx"
import {
@@ -20,7 +20,7 @@ import {
Collapse,
IconButton,
} from "@mui/material";
import { Context } from "../context/ContextApi.jsx";
import {
FavoriteBorder as FavoriteBorderIcon,
Error as ErrorIcon,
@@ -76,6 +76,8 @@ const ConfigureWorkflow = (props) => {
const [showFinalizeAnimation, setShowFinalizeAnimation] = React.useState(false);
const [loopRunning, setLoopRunning] = useState(false)
const [checkStarted, setCheckStarted] = React.useState(false);
const { themeMode } = useContext(Context);
const theme = getTheme(themeMode);
useEffect(() => {
if (requiredActions.length === 0) {
@@ -799,7 +801,7 @@ const ConfigureWorkflow = (props) => {
>
<div
style={{
border: filled ? `1px solid ${theme.palette.green}` : "1px solid rgba(255,255,255,0.3)", borderRadius: theme.palette?.borderRadius, width: "100%", padding: 12, cursor: filled ? "default" : "pointer",
border: filled ? `1px solid ${theme.palette.green}` : theme.palette.textFieldStyle.border, borderRadius: theme.palette?.borderRadius, width: "100%", padding: 12, cursor: filled ? "default" : "pointer",
}}
id="app-config"
>
+12 -6
View File
@@ -3,6 +3,8 @@ import OrgHeaderexpanded from "../components/OrgHeaderexpandedNew.jsx";
import OrgHeader from '../components/OrgHeaderNew.jsx';
import { toast } from "react-toastify";
import CloudSyncTab from '../components/CloudSyncTab.jsx';
import { Context } from '../context/ContextApi.jsx';
import { getTheme } from '../theme.jsx';
import {
FileCopy as FileCopyIcon,
} from "@mui/icons-material";
@@ -10,6 +12,7 @@ import {
Button,
Tooltip,
IconButton,
Typography,
} from "@mui/material";
const EditOrgTab = (props) => {
@@ -33,6 +36,9 @@ const EditOrgTab = (props) => {
}
}, []);
const { themeMode, brandColor } = useContext(Context);
const theme = getTheme(themeMode, brandColor);
const handleStatusChange = (event) => {
const { value } = event.target;
setSelectedStatus(value);
@@ -283,23 +289,23 @@ If you're interested, please let me know a time that works for you, or set up a
return (
<div style={{ width: "100%", boxSizing: 'border-box', padding: "27px 10px 19px 27px", height:"100%", backgroundColor: '#212121', borderRadius: '16px', }}>
<div style={{ height: "100%", width: "100%", overflowX: 'hidden', scrollbarColor: '#494949 transparent', scrollbarWidth: 'thin'}} >
<div style={{ width: "100%", boxSizing: 'border-box', padding: "27px 10px 19px 27px", height:"100%", backgroundColor: theme.palette.platformColor, borderRadius: '16px', }}>
<div style={{ height: "100%", width: "100%", overflowX: 'hidden', scrollbarColor: theme.palette.scrollbarColorTransparent, scrollbarWidth: 'thin'}} >
<div style={{ marginBottom: 20 }}>
<div style={{display:"flex"}}>
<div style={{width:'70%'}}>
<h2 style={{ marginBottom: 8, marginTop: 0, color: "#ffffff" }}>Organization overview</h2>
<span style={{ color: "#9E9E9E" }}>
<Typography variant='h3' sx={{ marginBottom: "15px", marginTop: 0, }}>Organization overview</Typography>
<Typography variant="body2" style={{ color: theme.palette.text.secondary, fontSize: 16 }}>
On this page organization admins can configure organisations, and sub-orgs (MSSP).{" "}
<a
target="_blank"
rel="noopener noreferrer"
href="/docs/organizations#organization"
style={{ color: "#FF8444" }}
style={{ color: theme.palette.linkColor, }}
>
Learn more
</a>
</span>
</Typography>
</div>
<div style={{display:"flex", alignItems:"center", marginLeft:50}}>
<Tooltip
+27 -22
View File
@@ -1,5 +1,5 @@
import React, { useEffect, useContext } from "react";
import theme from '../theme.jsx';
import { getTheme } from '../theme.jsx';
import { isMobile } from "react-device-detect"
import { MuiChipsInput } from "mui-chips-input";
import { toast } from "react-toastify"
@@ -46,7 +46,7 @@ import {
Slider,
} from "@mui/material";
import { Context } from "../context/ContextApi.jsx";
import {
DatePicker,
LocalizationProvider,
@@ -69,7 +69,8 @@ const EditWorkflow = (props) => {
const { globalUrl, workflow, setWorkflow, modalOpen, setModalOpen, showUpload, usecases, setNewWorkflow, appFramework, isEditing, userdata, apps, saveWorkflow, expanded, scrollTo, setRealtimeMarkdown, boxWidth, setBoxWidth, } = props
const [_, setUpdate] = React.useState(""); // Used for rendering, don't remove
const {themeMode, brandColor} = useContext(Context)
const theme = getTheme(themeMode, brandColor)
const [submitLoading, setSubmitLoading] = React.useState(false);
const [showMoreClicked, setShowMoreClicked] = React.useState(isEditing !== false ? true : false);
@@ -203,23 +204,27 @@ const EditWorkflow = (props) => {
setModalOpen(false);
}}
PaperProps={{
style: {
color: "white",
minWidth: isMobile ? "90%" : 650,
maxWidth: isMobile ? "90%" : 650,
minHeight: 400,
paddingTop: 25,
paddingLeft: 50,
sx: {
color: theme.palette.DialogStyle.color,
minWidth: isMobile ? "90%" : "650px",
maxWidth: isMobile ? "90%" : "650px",
minHeight: "400px",
//minWidth: isMobile ? "90%" : newWorkflow === true ? 1000 : 550,
//maxWidth: isMobile ? "90%" : newWorkflow === true ? 1000 : 550,
borderRadius: theme.palette.borderRadius,
backgroundColor: "black",
borderRadius: theme.palette.DialogStyle.borderRadius,
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
'& .MuiDialogContent-root': {
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
},
'& .MuiDialogTitle-root': {
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
},
},
}}
>
<DialogTitle style={{ padding: 30, paddingBottom: 0, zIndex: 1000, }}>
<DialogTitle style={{ padding: 30, paddingBottom: 0, zIndex: 1000, paddingTop: "25px", paddingLeft: "50px"}}>
<div style={{ display: "flex" }}>
<div style={{ flex: 1, color: "rgba(255,255,255,0.9)" }}>
<div style={{ flex: 1, color: theme.palette.textColor }}>
<div style={{ display: "flex" }}>
<Typography variant="h4" style={{ flex: 9, }}>
{newWorkflow ? "New" : "Editing"} workflow
@@ -248,7 +253,7 @@ const EditWorkflow = (props) => {
</div>
<Typography variant="body2" color="textSecondary" style={{ marginTop: 20, maxWidth: 440, }}>
Workflows can be built from scratch, or from templates. <a href="/usecases2" rel="noopener noreferrer" target="_blank" style={{ textDecoration: "none", color: "#f86a3e" }}>Usecases</a> can help you discover next steps, and you can <a href="/search?tab=workflows" rel="noopener noreferrer" target="_blank" style={{ textDecoration: "none", color: "#f86a3e" }}>search</a> for them directly. <a href="/docs/workflows" rel="noopener noreferrer" target="_blank" style={{ textDecoration: "none", color: "#f86a3e" }}>Learn more</a>
Workflows can be built from scratch, or from templates. <a href="/usecases2" rel="noopener noreferrer" target="_blank" style={{ textDecoration: "none", color: theme.palette.linkColor }}>Usecases</a> can help you discover next steps, and you can <a href="/search?tab=workflows" rel="noopener noreferrer" target="_blank" style={{ textDecoration: "none", color: theme.palette.linkColor }}>search</a> for them directly. <a href="/docs/workflows" rel="noopener noreferrer" target="_blank" style={{ textDecoration: "none", color:theme.palette.linkColor }}>Learn more</a>
</Typography>
<div style={{marginTop: 10, marginBottom: 10, marginRight: 50, }}>
@@ -288,16 +293,16 @@ const EditWorkflow = (props) => {
</DialogTitle>
<FormControl>
<div style={{
borderTop: "1px solid rgba(255,255,255,0.5)",
borderTop: theme.palette.defaultBorder,
width: 600,
position: "fixed",
right: 20,
bottom: 0,
zIndex: 1002,
backgroundColor: theme.palette.backgroundColor,
backgroundColor: theme.palette.DialogStyle.backgroundColor,
height: 75,
paddingTop: 20,
paddingLeft: 75,
paddingLeft: 30,
}}>
<Button
variant="contained"
@@ -388,7 +393,7 @@ const EditWorkflow = (props) => {
</Button>
</div>
<DialogContent style={{ paddingTop: 10, display: "flex", minHeight: 300, zIndex: 1001, paddingBottom: 200, }}>
<DialogContent style={{ paddingTop: 10, display: "flex", minHeight: 300, zIndex: 1001, paddingBottom: 200, paddingLeft: "50px" }}>
<div style={{ minWidth: newWorkflow ? 500 : 550, maxWidth: newWorkflow ? 450 : 500, }}>
<TextField
onChange={(event) => {
@@ -396,7 +401,7 @@ const EditWorkflow = (props) => {
}}
InputProps={{
style: {
color: "white",
color: theme.palette.textFieldStyle.color,
},
}}
color="primary"
@@ -469,7 +474,7 @@ const EditWorkflow = (props) => {
style={{ flex: 1, maxHeight: 120, overflow: "auto", }}
InputProps={{
style: {
color: "white",
color: theme.palette.textFieldStyle.color,
},
}}
placeholder="Tags"
@@ -1287,7 +1292,7 @@ const EditWorkflow = (props) => {
{newWorkflow === true ?
<span style={{ marginTop: 30, }}>
<span style={{ paddingTop: 30, backgroundColor: theme.palette.DialogStyle.backgroundColor }}>
<Typography variant="h6" style={{ marginLeft: 30, paddingBottom: 0, }}>
Relevant Workflows
</Typography>
+63 -50
View File
@@ -1,5 +1,5 @@
import React, { memo, useContext, useEffect, useState } from 'react';
import theme from "../theme.jsx";
import { getTheme } from "../theme.jsx";
import {
Tooltip,
Typography,
@@ -62,6 +62,9 @@ const EnvironmentTab = memo((props) => {
const [selectedSubOrg, setSelectedSubOrg] = React.useState([]);
const [showLocationActionModal, setShowLocationActionModal] = React.useState(undefined)
const { themeMode, supportEmail, brandColor } = useContext(Context);
const theme = getTheme(themeMode, brandColor);
useEffect(() => {
getEnvironments();
@@ -370,7 +373,7 @@ const EnvironmentTab = memo((props) => {
})
.catch((error) => {
toast(
"Failed dismissing alert. Please contact support@shuffler.io if this persists.",
`Failed dismissing alert. Please contact ${supportEmail} if this persists.`,
);
});
};
@@ -512,11 +515,11 @@ const EnvironmentTab = memo((props) => {
}}
>
<DialogTitle>
<span style={{ color: "white" }}>Add Location</span>
<Typography variant='h5' color="textPrimary" >Add Location</Typography>
</DialogTitle>
<DialogContent>
<div>
Location Name
<Typography variant='body2' color="textPrimary">Location Name</Typography>
<TextField
color="primary"
style={{ backgroundColor: theme.palette.textFieldStyle.backgroundColor,}}
@@ -524,7 +527,7 @@ const EnvironmentTab = memo((props) => {
InputProps={{
style: {
height: "50px",
color: "white",
color: theme.palette.textFieldStyle.color,
fontSize: "1em",
},
}}
@@ -539,19 +542,18 @@ const EnvironmentTab = memo((props) => {
}
/>
</div>
{loginInfo} {/* Assuming loginInfo is part of the relevant content */}
{loginInfo}
</DialogContent>
<DialogActions>
<Button
style={{ borderRadius: "2px", fontSize: 16, textTransform: "none", color: "#ff8544" }}
style={{ borderRadius: "2px", fontSize: 16, textTransform: "none", color: theme.palette.primary.main, }}
onClick={() => setModalOpen(false)}
color="primary"
>
Cancel
</Button>
<Button
variant="contained"
style={{ borderRadius: "2px", fontSize: 16, textTransform: "none", backgroundColor: "#ff8544", color: "#1a1a1a" }}
style={{ borderRadius: "2px", fontSize: 16, textTransform: "none", }}
onClick={() => {
submitEnvironment(modalUser); // Assuming modalUser is available
}}
@@ -733,9 +735,9 @@ const EnvironmentTab = memo((props) => {
}}
>
<DialogTitle>
<div style={{ color: "rgba(255,255,255,0.9)" }}>
<Typography variant='h5' color="textPrimary" >
Select sub-org to distribute Environments
</div>
</Typography>
</DialogTitle>
<DialogContent style={{ color: "rgba(255,255,255,0.65)" }}>
<MenuItem value="none" onClick={()=> {handleSelectSubOrg(null, "none")}}>None</MenuItem>
@@ -778,7 +780,7 @@ const EnvironmentTab = memo((props) => {
<div style={{ display: "flex", marginTop: 20 }}>
<Button
style={{ borderRadius: "2px", textTransform: 'none', fontSize:16, color: "#ff8544" }}
style={{ borderRadius: "2px", textTransform: 'none', fontSize:16, color: theme.palette.primary.main }}
onClick={() => setShowDistributionPopup(false)}
color="primary"
>
@@ -786,7 +788,7 @@ const EnvironmentTab = memo((props) => {
</Button>
<Button
variant="contained"
style={{ borderRadius: "2px", textTransform: 'none', fontSize:16, color: "#1a1a1a", backgroundColor: "#ff8544", marginLeft: 10 }}
style={{ borderRadius: "2px", textTransform: 'none', fontSize:16, marginLeft: 10 }}
onClick={() => {
changeDistribution(selectedEnvironment, selectedSubOrg);
}}
@@ -800,27 +802,27 @@ const EnvironmentTab = memo((props) => {
) : null;
return (
<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", }}>
<div style={{ width: "100%", minHeight: 1100, boxSizing: 'border-box', padding: "27px 10px 19px 27px", height:"100%", backgroundColor: theme.palette.platformColor,borderTopRightRadius: '8px', borderBottomRightRadius: 8, borderLeft: theme.palette.defaultBorder, }}>
{modalView}
{EnvironmentDistributionModal}
<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={{ height: "100%", maxHeight: 1700, overflowY: "auto", scrollbarColor: theme.palette.scrollbarColorTransparent, scrollbarWidth: 'thin' }}>
<div style={{ height: "100%", width: "calc(100% - 20px)", scrollbarColor: theme.palette.scrollbarColorTransparent, scrollbarWidth: 'thin'}}>
<div style={{ marginBottom: 20 }}>
<h2 style={{ marginBottom: 8, marginTop: 0, color: "#ffffff" }}>Runtime Locations</h2>
<span style={{ color: textColor }}>
Decides which Orborus <b>runtime location</b> to run your workflows in. Previously called Environments. <br /> If you have scale problems, <a href="https://shuffler.io/docs/configuration#high-availability" target="_blank" rel="noopener noreferrer" style={{ color: "#FF8444" }}>check the docs</a> or talk to our team: support@shuffler.io.&nbsp;
<Typography variant='h5' color="textPrimary" style={{ marginBottom: 8, marginTop: 0,}}>Runtime Locations</Typography>
<Typography variant='body2' color="textSecondary">
Decides which Orborus <b>runtime location</b> to run your workflows in. Previously called Environments. <br /> If you have scale problems, <a href="https://shuffler.io/docs/configuration#high-availability" target="_blank" rel="noopener noreferrer" style={{ color: theme.palette.linkColor }}>check the docs</a> or talk to our team: {supportEmail}.&nbsp;
<a
target="_blank"
rel="noopener noreferrer"
href="/docs/organizations#locations"
style={{ color: "#FF8444" }}
style={{ color: theme.palette.linkColor, }}
>
Learn more
</a>
</span>
</Typography>
</div>
<Button
style={{ backgroundColor: '#ff8544', color: "#1a1a1a", borderRadius: 4, textTransform: "capitalize", fontSize: 16, }}
style={{ borderRadius: 4, textTransform: "capitalize", fontSize: 16, }}
variant="contained"
color="primary"
onClick={() => setModalOpen(true)}
@@ -828,9 +830,9 @@ const EnvironmentTab = memo((props) => {
Add Location
</Button>
<Button
style={{ backgroundColor: "#2F2F2F", borderRadius: 4, width: 81, height: 40, marginLeft: 16, marginRight: 15 }}
style={{ borderRadius: 4, width: 81, height: 40, marginLeft: 16, marginRight: 15 }}
variant="contained"
color="primary"
color="secondary"
onClick={getEnvironments}
>
<CachedIcon />
@@ -851,7 +853,7 @@ const EnvironmentTab = memo((props) => {
style={{
borderRadius: 4,
marginTop: 24,
border: "1px solid #494949",
border: theme.palette.defaultBorder,
width: "100%",
overflowX: "auto",
paddingBottom: 0,
@@ -873,7 +875,7 @@ const EnvironmentTab = memo((props) => {
width: "100%",
minWidth: 800,
paddingBottom: 0,
borderBottom: "1px solid #494949",
borderBottom: theme.palette.defaultBorder,
}}
>
{["Type", "Status", "Scale", "Pipeline", "Name", "Type", "Queue", "Actions", "Distribution"].map((header, index) => {
@@ -901,7 +903,7 @@ const EnvironmentTab = memo((props) => {
style={{
display: "grid",
gridTemplateColumns: "80px 80px 80px 120px 120px 120px 120px 350px 150px",
backgroundColor: "#212121",
backgroundColor: theme.palette.platformColor,
height: 40,
width: "100%",
boxSizing: "border-box",
@@ -919,7 +921,7 @@ const EnvironmentTab = memo((props) => {
variant="text"
animation="wave"
sx={{
backgroundColor: "#1a1a1a",
backgroundColor: theme.palette.loaderColor,
borderRadius: "4px",
}}
/>
@@ -941,9 +943,9 @@ const EnvironmentTab = memo((props) => {
return null;
}
var bgColor = "#212121";
var bgColor = themeMode === "dark" ? "#212121" : "#FFFFFF";
if (index % 2 === 0) {
bgColor = "#1A1A1A";
bgColor = themeMode === "dark" ? "#1A1A1A" : "#EAEAEA";
}
// Check if there's a notification for it in userdata.priorities
@@ -1013,7 +1015,9 @@ const EnvironmentTab = memo((props) => {
environment.name === "Cloud" ? (
<Tooltip title="Cloud" placement="top">
<CloudIcon
style={{ color: "rgba(255,255,255,0.8)" }}
style={{
color: themeMode === "dark" ? "#CCCCCC" : "#333333",
}}
/>
</Tooltip>
) : environment.run_type === "docker" ? (
@@ -1075,7 +1079,7 @@ const EnvironmentTab = memo((props) => {
:
<span>IP / label: {environment?.running_ip?.split(":")[0]}. May stay running up to a minute after stopping Orborus.</span>
:
"Cloud is automatically configured. Reachout to support@shuffler.io if you have any questions."
`Cloud is automatically configured. Reachout to ${supportEmail} if you have any questions.`
}
<br />
@@ -1169,7 +1173,7 @@ const EnvironmentTab = memo((props) => {
<ListItemText
primary={
environment.Type === "cloud" ?
<Tooltip title={"Make a new environment to set up a Datalake node. Please contact support@shuffler.io if this is something you want to see on Cloud directly."} placement="top">
<Tooltip title={`Make a new environment to set up a Datalake node. Please contact ${supportEmail} if this is something you want to see on Cloud directly.`} placement="top">
<CancelIcon style={{ color: "rgba(255,255,255,0.3)" }} />
</Tooltip>
:
@@ -1267,13 +1271,12 @@ const EnvironmentTab = memo((props) => {
/>
<ListItemText
style={{
minWidth: 300,
overflow: "hidden",
minWidth: 350,
}}
>
<div style={{ display: "flex", flexWrap: "nowrap" }}>
<ButtonGroup
style={{ borderRadius: "5px 5px 5px 5px", flexWrap: "nowrap" }}
style={{ borderRadius: "5px 5px 5px 5px", flexWrap: "nowrap", width: "100%" }}
>
<Button
variant="outlined"
@@ -1369,7 +1372,7 @@ const EnvironmentTab = memo((props) => {
</ButtonGroup>
<IconButton disabled={environment.Type === "cloud"} onClick={()=> {setIsExpanded(prev => !prev)}}>
{listItemExpanded === index ? <ExpandLessIcon /> : <ExpandMoreIcon />}
{listItemExpanded === index ? <ExpandLessIcon sx={{color: theme.palette.text.primary}} /> : <ExpandMoreIcon sx={{color: theme.palette.text.primary}}/>}
</IconButton>
</div>
</ListItemText>
@@ -1443,7 +1446,7 @@ const EnvironmentTab = memo((props) => {
>
<Tab
value={0}
label=<span>
label=<span style={{color: theme.palette.text.secondary, }}>
<img
src="/icons/docker.svg"
style={{ width: 20, height: 20, marginRight: 10, }}
@@ -1452,32 +1455,32 @@ const EnvironmentTab = memo((props) => {
/>
<Tab
value={1}
label=<span>
label=<span style={{color: theme.palette.text.secondary, }}>
<img
src="/icons/docker.svg"
style={{ width: 20, height: 20, marginRight: 10, }}
style={{ width: 20, height: 20, marginRight: 10,}}
/> Scale
</span>
/>
<Tab
value={2}
label=<span>
label=<span style={{color: theme.palette.text.secondary, }}>
<img
src="/icons/k8s.svg"
style={{ width: 20, height: 20, marginRight: 10, }}
style={{ width: 20, height: 20, marginRight: 10 }}
/> k8s
</span>
/>
</Tabs>
<Typography variant="body1" color="textSecondary" style={{marginTop: 15, }}>
{installationTab === 2 ?
<span>
<Typography variant='body2' color="textSecondary">
Check our <a href="https://docs.docker.com/get-started/get-docker/" target="_blank" rel="noopener noreferrer" style={{textDecoration: "none", color: "#f85a3e",}}>Kubernetes documentation</a> for more information on how to run Shuffle on Kubernetes. The status of the node will change when connected.
</span>
</Typography>
:
<span>
<Typography variant='body2' color="textSecondary">
1. <a href="https://docs.docker.com/get-started/get-docker/" target="_blank" rel="noopener noreferrer" style={{textDecoration: "none", color: "#f85a3e",}}>Ensure Docker is installed</a> and the target server can reach '{globalUrl}'
</span>
</Typography>
}
</Typography>
@@ -1501,13 +1504,19 @@ const EnvironmentTab = memo((props) => {
>
<div style={{ display: "flex", position: "relative", }}>
<code
contenteditable="true"
contentEditable="true"
id="orborus_command"
style={{
// Wrap if larger than X
whiteSpace: "pre-wrap",
overflow: "auto",
marginRight: 30,
backgroundColor: themeMode === "dark" ? "#1e1e1e" : "#f5f5f5",
color: themeMode === "dark" ? "#f8f8f2" : "#333",
padding: "8px",
borderRadius: "4px",
fontFamily: "monospace",
fontSize: 18,
border: themeMode === "dark" ? "1px solid #555" : "1px solid #ddd",
}}
>
{getOrborusCommand(environment)}
@@ -1532,7 +1541,8 @@ const EnvironmentTab = memo((props) => {
</div>
<Divider style={{marginTop: 25, marginBottom: 10, }}/>
Configure HTTP Proxies: <Checkbox
<div style={{display: 'flex', alignItems: 'center', }}>
<Typography variant='body2' color="textSecondary">Configure HTTP Proxies:</Typography> <Checkbox
id="shuffle_skip_proxies"
onClick={() => {
if (commandController.proxies === undefined) {
@@ -1545,8 +1555,10 @@ const EnvironmentTab = memo((props) => {
setUpdate(Math.random())
}}
/>
</div>
<div />
Disable Pipelines & Data Lake: <Checkbox
<div style={{display: 'flex', alignItems: 'center', }}>
<Typography variant='body2' color="textSecondary">Disable Pipelines & Data Lake:</Typography> <Checkbox
id="shuffle_skip_pipelines"
onClick={() => {
if (commandController.pipelines === undefined) {
@@ -1559,6 +1571,7 @@ const EnvironmentTab = memo((props) => {
}}
/>
</div>
</div>
}
<Typography variant="body1" color="textSecondary" style={{marginTop: 15, }}>
+107 -47
View File
@@ -43,7 +43,7 @@ import {
import Dropzone from "../components/Dropzone.jsx";
import ShuffleCodeEditor from "../components/ShuffleCodeEditor1.jsx";
import theme from "../theme.jsx";
import {getTheme} from "../theme.jsx";
import { Context } from "../context/ContextApi.jsx";
const Files = memo((props) => {
@@ -58,6 +58,8 @@ const Files = memo((props) => {
const [openEditor, setOpenEditor] = React.useState(false);
const [renderTextBox, setRenderTextBox] = React.useState(false);
const [loadFileModalOpen, setLoadFileModalOpen] = React.useState(false);
const { themeMode, brandColor } = useContext(Context);
const theme = getTheme(themeMode, brandColor);
const [field1, setField1] = React.useState("");
const [field2, setField2] = React.useState("");
@@ -442,7 +444,7 @@ const Files = memo((props) => {
</DialogContent>
<DialogActions>
<Button
style={{ borderRadius: "2px", textTransform: 'none', fontSize:16, color: "#ff8544" }}
style={{ borderRadius: "2px", textTransform: 'none', fontSize:16, color: theme.palette.primary.main }}
onClick={() => setLoadFileModalOpen(false)}
color="primary"
>
@@ -450,7 +452,7 @@ const Files = memo((props) => {
</Button>
<Button
variant="contained"
style={{ borderRadius: "2px", textTransform: 'none', fontSize:16, color: "#1a1a1a", backgroundColor: "#ff8544" }}
style={{ borderRadius: "2px", textTransform: 'none', fontSize:16, }}
disabled={downloadUrl.length === 0 || !downloadUrl.includes("http")}
onClick={() => {
handleGithubValidation();
@@ -517,9 +519,9 @@ const Files = memo((props) => {
}}
>
<DialogTitle>
<div style={{ color: "rgba(255,255,255,0.9)" }}>
<Typography variant="h5" color="textPrimary" >
Select sub-org to distribute files
</div>
</Typography>
</DialogTitle>
<DialogContent style={{ color: "rgba(255,255,255,0.65)" }}>
<MenuItem value="none" onClick={()=> {handleSelectSubOrg(null, "none")}}>None</MenuItem>
@@ -562,15 +564,14 @@ const Files = memo((props) => {
<div style={{ display: "flex", marginTop: 20 }}>
<Button
style={{ borderRadius: "2px", textTransform: 'none', fontSize:16, color: "#ff8544" }}
style={{ borderRadius: "2px", textTransform: 'none', fontSize:16, color: theme.palette.primary.main }}
onClick={() => setShowDistributionPopup(false)}
color="primary"
>
Cancel
</Button>
<Button
variant="contained"
style={{ borderRadius: "2px", textTransform: 'none', fontSize:16, color: "#1a1a1a", backgroundColor: "#ff8544", marginLeft: 10 }}
style={{ borderRadius: "2px", textTransform: 'none', fontSize:16, marginLeft: 10 }}
onClick={() => {
changeDistribution(fileIdSelectedForDistribution, selectedSubOrg);
}}
@@ -906,27 +907,27 @@ const Files = memo((props) => {
onDrop={uploadFile}
>
{fileDistributionModal}
<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", }}>
<div style={{width: "100%", minHeight: 1100, boxSizing: 'border-box', padding: "27px 10px 19px 27px", height:"100%", backgroundColor: theme.palette.platformColor,borderTopRightRadius: '8px', borderBottomRightRadius: 8, borderLeft: theme.palette.defaultBorder, }}>
<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={{height: "100%", maxHeight: 1700,overflowY: 'auto', scrollbarColor: theme.palette.scrollbarColorTransparent, scrollbarWidth: 'thin'}}>
<div style={{ height: "100%", width: "calc(100% - 20px)", scrollbarColor: theme.palette.scrollbarColorTransparent, scrollbarWidth: 'thin' }}>
<DownloadFileIcon setLoadFileModalOpen={setLoadFileModalOpen} isSelectedFiles={isSelectedFiles} />
{fileDownloadModal}
<div style={{ marginTop: isSelectedFiles ? 2: 20, marginBottom:20 }}>
<h2 style={{ display: isSelectedFiles ? null : "inline", marginTop: isSelectedFiles?0:null, marginBottom: isSelectedFiles?8:null, color: "#FFFFFF"}}>Files</h2>
<span style={{ marginLeft: isSelectedFiles ? null : 25, color:isSelectedFiles?"#9E9E9E":null}}>
<Typography variant="h5" color="textPrimary" style={{ display: isSelectedFiles ? null : "inline", marginTop: isSelectedFiles?0:null, marginBottom: isSelectedFiles?8:null, fontWeight: 500}}>Files</Typography>
<Typography variant="body2" color="textSecondary" style={{ marginLeft: isSelectedFiles ? null : 25,}}>
Files from Workflows are a way to store as well as edit files.{" "}
<a
target="_blank"
rel="noopener noreferrer"
href="https://shuffler.io/docs/organizations#files"
style={{ textDecoration: isSelectedFiles ? null:"none", color: isSelectedFiles? "#FF8444": "#f85a3e" }}
style={{ textDecoration: isSelectedFiles ? null:"none", color: theme.palette.linkColor }}
>
Learn more
</a>
</span>
</Typography>
</div>
@@ -937,7 +938,7 @@ const Files = memo((props) => {
onClick={() => {
upload.click();
}}
style={{backgroundColor: isSelectedFiles?'#ff8544':null, color:isSelectedFiles?"#212121":null, textTransform: 'none',fontSize: 16, borderRadius:isSelectedFiles?4:null, width:isSelectedFiles?143:null, height:isSelectedFiles?35:null, boxShadow: isSelectedFiles?'none':null,}}
style={{ textTransform: 'none',fontSize: 16, borderRadius:isSelectedFiles?4:null, width:isSelectedFiles?143:null, height:isSelectedFiles?35:null, boxShadow: isSelectedFiles?'none':null,}}
>
Upload files
</Button>
@@ -958,9 +959,9 @@ const Files = memo((props) => {
}}
/>
<Button
style={{ marginLeft: 16, marginRight: 15, backgroundColor:isSelectedFiles?"#2F2F2F":null,borderRadius:isSelectedFiles?4:null, width:isSelectedFiles?81:null, height:isSelectedFiles?35:null, boxShadow: isSelectedFiles?'none':null, }}
style={{ marginLeft: 16, marginRight: 15, borderRadius:isSelectedFiles?4:null, width:isSelectedFiles?81:null, height:isSelectedFiles?35:null, boxShadow: isSelectedFiles?'none':null, }}
variant="contained"
color="primary"
color="secondary"
onClick={() => getFiles(selectedCategory)}
>
<CachedIcon />
@@ -976,7 +977,6 @@ const Files = memo((props) => {
labelId="input-namespace-select-label"
id="input-namespace-select-id"
style={{
color: "white",
minWidth: 122,
maxWidth: 122,
height: 35,
@@ -1013,7 +1013,6 @@ const Files = memo((props) => {
<MenuItem
key={index}
value={data}
style={{ color: "white" }}
>
{data.replaceAll("_", " ")}
</MenuItem>
@@ -1044,8 +1043,8 @@ const Files = memo((props) => {
Please note that your selected files ({selectedFileId?.length}) will be moved to the <kbd>{updateToThisCategory}</kbd> category.
</DialogContent>
<DialogActions>
<Button onClick={() => setShowFileCategoryPopup(false)} style={{fontSize: 16, textTransform: 'none'}}>Close</Button>
<Button onClick={() => handleUpdateFileCategory(updateToThisCategory)} style={{fontSize: 16, textTransform: 'none', color: "#1a1a1a", backgroundColor: "#ff8544"}}>Update</Button>
<Button onClick={() => setShowFileCategoryPopup(false)} style={{fontSize: 16, textTransform: 'none', color: theme.palette.primary.main }}>Close</Button>
<Button variant="contained" color="primary" onClick={() => handleUpdateFileCategory(updateToThisCategory)} style={{fontSize: 16, textTransform: 'none', }}>Update</Button>
</DialogActions>
</Dialog>
</FormControl>
@@ -1055,8 +1054,9 @@ const Files = memo((props) => {
{renderTextBox ?
<Tooltip title={"Close"} style={{}} aria-label={""}>
<Button
style={{ marginLeft: 5, marginRight: 15, height: 35, borderRadius: 4, backgroundColor: "#494949", textTransform: 'none', fontSize: 16, color: "#f1f1f1" }}
color="primary"
style={{ marginLeft: 5, marginRight: 15, height: 35, borderRadius: 4, textTransform: 'none', fontSize: 16, }}
variant="contained"
color="secondary"
onClick={() => {
setRenderTextBox(false);
console.log(" close clicked")
@@ -1068,8 +1068,9 @@ const Files = memo((props) => {
:
<Tooltip title={"Add new file category"} style={{}} aria-label={""}>
<Button
style={{ marginLeft: 5, marginRight: 15, width: 169, height: 35, borderRadius: 4, backgroundColor: "#494949", textTransform: 'none', fontSize: 16, color: "#f1f1f1" }}
color="primary"
style={{whiteSpace: 'nowrap', textWrap: 'nowrap', marginLeft: 5, marginRight: 15, width: 169, height: 35, borderRadius: 4, textTransform: 'none', fontSize: 16, }}
variant="contained"
color="secondary"
onClick={() => {
setRenderTextBox(true);
}}
@@ -1096,7 +1097,8 @@ const Files = memo((props) => {
}}
InputProps={{
style: {
color: "white",
color: theme.palette.textFieldStyle.color,
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
height: 35,
fontSize: 16,
borderRadius: 4,
@@ -1133,7 +1135,7 @@ const Files = memo((props) => {
style={{
borderRadius: 4,
marginTop: 24,
border: "1px solid #494949",
border: theme.palette.defaultBorder,
width: "100%",
overflowX: "auto",
paddingBottom: 0,
@@ -1151,7 +1153,7 @@ const Files = memo((props) => {
>
<ListItem
style={{
borderBottom: "1px solid #494949" ,
borderBottom: theme.palette.defaultBorder ,
display: "table-row"
}}
>
@@ -1198,7 +1200,7 @@ const Files = memo((props) => {
padding: index === 0 ? "0px 8px 8px 15px" : "0px 8px 8px 8px",
whiteSpace: "nowrap",
textOverflow: "ellipsis",
borderBottom: "1px solid #494949",
borderBottom: theme.palette.defaultBorder,
verticalAlign: "middle"
}}
primaryTypographyProps={{
@@ -1215,7 +1217,7 @@ const Files = memo((props) => {
key={rowIndex}
style={{
display: "table-row",
backgroundColor: "#212121",
backgroundColor: theme.palette.platformColor,
}}
>
{Array(8)
@@ -1232,7 +1234,7 @@ const Files = memo((props) => {
variant="text"
animation="wave"
sx={{
backgroundColor: "#1a1a1a",
backgroundColor: theme.palette.loaderColor,
height: "20px",
borderRadius: "4px",
}}
@@ -1257,9 +1259,9 @@ const Files = memo((props) => {
return null;
}
var bgColor = isSelectedFiles ? "#212121":"#27292d";
var bgColor = themeMode === "dark" ? "#212121" : "#FFFFFF";
if (index % 2 === 0) {
bgColor = isSelectedFiles ? "#1A1A1A":"#1f2023";
bgColor = themeMode === "dark" ? "#1A1A1A" : "#EAEAEA";
}
const isDistributed = file?.suborg_distribution?.length > 0 ? true : false;
const filenamesplit = file.filename.split(".")
@@ -1425,9 +1427,20 @@ const Files = memo((props) => {
readFileData(file)
}}
>
<img src="/icons/editIcon.svg" alt="edit icon"
style={{color: iseditable ? "white" : "grey", width: 24, height: 24}}
/>
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M16.1038 4.66848C16.3158 4.45654 16.5674 4.28843 16.8443 4.17373C17.1212 4.05903 17.418 4 17.7177 4C18.0174 4 18.3142 4.05903 18.5911 4.17373C18.868 4.28843 19.1196 4.45654 19.3315 4.66848C19.5435 4.88041 19.7116 5.13201 19.8263 5.40891C19.941 5.68582 20 5.9826 20 6.28232C20 6.58204 19.941 6.87882 19.8263 7.15573C19.7116 7.43263 19.5435 7.68423 19.3315 7.89617L8.43807 18.7896L4 20L5.21038 15.5619L16.1038 4.66848Z"
stroke={themeMode=== "dark" ? "#F1F1F1" : "#333"}
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
</IconButton>
</span>
</Tooltip>
@@ -1470,15 +1483,39 @@ const Files = memo((props) => {
downloadFile(file);
}}
>
<img src="/icons/downloadIcon.svg" alt="download icon"
style={{
width: 24, height: 24,
color:
file.status === "active"
? "white"
: "grey",
}}
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<rect
width="24"
height="24"
fill={themeMode === "dark" ? "#212121" : "#EDEDED"}
fillOpacity="0.02"
/>
<path
d="M8.22595 16.4463L11.7792 19.9995L15.3324 16.4463"
stroke={file.status === "active" ? (themeMode === "dark" ? "#F1F1F1" : "black") : "grey"}
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M11.7792 12.0049V19.9997"
stroke={file.status === "active" ? (themeMode === "dark" ? "#F1F1F1" : "black") : "grey"}
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M19.6676 17.415C20.4399 16.8719 21.019 16.0968 21.321 15.2023C21.6229 14.3078 21.632 13.3403 21.3468 12.4403C21.0617 11.5402 20.4971 10.7545 19.7352 10.197C18.9732 9.6396 18.0534 9.33948 17.1092 9.34021H15.99C15.7228 8.299 15.2229 7.33196 14.5279 6.5119C13.8329 5.69184 12.961 5.04013 11.9777 4.60583C10.9944 4.17153 9.92534 3.96596 8.85109 4.00459C7.77684 4.04322 6.72535 4.32505 5.77578 4.82886C4.82621 5.33267 4.00331 6.04534 3.36902 6.9132C2.73474 7.78106 2.30559 8.78151 2.11391 9.83922C1.92222 10.8969 1.97297 11.9844 2.26236 13.0196C2.55174 14.0549 3.07221 15.011 3.78459 15.816"
stroke={file.status === "active" ? (themeMode === "dark" ? "#F1F1F1" : "black") : "grey"}
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
</IconButton>
</span>
</Tooltip>
@@ -1496,7 +1533,31 @@ const Files = memo((props) => {
toast(file.id + " copied to clipboard");
}}
>
<img src="/icons/copyIcon.svg" alt="copy icon" style={{ color: "white", width: 24, height: 24 }} />
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<rect
width="24"
height="24"
fillOpacity="1"
/>
<path
d="M14 4H7.6C7.17565 4 6.76869 4.16857 6.46863 4.46863C6.16857 4.76869 6 5.17565 6 5.6V18.4C6 18.8243 6.16857 19.2313 6.46863 19.5314C6.76869 19.8314 7.17565 20 7.6 20H17.2C17.6243 20 18.0313 19.8314 18.3314 19.5314C18.6314 19.2313 18.8 18.8243 18.8 18.4V8.8L14 4Z"
stroke={themeMode === "dark" ? "#F1F1F1" : "#333"}
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M14 4V8.8H18.8"
stroke={themeMode === "dark" ? "#F1F1F1" : "#333"}
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
</IconButton>
</Tooltip>
<Tooltip
@@ -1570,7 +1631,6 @@ const Files = memo((props) => {
disabled={userdata?.active_org?.role !== "admin" || (selectedOrganization.creator_org !== undefined && selectedOrganization.creator_org !== null && selectedOrganization.creator_org !== "" ) ? true : false}
checked={isDistributed}
style={{ }}
color="secondary"
onClick={() => {
setShowDistributionPopup(true)
if(file?.suborg_distribution?.length > 0){
File diff suppressed because it is too large Load Diff
+19 -17
View File
@@ -1,7 +1,7 @@
import React, { useState, useEffect } from "react";
import React, { useState, useEffect, useContext } from "react";
import ReactGA from 'react-ga4';
import theme from "../theme.jsx";
import {getTheme} from "../theme.jsx";
import countries from "../components/Countries.jsx";
import {
Box,
@@ -32,7 +32,7 @@ import {
import { useNavigate, Link } from "react-router-dom";
import { Autocomplete } from "@mui/material";
import { toast } from "react-toastify"
import { Context } from "../context/ContextApi.jsx";
import {
Cached as CachedIcon,
ContentCopy as ContentCopyIcon,
@@ -74,6 +74,9 @@ const LicencePopup = (props) => {
const [errorMessage, setErrorMessage] = useState("")
const [highlight, setHighlight] = useState(false)
const { themeMode } = useContext(Context);
const theme = getTheme(themeMode);
// Cloud
const [calculatedApps, setCalculatedApps] = useState(600)
const [calculatedCost, setCalculatedCost] = useState("$600")
@@ -145,11 +148,12 @@ const LicencePopup = (props) => {
padding: 20,
paddingBottom: 30,
borderRadius: theme.palette?.borderRadius,
height: "100%"
height: "100%",
}
const userInScalePlan = userdata?.app_execution_limit > 10000
const appRuns = userInScalePlan ? (userdata?.app_execution_limit / 1000) + "K App Runs" : userdata?.app_execution_limit === 10000 ? "10,000 App Runs" : "2,000 App Runs"
const userInScalePlan = userdata?.app_execution_limit > 2000
const appRuns = (userdata?.app_execution_limit / 1000) + "K App Runs"
// Add this function to format the limit value
const formatLimit = (limit) => {
@@ -277,7 +281,7 @@ const LicencePopup = (props) => {
}
};
console.log("selectedOrganization: ", selectedOrganization)
// Update the subscription features section
billingInfo.subscription = {
"active": true,
@@ -480,7 +484,6 @@ const LicencePopup = (props) => {
});
}
console.log("OrgSyncFeatures: ", selectedOrganization?.sync_features)
const extraFeatures = Object.entries(features || {})
.filter(([_, featureData]) => {
@@ -503,9 +506,9 @@ const LicencePopup = (props) => {
style={{ borderRadius: theme.palette?.borderRadius, }}
placement="bottom"
>
<div style={{ backgroundColor: "#1e1e1e", border: "1.2px solid #ff8544", borderRadius: theme.palette?.borderRadius}}>
<Paper
style={newPaperstyle}
<div style={{ backgroundColor: theme.palette.cardBackgroundColor, border: "1.2px solid #ff8544", borderRadius: theme.palette?.borderRadius}}>
<div
style={{ backgroundColor: theme.palette.cardBackgroundColor, ...newPaperstyle }}
// onMouseEnter={() => setHovered(true)}
// onMouseLeave={() => setHovered(false)}
>
@@ -591,9 +594,9 @@ const LicencePopup = (props) => {
</div>
</DialogContent>
</Dialog>
{subscription.active === true && !isScale && <Button style={{ backgroundColor: '#2f2f2f', color: "#ffffff", textTransform: "capitalize", borderRadius: 200, boxShadow: 'none', fontSize: 13 }}
{subscription.active === true && !isScale && <Button style={{ textTransform: "capitalize", borderRadius: 200, boxShadow: 'none', fontSize: 13 }}
variant="contained"
color="primary">
color="secondary">
Current Plan
</Button>}
<div style={{ display: "flex" }}>
@@ -849,13 +852,12 @@ const LicencePopup = (props) => {
<Button
fullWidth
color="primary"
variant="contained"
style={{
marginTop: !userdata.has_card_available ? 25 : 10,
borderRadius: 4,
height: 40,
fontSize: 16,
color: "#1a1a1a",
backgroundColor: "#ff8544",
// backgroundImage: userdata.has_card_available ? null : "linear-gradient(to right, #f86a3e, #f34079)",
textTransform: "none",
@@ -873,13 +875,13 @@ const LicencePopup = (props) => {
</Button> ) : null}
<Button
variant="outlined"
color="primary"
style={{
marginTop: isCloud? 10 : 30,
borderRadius: 4,
width: "100%",
cursor: "pointer",
textTransform: "capitalize",
backgroundColor: "transparent",
fontSize: 16,
position: "relative", // Required for positioning tooltip
}}
@@ -922,7 +924,7 @@ const LicencePopup = (props) => {
</span>
</Button>
</Paper>
</div>
{/*
<div style={{ paddingRight: 150, display: "flex", alignItems: "baseline" }}>
+112 -54
View File
@@ -1,5 +1,6 @@
import React, { useContext, useEffect, useState } from "react";
import { Link, useNavigate } from "react-router-dom";
import {
AppBar,
Box,
@@ -36,6 +37,7 @@ import AddIcon from '@mui/icons-material/Add';
import Mousetrap from "mousetrap";
import LicencePopup from "../components/LicencePopup.jsx";
import { Context } from "../context/ContextApi.jsx";
import { getTheme } from "../theme.jsx";
const curpath = (typeof window !== "undefined" && window.location && typeof window.location.pathname === "string")
? window.location.pathname
@@ -118,18 +120,18 @@ const menuData = {
label: "training_click"
}
},
{
title: "Security Consultation",
description:
"Automate your infrastructure with expert guidance and tailored solutions.",
icon: "/images/SecurityConsultation.svg",
path: "/contact?category=security_consultation",
gaData: {
category: "navbar",
action: "services_click",
label: "security_consultation_click"
}
},
// {
// title: "Security Consultation",
// description:
// "Automate your infrastructure with expert guidance and tailored solutions.",
// icon: "/images/SecurityConsultation.svg",
// path: "/contact?category=security_consultation",
// gaData: {
// category: "navbar",
// action: "services_click",
// label: "security_consultation_click"
// }
// },
],
Resources: {
columns: [
@@ -751,7 +753,9 @@ const Navbar = (props) => {
const topbar_var = "topbar_closed10"
const theme = useTheme();
const {searchBarModalOpen, setSearchBarModalOpen, isDocSearchModalOpen} = useContext(Context)
const {themeMode} = useContext(Context);
const currentTheme = getTheme(themeMode);
const {searchBarModalOpen, setSearchBarModalOpen, isDocSearchModalOpen, setIsDocSearchModalOpen} = useContext(Context)
const [pricingModalOpen, setPricingModalOpen] = useState(false);
const isTabletOrMobile = useMediaQuery(theme.breakpoints.down("lg"));
const isMobile = useMediaQuery(theme.breakpoints.down("md"));
@@ -772,7 +776,6 @@ const Navbar = (props) => {
window.location.host === "shuffler.io" ||
window.location.host === "localhost:5002";
const stripeKey = typeof window === 'undefined' || window.location === undefined ? "" : window.location.origin === "https://shuffler.io" ? "pk_live_51PXYYMEJjT17t98N20qEqItyt1fLQjrnn41lPeG2PjnSlZHTDNKHuisAbW00s4KAn86nGuqB9uSVU4ds8MutbnMU00DPXpZ8ZD" : "pk_test_51PXYYMEJjT17t98NbDkojZ3DRvsFUQBs35LGMx3i436BXwEBVFKB9nCvHt0Q3M4MG3dz4mHheuWvfoYvpaL3GmsG00k1Rb2ksO"
useEffect(() => {
@@ -853,51 +856,54 @@ const Navbar = (props) => {
setSearchBarModalOpen(false);
}}
PaperProps={{
style: {
sx: {
color: "white",
minWidth: 750,
height: 785,
borderRadius: 16,
minWidth: "750px",
height: "785px",
borderRadius: "16px",
border: "1px solid var(--Container-Stroke, #494949)",
background: "var(--Container, #000000)",
background: currentTheme.palette.DialogStyle.backgroundColor,
boxShadow: "0px 16px 24px 8px rgba(0, 0, 0, 0.25)",
},
'& .MuiDialogContent-root': {
backgroundColor: currentTheme?.palette?.DialogStyle?.backgroundColor,
},
'& .MuiDialogTitle-root': {
backgroundColor: currentTheme?.palette?.DialogStyle?.backgroundColor,
},
}}
sx={{
zIndex: 50005,
'& .MuiBackdrop-root': {
backgroundColor: 'rgba(0, 0, 0, 0.8)',
},
}}
>
<Box sx={{ display: "flex", justifyContent: "space-between", alignItems: "center", px: 2, pt: 2 }}>
<DialogTitle
sx={{
color: "var(--Paragraph-text, #C8C8C8)",
p: 0,
m: 0,
fontFamily: theme.typography.fontFamily,
}}
>
Search for Docs, Apps, Workflows and more
</DialogTitle>
<IconButton
onClick={() => setSearchBarModalOpen(false)}
sx={{
color: 'white',
'&:hover': {
backgroundColor: 'rgba(255, 255, 255, 0.1)'
}
}}
>
<CloseIcon />
</IconButton>
</Box>
<DialogContent >
<Box sx={{ display: "flex", justifyContent: "space-between",backgroundColor: currentTheme?.palette?.DialogStyle?.backgroundColor, alignItems: "center", px: 2, pt: 2 }}>
<DialogTitle
sx={{
color: "var(--Paragraph-text, #C8C8C8)",
p: 0,
m: 0,
fontFamily: theme.typography.fontFamily,
}}
>
Search for Docs, Apps, Workflows and more
</DialogTitle>
<IconButton
onClick={() => setSearchBarModalOpen(false)}
sx={{
color: 'white',
'&:hover': {
backgroundColor: 'rgba(255, 255, 255, 0.1)'
}
}}
>
<CloseIcon />
</IconButton>
</Box>
<DialogContent style={{backgroundColor: currentTheme?.palette?.DialogStyle?.backgroundColor}} >
<Box sx={{ pt: 3 }}>
<SearchBox globalUrl={globalUrl} serverside={serverside} userdata={userdata} />
</Box>
</DialogContent>
</DialogContent>
<Divider sx={{ backgroundColor: 'rgba(255, 255, 255, 0.1)' }}/>
</Dialog>
);
@@ -974,6 +980,10 @@ const Navbar = (props) => {
transform: "rotate(180deg)",
},
},
// Add this to hide the ripple effect's container
"& .MuiTouchRipple-root": {
display: "none",
},
};
// Render menu content based on type
@@ -1362,6 +1372,8 @@ const Navbar = (props) => {
</Box>
))}
<Button
component={Link}
to="/pricing"
sx={buttonStyles}
onClick={() => {
if(isCloud) {
@@ -1380,6 +1392,8 @@ const Navbar = (props) => {
Pricing
</Button>
<Button
component={Link}
to="/partners"
sx={buttonStyles}
onClick={() => {
if(isCloud) {
@@ -1451,10 +1465,12 @@ const Navbar = (props) => {
useEffect(() => {
Mousetrap.bind(['command+k', 'ctrl+k'], () => {
setSearchBarModalOpen(true);
setIsDocSearchModalOpen(false);
return false; // Prevent the default action
});
Mousetrap.bind(['esc'], () => {
setSearchBarModalOpen(false);
setIsDocSearchModalOpen(false);
return false; // Prevent the default action
});
@@ -1537,9 +1553,10 @@ const Navbar = (props) => {
const topbar = !isCloud || !showTopbar ? null :
curpath === "/" || curpath.includes("/docs") || curpath === "/pricing" || curpath === "/contact" || curpath === "/search" || curpath === "/usecases" || curpath === "/training" || curpath === "/professional-services" ?
<span style={{ zIndex: 50001, marginTop: -4}}>
<div style={{ position: "relative", height: topbarHeight, backgroundImage: "linear-gradient(to right, #f86a3e, #f34079)", overflow: "hidden", }}>
{/* uncommit this to show topbar for release */}
{/* <div style={{ position: "relative", height: topbarHeight, backgroundImage: "linear-gradient(to right, #f86a3e, #f34079)", overflow: "hidden", }}>
<Typography style={{ paddingTop: 7, fontSize:16, margin: "auto", textAlign: "center", color: "white", }}>
{/* Shuffle 1.4.0 is out! Read more about&nbsp; */}
// Shuffle 1.4.0 is out! Read more about&nbsp;
Shuffle 2.0.0 is out now!&nbsp;
<u>
<span onClick={() => {
@@ -1564,7 +1581,37 @@ const Navbar = (props) => {
}}>
<CloseIcon />
</IconButton>
</div>
</div> */}
{/* commit below div if we need to show release stuff */}
<div style={{ position: "relative", height: topbarHeight, backgroundImage: "linear-gradient(to right, #f86a3e, #f34079)", overflow: "hidden", }}>
<Typography style={{ paddingTop: 7, fontSize:16, margin: "auto", textAlign: "center", color: "white", }}>
{/* Shuffle 1.4.0 is out! Read more about&nbsp; */}
New&nbsp;
<u>
<span onClick={() => {
ReactGA.event({
category: "landingpage",
action: "click_header_training",
label: "",
})
navigate("/training")
}} style={{ cursor: "pointer", textDecoration: "none", fontWeight: 600, color: "rgba(255,255,255,0.9)" }}>
Public Training
</span>
</u>
&nbsp;Dates Released!
</Typography>
<IconButton color="secondary" style={{ position: "absolute", top: 0, right: 20, }} onClick={(event) => {
setShowTopbar(false)
// Set storage that it's clicked
localStorage.setItem(topbar_var, "true")
}}>
<CloseIcon />
</IconButton>
</div>
</span>
:
null
@@ -1784,7 +1831,10 @@ const Navbar = (props) => {
<Box sx={{ display: "flex", alignItems: "center", gap: 1 }}>
<IconButton
sx={{ color: "white" }}
onClick={() => setSearchBarModalOpen(true)}
onClick={() => {
setSearchBarModalOpen(true);
setIsDocSearchModalOpen(false);
}}
>
<SearchIcon sx={{ fontSize: 22 }} />
</IconButton>
@@ -1866,6 +1916,7 @@ const Navbar = (props) => {
})
}
setSearchBarModalOpen(true);
setIsDocSearchModalOpen(false);
}}
>
<SearchIcon sx={{ fontSize: 24 }} />
@@ -1910,6 +1961,7 @@ const Navbar = (props) => {
padding: "8px 20px",
"&:hover": {
backgroundColor: "#494949",
color: "white",
border: "1px solid white",
},
}}
@@ -2263,6 +2315,7 @@ const Navbar = (props) => {
sx={{ width: 20, height: 20 }}
/>
<Typography sx={{
color: "inherit",
fontSize: '14px',
fontFamily: theme.typography.fontFamily
}}>
@@ -2304,6 +2357,7 @@ const Navbar = (props) => {
/>
<Typography sx={{
fontSize: '14px',
color: "inherit",
fontFamily: theme.typography.fontFamily
}}>
Settings
@@ -2344,6 +2398,7 @@ const Navbar = (props) => {
/>
<Typography sx={{
fontSize: '14px',
color: "inherit",
fontFamily: theme.typography.fontFamily
}}>
Notifications ({notifications === undefined || notifications === null ? 0 :
@@ -2388,6 +2443,7 @@ const Navbar = (props) => {
/>
<Typography sx={{
fontSize: '14px',
color: "inherit",
fontFamily: theme.typography.fontFamily
}}>
About
@@ -2427,6 +2483,7 @@ const Navbar = (props) => {
/>
<Typography sx={{
fontSize: '14px',
color: "inherit",
fontFamily: theme.typography.fontFamily
}}>
Logout
@@ -2457,7 +2514,7 @@ const Navbar = (props) => {
margin: 0
}}
>
Version 1.4.5
Version 2.0.2
</Typography>
</Box>
</Menu>
@@ -2503,6 +2560,7 @@ const Navbar = (props) => {
})
}
setSearchBarModalOpen(true);
setIsDocSearchModalOpen(false);
}}
>
<SearchIcon sx={{ fontSize: 24 }} />
@@ -2543,14 +2601,14 @@ const Navbar = (props) => {
{isCloud &&
<Button
variant="outlined"
variant="contained"
color="secondary"
sx={{
...sharedButtonStyles,
display: {
xs: "none",
lg: "block"
},
color: "white",
backgroundColor: "#2F2F2F",
border: "1px solid #2F2F2F",
borderRadius: "8px",
+8 -5
View File
@@ -1,7 +1,8 @@
import React, { useRef, useState, useEffect, useLayoutEffect } from "react";
import React, { useRef, useState, useEffect, useLayoutEffect, useContext } from "react";
import { Context } from "../context/ContextApi.jsx";
import { toast } from 'react-toastify';
import { useParams, useNavigate, Link } from "react-router-dom";
import theme from '../theme.jsx';
import { getTheme } from '../theme.jsx';
//import { useAlert
import { v4 as uuidv4 } from "uuid";
@@ -112,6 +113,8 @@ const AuthenticationOauth2 = (props) => {
authenticationType.client_secret !== null &&
authenticationType.client_secret.length > 0
);
const {themeMode, brandColor} = useContext(Context);
const theme = getTheme(themeMode, brandColor);
const [clientId, setClientId] = React.useState(defaultConfigSet ? authenticationType.client_id : "");
const [clientSecret, setClientSecret] = React.useState(defaultConfigSet ? authenticationType.client_secret : "");
@@ -724,7 +727,7 @@ const AuthenticationOauth2 = (props) => {
style={{ margin: 4, minHeight: 30, maxHeight: 30, borderRadius: theme.palette?.borderRadius, }}
src={selectedAction.large_image}
/>
<Typography style={{ margin: 0, marginLeft: 10, marginTop: 5,}} variant="body1">
<Typography style={{ margin: 0, marginLeft: 10, marginTop: 5, color: "#2f2f2f",}} variant="body1">
One-click Login
</Typography>
</span>
@@ -738,7 +741,7 @@ const AuthenticationOauth2 = (props) => {
return (
<div>
<DialogTitle>
<div style={{ color: "white" }}>
<div style={{ color: theme.palette.text.primary }}>
Authenticate {selectedApp.name.replaceAll("_", " ")}
</div>
</DialogTitle>
@@ -749,7 +752,7 @@ const AuthenticationOauth2 = (props) => {
target="_blank"
rel="norefferer"
href="/docs/apps#authentication"
style={{ textDecoration: "none", color: "#f85a3e" }}
style={{ textDecoration: "none", color: theme.palette.linkColor}}
>
{" "}
Learn more about Oauth2 with Shuffle
+15 -13
View File
@@ -1,8 +1,8 @@
import React, { useEffect, useState } from "react";
import theme from "../theme.jsx";
import React, { useEffect, useState, useContext } from "react";
import {getTheme} from "../theme.jsx";
import { makeStyles } from "@mui/styles";
import { toast } from 'react-toastify';
import { Context } from "../context/ContextApi.jsx";
import {
Tooltip,
TextField,
@@ -69,6 +69,8 @@ const OrgHeader = (props) => {
selectedOrganization.description
);
const { themeMode } = useContext(Context)
const {theme} = getTheme(themeMode)
const [file, setFile] = React.useState("");
const [fileBase64, setFileBase64] = React.useState(
@@ -364,10 +366,10 @@ const OrgHeader = (props) => {
>
<FormControl>
<DialogTitle>
<div style={{ color: "rgba(255, 255, 255, 0.9)" }}>Upload Organization Image</div>
<div style={{ color: theme?.palette?.textColor}}>Upload Organization Image</div>
</DialogTitle>
{errorText}
<DialogContent style={{ color: "rgba(255, 255, 255, 0.65)" }}>
<DialogContent style={{ color: theme?.palette?.textColor }}>
<AvatarEditor
ref={setEditorRef}
image={croppedData}
@@ -389,7 +391,7 @@ const OrgHeader = (props) => {
style={appIconStyle}
onClick={() => { upload.click(); }}
>
<AddAPhotoOutlinedIcon style={{ color: "rgba(255, 255, 255, 0.9)" }} />
<AddAPhotoOutlinedIcon style={{ color: theme?.palette?.textColor }} />
</Button>
</Tooltip>
<Tooltip title="Zoom In">
@@ -399,7 +401,7 @@ const OrgHeader = (props) => {
style={appIconStyle}
onClick={zoomIn}
>
<ZoomInOutlinedIcon style={{ color: "rgba(255, 255, 255, 0.9)" }} />
<ZoomInOutlinedIcon style={{ color: theme?.palette?.textColor }} />
</Button>
</Tooltip>
<Tooltip title="Zoom Out">
@@ -409,7 +411,7 @@ const OrgHeader = (props) => {
style={appIconStyle}
onClick={zoomOut}
>
<ZoomOutOutlinedIcon style={{ color: "rgba(255, 255, 255, 0.9)" }} />
<ZoomOutOutlinedIcon style={{ color: theme?.palette?.textColor }} />
</Button>
</Tooltip>
<Tooltip title="Rotate">
@@ -419,7 +421,7 @@ const OrgHeader = (props) => {
style={appIconStyle}
onClick={rotation}
>
<LoopIcon style={{ color: "rgba(255, 255, 255, 0.9)" }} />
<LoopIcon style={{ color: theme?.palette?.textColor }} />
</Button>
</Tooltip>
</div>
@@ -427,7 +429,7 @@ const OrgHeader = (props) => {
</DialogContent>
<DialogActions>
<Button
style={{ borderRadius: "2px", fontSize: 16, textTransform: "none", color: "rgba(255, 255, 255, 0.9)" }}
style={{ borderRadius: "2px", fontSize: 16, textTransform: "none", color: theme?.palette?.textColor }}
onClick={onCancelSaveAppIcon}
>
Cancel
@@ -491,7 +493,7 @@ const OrgHeader = (props) => {
<div style={{ marginLeft: 16, alignContent: "center" }}>
<div >
<Button
style={{ backgroundColor: '#ff8544', fontSize: 16, textTransform: 'capitalize', color: "#212121", boxShadow: "none", borderRadius: 4, width: 128, height: 40 }}
style={{ fontSize: 16, textTransform: 'capitalize', boxShadow: "none", borderRadius: 4, width: 128, height: 40 }}
variant="contained"
color="primary"
onClick={() => {
@@ -503,9 +505,9 @@ const OrgHeader = (props) => {
</div>
<div>
<Button
style={{ backgroundColor: '#494949', fontSize: 16, textTransform: 'capitalize', color: "#ffffff", boxShadow: "none", marginTop: 20, borderRadius: 4, width: 128, height: 40 }}
variant="contained"
color="primary"
color="secondary"
style={{ fontSize: 16, textTransform: 'capitalize', boxShadow: "none", marginTop: 20, borderRadius: 4, width: 128, height: 40 }}
onClick={() => removeImage()}
>
Remove
@@ -1,8 +1,9 @@
import React, { memo, useEffect, useState } from "react";
import React, { memo, useEffect, useState, useContext } from "react";
import { makeStyles } from "@mui/styles";
import { toast } from "react-toastify"
import theme from '../theme.jsx';
import { getTheme } from '../theme.jsx';
import { Context } from "../context/ContextApi.jsx";
//import { useAlert
import {
@@ -89,6 +90,8 @@ const OrgHeaderexpandedNew = (props) => {
);
const [openNotification, setOpenNotification] = React.useState(false);
const { themeMode, supportEmail, brandColor } = useContext(Context);
const theme = getTheme(themeMode, brandColor);
const handleStatusChange = (event) => {
const { value } = event.target;
@@ -178,7 +181,7 @@ const OrgHeaderexpandedNew = (props) => {
const [uploadRepo, setUploadRepo] = React.useState(selectedOrganization.defaults === undefined ? "" : selectedOrganization.defaults.workflow_upload_repo === undefined || selectedOrganization.defaults.workflow_upload_repo.length === 0 ? "" : selectedOrganization.defaults.workflow_upload_repo)
const [uploadBranch, setUploadBranch] = React.useState(selectedOrganization.defaults === undefined ? defaultBranch : selectedOrganization.defaults.workflow_upload_branch === undefined || selectedOrganization.defaults.workflow_upload_branch.length === 0 ? defaultBranch : selectedOrganization.defaults.workflow_upload_branch)
const [uploadUsername, setUploadUsername] = React.useState(selectedOrganization.defaults === undefined ? "" : selectedOrganization.defaults.workflow_upload_username === undefined || selectedOrganization.defaults.workflow_upload_username.length === 0 ? "" : selectedOrganization.defaults.workflow_upload_username)
const [uploadToken, setUploadToken] = React.useState(selectedOrganization.defaults === undefined ? "" : selectedOrganization.defaults.workflow_upload_token === undefined || selectedOrganization.defaults.workflow_upload_token.length === 0 ? "" : selectedOrganization.defaults.workflow_upload_token)
const [uploadToken, setUploadToken] = React.useState("")
const [regionStatus, setRegionStatus] = useState();
useEffect(() => {
@@ -199,9 +202,10 @@ const OrgHeaderexpandedNew = (props) => {
setUploadUsername(selectedOrganization?.defaults?.workflow_upload_username)
}
if (uploadToken !== selectedOrganization?.defaults?.workflow_upload_token) {
setUploadToken(selectedOrganization?.defaults?.workflow_upload_token)
}
// Not showing the token in the UI (cause it's in plain text)
// if (uploadToken !== selectedOrganization?.defaults?.workflow_upload_token) {
// setUploadToken(selectedOrganization?.defaults?.workflow_upload_token)
// }
}, [selectedOrganization])
useEffect(() => {
@@ -264,7 +268,7 @@ const OrgHeaderexpandedNew = (props) => {
const handleSendChangeRegionMail = (region) => {
if (selectedOrganization === undefined || selectedOrganization === null) {
toast.error("Failed to send request for changing region. Please contact support@shuffler.io.")
toast.error(`Failed to send request for changing region. Please contact ${supportEmail}`)
return
}
@@ -294,13 +298,13 @@ const OrgHeaderexpandedNew = (props) => {
body: JSON.stringify(data),
}).then((response) => {
if (response.status !== 200) {
toast.error("Failed to send request for changing region. Please contact support@shuffler.io.")
toast.error(`Failed to send request for changing region. Please contact ${supportEmail}`)
} else {
toast.success("Successfully sent request for region change. We will process the move and contact you shortly.")
}
}).catch((err) => {
console.log(err)
toast.error("Failed to send request for changing region. Please contact support@shuffler.io.")
toast.error(`Failed to send request for changing region. Please contact ${supportEmail}`)
})
}
@@ -358,7 +362,7 @@ const OrgHeaderexpandedNew = (props) => {
const orgSaveButton = (
<Tooltip title="Save any unsaved data" placement="bottom">
<Button
style={{ width: 244, height: 51, display: 'flex', justifyContent: 'center', textTransform: 'capitalize', padding: "16px, 24px, 16px, 24px", borderRadius: 4, backgroundColor: "#ff8544", color: "#1a1a1a", fontSize: 16, }}
style={{ width: 244, height: 51, display: 'flex', justifyContent: 'center', textTransform: 'capitalize', padding: "16px, 24px, 16px, 24px", borderRadius: 4, fontSize: 16, }}
variant="contained"
color="primary"
disabled={
@@ -448,7 +452,7 @@ const OrgHeaderexpandedNew = (props) => {
<div style={{ marginTop: 8, display: "flex" }} />
<div style={{ display: "flex" }}>
<div style={{ width: "100%", maxWidth: 434, marginRight: 10 }}>
Name
<Typography variant="text" style={{color: theme.palette.text.primary}}>Name</Typography>
<TextField
required
style={{
@@ -459,7 +463,8 @@ const OrgHeaderexpandedNew = (props) => {
maxWidth: 434,
marginTop: "5px",
marginRight: "15px",
backgroundColor: isEditOrgTab ? "#212121" : theme.palette.inputColor,
color: theme.palette.textFieldStyle.color,
backgroundColor: isEditOrgTab ? theme.palette.textFieldStyle.backgroundColor : theme.palette.inputColor,
}}
fullWidth={true}
placeholder="Name"
@@ -513,10 +518,11 @@ const OrgHeaderexpandedNew = (props) => {
color="primary"
InputProps={{
style: {
color: "white",
color: theme.palette.textFieldStyle.color,
height: "35px",
fontSize: "1em",
borderRadius: 4,
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
},
classes: {
notchedOutline: isEditOrgTab ? null : classes.notchedOutline,
@@ -526,10 +532,10 @@ const OrgHeaderexpandedNew = (props) => {
</div>
{userdata?.support ? (
<div style={{ alignItems: 'center' }}>
<div style={{ marginRight: '12px', color: 'white' }}>Status</div>
<div style={{ marginRight: '12px', color: theme.palette.text.primary }}>Status</div>
<FormControl style={{ width: 220, height: 35 }}>
<Select
style={{ minWidth: 220, marginTop: 5, maxWidth: 220, height: 35, borderRadius: 4 }}
style={{ minWidth: 220, marginTop: 5, maxWidth: 220, height: 35, borderRadius: 4, color: theme.palette.textFieldStyle.color}}
id="multiselect-status"
multiple
value={selectedStatus}
@@ -552,13 +558,13 @@ const OrgHeaderexpandedNew = (props) => {
{isCloud ? (
<div style={{ marginLeft: 13, fontSize: 16, color: "#9E9E9E" }} >
Change Region
<Typography variant="text" style={{color: theme.palette.text.primary}}>Change Region</Typography>
<RegionChangeModal selectedOrganization={selectedOrganization} setSelectedRegion={setSelectedRegion} userdata={userdata} handleSendChangeRegionMail={handleSendChangeRegionMail} />
</div>
) : null}
</div>
<div style={{ marginTop: "10px" }} />
About
<Typography variant="text" style={{color: theme.palette.text.primary}}>Description</Typography>
<div style={{ display: "flex" }}>
<TextField
required
@@ -568,7 +574,8 @@ const OrgHeaderexpandedNew = (props) => {
flex: "1",
marginTop: "5px",
marginRight: "15px",
backgroundColor: isEditOrgTab ? "#212121" : theme.palette.inputColor,
color: theme.palette.textFieldStyle.color,
backgroundColor: isEditOrgTab ? theme.palette.textFieldStyle.backgroundColor : theme.palette.inputColor,
height: 89,
borderRadius: 4,
}}
@@ -621,7 +628,7 @@ const OrgHeaderexpandedNew = (props) => {
notchedOutline: isEditOrgTab ? null : classes.notchedOutline,
},
style: {
color: "white",
color: theme.palette.textFieldStyle.color,
height: 89,
borderRadius: 4,
},
@@ -632,7 +639,7 @@ const OrgHeaderexpandedNew = (props) => {
</div>
</div>
<Typography variant="h5" style={{ color: "rgba(241, 241, 241, 1)", fontSize: 24, fontWeight: 600, marginTop: 40, textAlign: "left" }}>
<Typography variant="h5" style={{ fontSize: 24, fontWeight: 500, marginTop: 40, textAlign: "left" }}>
Preferences
</Typography>
@@ -653,7 +660,7 @@ const OrgHeaderexpandedNew = (props) => {
</Grid>
<Grid item xs={12}>
<span>
<Typography style={{ fontWeight: 400, fontSize: 16 }}>Org Documentation reference</Typography>
<Typography style={{ fontWeight: 400, fontSize: 18, color: theme.palette.text.primary }}>Org Documentation reference</Typography>
<Typography variant="body2" color="textSecondary" style={{ fontWeight: 400, fontSize: 16, marginTop: 8 }}>
Add a URL that is added as a link, pointing to any external documentation page you want.
@@ -668,7 +675,8 @@ const OrgHeaderexpandedNew = (props) => {
height: 35,
fontSize: 16,
borderRadius: 4,
backgroundColor: isEditOrgTab ? "rgba(33, 33, 33, 1)" : theme.palette.inputColor,
color: theme.palette.textFieldStyle.color,
backgroundColor: isEditOrgTab ? theme.palette.textFieldStyle.backgroundColor : theme.palette.inputColor,
}}
fullWidth={true}
type="name"
@@ -709,6 +717,13 @@ const OrgHeaderexpandedNew = (props) => {
auto_provision: selectedOrganization?.sso_config?.auto_provision,
}
)
if (userdata.org_status.includes("integration_partner")) {
toast.info("Reloading page to update the changes everywhere")
setTimeout(() => {
window.location.reload()
}, 5000)
}
}
}}
onChange={(e) => {
@@ -719,8 +734,7 @@ const OrgHeaderexpandedNew = (props) => {
notchedOutline: isEditOrgTab ? null : classes.notchedOutline,
},
style: {
color: "white",
color: theme.palette.textFieldStyle.color,
fontWeight: 400,
fontSize: 16,
borderRadius: 4,
@@ -736,9 +750,9 @@ const OrgHeaderexpandedNew = (props) => {
serverside={false}
/>
<Grid item xs={12} style={{ marginTop: 20, }}>
<Typography variant="h4" style={{ textAlign: "left", color: "rgba(241, 241, 241, 1)", fontSize: 24, fontWeight: 600, }}>Workflow Backup Repository</Typography>
<Typography variant="body2" style={{ textAlign: "left", marginTop: 8, color: "#9E9E9E", fontSize: 16, fontWeight: 400 }}>
Decide where workflows are backed up in a Git repository. Will create logs and notifications if upload fails. The repository and branch must already have been initialized. Files will show up in the repo root in the /orgId/workflow-status/workflowId.json format. <b>MSSP:</b> If suborg exists, this will automatically be applied for them as well (not retroactive). <a href="/docs/configuration#environment-variables" style={{ textDecoration: "none", color: "#f86a3e" }} target="_blank">Credentials are encrypted.</a>
<Typography variant="h5" style={{ textAlign: "left", fontWeight: 500, }}>Workflow Backup Repository</Typography>
<Typography variant="body2" style={{ textAlign: "left", marginTop: 8, color: theme.palette.text.secondary, fontSize: 16, fontWeight: 400 }}>
Decide where workflows are backed up in a Git repository. Will create logs and notifications if upload fails. The repository and branch must already have been initialized. Files will show up in the repo root in the /orgId/workflow-status/workflowId.json format. <b>MSSP:</b> If suborg exists, this will automatically be applied for them as well (not retroactive). <a href="/docs/configuration#environment-variables" style={{ textDecoration: "none", color: theme.palette.linkColor }} target="_blank">Credentials are encrypted.</a>
</Typography>
<Grid container style={{ marginTop: 10, }} spacing={2}>
<Grid item xs={6} style={{}}>
@@ -751,7 +765,7 @@ const OrgHeaderexpandedNew = (props) => {
marginTop: "8px",
marginRight: "16px",
height: 35,
backgroundColor: isEditOrgTab ? "rgba(33, 33, 33, 1)" : theme.palette.inputColor,
backgroundColor: isEditOrgTab ? theme.palette.textFieldStyle.backgroundColor : theme.palette.inputColor,
}}
fullWidth={true}
type="name"
@@ -770,7 +784,7 @@ const OrgHeaderexpandedNew = (props) => {
notchedOutline: isEditOrgTab ? null : classes.notchedOutline,
},
style: {
color: "white",
color: theme.palette.textFieldStyle.color,
fontWeight: 400,
fontSize: 16,
@@ -791,7 +805,7 @@ const OrgHeaderexpandedNew = (props) => {
marginTop: "8px",
marginRight: "16px",
height: 35,
backgroundColor: isEditOrgTab ? "rgba(33, 33, 33, 1)" : theme.palette.inputColor,
backgroundColor: isEditOrgTab ? theme.palette.textFieldStyle.backgroundColor : theme.palette.inputColor,
}}
fullWidth={true}
type="name"
@@ -810,7 +824,7 @@ const OrgHeaderexpandedNew = (props) => {
notchedOutline: isEditOrgTab ? null : classes.notchedOutline,
},
style: {
color: "white",
color: theme.palette.textFieldStyle.color,
fontWeight: 400,
fontSize: 16,
@@ -833,7 +847,7 @@ const OrgHeaderexpandedNew = (props) => {
marginTop: "8px",
marginRight: "16px",
height: 35,
backgroundColor: isEditOrgTab ? "rgba(33, 33, 33, 1)" : theme.palette.inputColor,
backgroundColor: isEditOrgTab ? theme.palette.textFieldStyle.backgroundColor : theme.palette.inputColor,
}}
fullWidth={true}
type="name"
@@ -852,8 +866,7 @@ const OrgHeaderexpandedNew = (props) => {
notchedOutline: isEditOrgTab ? null : classes.notchedOutline,
},
style: {
color: "white",
color: theme.palette.textFieldStyle.color,
fontWeight: 400,
fontSize: 16,
borderRadius: 4,
@@ -865,7 +878,7 @@ const OrgHeaderexpandedNew = (props) => {
</Grid>
<Grid item xs={6} style={{}}>
<span>
<Typography style={{ fontWeight: 400, fontSize: 16 }}>Git token/password</Typography>
<Typography style={{ fontWeight: 400, fontSize: 16 }}>New Git token/password</Typography>
<TextField
required
style={{
@@ -873,7 +886,7 @@ const OrgHeaderexpandedNew = (props) => {
marginTop: "8px",
marginRight: "16px",
height: 35,
backgroundColor: isEditOrgTab ? "rgba(33, 33, 33, 1)" : theme.palette.inputColor,
backgroundColor: isEditOrgTab ? theme.palette.textFieldStyle.backgroundColor : theme.palette.inputColor,
}}
fullWidth={true}
id="outlined-with-placeholder"
@@ -891,7 +904,7 @@ const OrgHeaderexpandedNew = (props) => {
notchedOutline: isEditOrgTab ? null : classes.notchedOutline,
},
style: {
color: "white",
color: theme.palette.textFieldStyle.color,
fontWeight: 400,
fontSize: 16,
@@ -1116,7 +1129,7 @@ const RegionChangeModal = memo(({ selectedOrganization, setSelectedRegion, userd
} else if (regiontag === "ca") {
regiontag = "CA";
regionCode = "ca";
} else if (regiontag === "austrailia") {
} else if (regiontag === "au") {
regiontag = "AUS";
regionCode = "au"
}
@@ -1146,9 +1159,6 @@ const RegionChangeModal = memo(({ selectedOrganization, setSelectedRegion, userd
selectedOrganization.region = "europe-west2";
}
if (region === "AUS") {
region = "AUS (test)"
}
// Check if the current region matches the selected region
if (region === selectedOrganization.region) {
@@ -1157,7 +1167,7 @@ const RegionChangeModal = memo(({ selectedOrganization, setSelectedRegion, userd
<MenuItem value={region} key={index} disabled>
{/* show region image through cdn */}
<img src={`https://flagcdn.com/48x36/${regionImageCode}.png`} alt={region} style={{ marginRight: 10 }} />
{region}
{region === "AUS" ? "AUS (test)" : region}
</MenuItem>
);
} else {
@@ -1167,7 +1177,7 @@ const RegionChangeModal = memo(({ selectedOrganization, setSelectedRegion, userd
alt={region}
style={{ marginRight: 10, width: 20, height: 18, }}
/>
{region}
{region === "AUS" ? "AUS (test)" : region}
</MenuItem>;
}
})}
+55 -17
View File
@@ -1,4 +1,4 @@
import React, { useEffect, useState, useCallback } from 'react';
import React, { useEffect, useState, useCallback, useContext } from 'react';
import { Link, useNavigate, useLocation } from "react-router-dom";
import Billing from "../components/Billing.jsx";
import Priorities from "../components/Priorities.jsx";
@@ -8,7 +8,8 @@ import CloudSyncTab from '../components/CloudSyncTab.jsx';
import SSOTab from "../components/ssoTab.jsx"
import { ToastContainer, toast } from "react-toastify";
import { Button, Tooltip } from '@mui/material';
import { getTheme } from '../theme.jsx';
import { Context } from '../context/ContextApi.jsx';
const OrganizationTab = (props) => {
const location = useLocation();
const navigate = useNavigate();
@@ -25,7 +26,9 @@ const OrganizationTab = (props) => {
selectedOrganization, handleGetOrg,
handleStatusChange, handleEditOrg,
isLoaded,
removeCookie
removeCookie,
isIntegrationPartner, isChildOrg,
isGlobalUser
} = props;
const [selectedTab, setSelectedTab] = useState('org_config');
@@ -33,10 +36,28 @@ const OrganizationTab = (props) => {
const [billingInfo, setBillingInfo] = useState({});
const [orgRequest, setOrgRequest] = React.useState(true);
const [curIndex, setCurIndex] = React.useState(0);
const items = ['Org Configuration', "SSO", "Notifications", 'Billing & Stats', 'Branding'];
const [visibleTabs, setVisibleTabs] = useState(items);
const [unreadNotifications, setUnreadNotifications] = React.useState(
notifications?.filter((notification) => notification.read === false)?.length
);
const { themeMode, brandColor, brandName } = useContext(Context);
const theme = getTheme(themeMode, brandColor);
useEffect(() => {
if (isIntegrationPartner && isChildOrg && !isGlobalUser) {
setVisibleTabs(items.filter((item) => item !== 'Branding' && item !== 'SSO'));
}else {
if (userdata && userdata.active_org && userdata.active_org.role === 'admin') {
setVisibleTabs(items);
}else {
setVisibleTabs(items.filter((item) => item !== 'SSO'));
}
}
},[isIntegrationPartner, isChildOrg, isGlobalUser, userdata]);
useEffect(() => {
const queryParams = new URLSearchParams(location.search);
const tabName = queryParams.get('admin_tab');
@@ -48,10 +69,26 @@ const OrganizationTab = (props) => {
} else if(decodedTabName === 'sso'){
setCurIndex(1)
}else if (decodedTabName === 'notifications' || decodedTabName === 'priorities') {
setCurIndex(2);
if (isIntegrationPartner && isChildOrg && !isGlobalUser) {
setCurIndex(1);
}else {
if (userdata && userdata.active_org && userdata.active_org.role === 'admin') {
setCurIndex(2);
}else {
setCurIndex(1);
}
}
} else if (decodedTabName === 'billingstats' || decodedTabName === 'billing') {
setCurIndex(3);
} else if (decodedTabName === 'branding(beta)') {
if (isIntegrationPartner && isChildOrg && !isGlobalUser) {
setCurIndex(2);
} else {
if (userdata && userdata?.active_org && userdata?.active_org?.role === 'admin') {
setCurIndex(3);
} else {
setCurIndex(2);
}
}
} else if (decodedTabName === 'branding') {
setCurIndex(4);
}
// else if (decodedTabName === 'analytics') {
@@ -64,7 +101,7 @@ const OrganizationTab = (props) => {
const formattedTabName = tabName.toLowerCase().replace(/[\s&]+/g, '');
const encodedTabName = encodeURIComponent(formattedTabName);
setSelectedTab(formattedTabName);
document.title = `Shuffle - admin - ${formattedTabName}`;
document.title = brandName?.length > 0 ? `${brandName} - admin - ${formattedTabName}` : `Shuffle - admin - ${formattedTabName}`;
navigate(`?admin_tab=${encodedTabName}`);
};
@@ -136,11 +173,12 @@ const OrganizationTab = (props) => {
return <EditOrgTab isCloud={isCloud} selectedStatus={selectedStatus} setSelectedStatus={setSelectedStatus} handleStatusChange={handleStatusChange} handleEditOrg={handleEditOrg} userdata={userdata} globalUrl={globalUrl} serverside={serverside} handleGetOrg={handleGetOrg} setSelectedOrganization={setSelectedOrganization} selectedOrganization={selectedOrganization} />;
}
};
return (
<div style={{ height: "100%", width: "100%", color: '#FFFFFF', backgroundColor: '#212121', borderTopRightRadius: '8px', borderBottomRightRadius: 8, borderLeft: "1px solid #494949", boxSizing: 'border-box' }}>
<div style={{ display: 'flex', justifyContent: 'space-around', width: "100%", borderBottom: '1px solid #494949' ,boxSizing: 'border-box' }}>
{['Org Configuration', "sso", "Notifications", 'Billing & Stats', 'Branding'].map((tabName, index) => (
<div style={{ height: "100%", width: "100%", color: theme.palette.platformColor, backgroundColor: theme.palette.platformColor, borderTopRightRadius: '8px', borderBottomRightRadius: 8, borderLeft: theme.palette.defaultBorder, boxSizing: 'border-box' }}>
<div style={{ display: 'flex', justifyContent: 'space-around', width: "100%", borderBottom: theme.palette.defaultBorder ,boxSizing: 'border-box' }}>
{visibleTabs.map((tabName, index) => (
<Tooltip
key={index}
title={
@@ -161,10 +199,10 @@ const OrganizationTab = (props) => {
sx={{
"&.MuiButton-root": {
padding: '28px 0',
borderBottom: index === curIndex ? '2px solid #FF8444' : 'none',
borderBottom: index === curIndex ? `2px solid ${theme.palette.primary.main}`: 'none',
cursor: 'pointer',
fontWeight: index === curIndex ? 'bold' : 'normal',
color: index === curIndex ? "#FF8444" : "#FFFFFF",
color: index === curIndex ? theme.palette.primary.main : theme.palette.text.primary,
textTransform: 'none',
fontSize: 16,
width: "100%",
@@ -172,7 +210,7 @@ const OrganizationTab = (props) => {
borderRadius: 0,
},
"&: hover": {
backgroundColor: "#323232"
backgroundColor: theme.palette.hoverColor
},
"&.Mui-disabled": {
color: "#6F6F6F",
@@ -182,7 +220,7 @@ const OrganizationTab = (props) => {
((tabName === "sso")) && !(userdata?.support || userdata?.active_org?.role === "admin")
}
>
{index === 2 && unreadNotifications > 0 ? (
{tabName.toLowerCase() === "notifications" && unreadNotifications > 0 ? (
<div style={{ position: 'relative' }}>
<div style={{
position: 'absolute',
@@ -191,7 +229,7 @@ const OrganizationTab = (props) => {
width: 20,
height: 20,
borderRadius: 10,
backgroundColor: '#FF8444',
backgroundColor: theme.palette.primary.main,
color: '#FFFFFF',
fontSize: 12,
display: 'flex',
@@ -203,7 +241,7 @@ const OrganizationTab = (props) => {
{tabName}
</div>
) : (
<>{index === 1 ? "SSO" : tabName}</>
<>{tabName}</>
)}
</Button>
</div>
+119 -62
View File
@@ -1,7 +1,7 @@
import React, { useState, useEffect, useLayoutEffect, useMemo } from "react";
import React, { useState, useEffect, useContext, useMemo } from "react";
import { toast } from 'react-toastify';
import { makeStyles, createStyles } from "@mui/styles";
import theme from '../theme.jsx';
import {getTheme} from '../theme.jsx';
import { useNavigate, Link, useParams } from "react-router-dom";
import { validateJson, GetIconInfo } from "../views/Workflows.jsx";
@@ -11,7 +11,7 @@ import { NestedMenuItem } from "mui-nested-menu";
import { parsedDatatypeImages } from "../components/AppFramework.jsx";
import { green, yellow, red } from "../views/AngularWorkflow.jsx"
//import { useAlert
import { Context } from "../context/ContextApi.jsx";
import {
Chip,
ButtonGroup,
@@ -215,6 +215,9 @@ const ParsedAction = (props) => {
}
}, [expansionModalOpen])
const {themeMode, supportEmail} = useContext(Context)
const theme = getTheme(themeMode)
/*
useEffect(() => {
// This will have the OLD selectedAction, not the new one huh?
@@ -1626,7 +1629,7 @@ const ParsedAction = (props) => {
padding: 8,
paddingLeft: 14,
paddingBottom: 4,
backgroundColor: hover ? theme.palette.surfaceColor : theme.palette.inputColor,
backgroundColor: hover ? theme.palette.hoverColor : theme.palette.textFieldStyle.backgroundColor,
}} onMouseEnter={() => setHover(true)} onMouseLeave={() => setHover(false)}
onClick={(event) => {
// event.preventDefault()
@@ -1664,7 +1667,7 @@ const ParsedAction = (props) => {
<span style={{ marginBottom: 0, marginTop: 3, }}>{newActionname}</span>
</div>
{extraDescription.length > 0 ?
<Typography variant="body2" color="textSecondary" style={{ marginTop: 0, overflow: "hidden", whiteSpace: "nowrap", display: "block", }}>
<Typography variant="body2" style={{ marginTop: 0, overflow: "hidden", whiteSpace: "nowrap", display: "block", color: theme.palette.textPrimary}}>
{extraDescription}
</Typography>
: null}
@@ -1738,14 +1741,14 @@ const ParsedAction = (props) => {
}}
>
<Tooltip title={"App: " + selectedAction.app_name + ". Click to open in new tab"} placement="top">
<a href={"/apps/" + selectedAction?.app_id} target="_blank" style={{ textDecoration: "none", color: "white", }}>
<a href={"/apps/" + selectedAction?.app_id} target="_blank" style={{ textDecoration: "none", color: theme.palette.textPrimary, }}>
<img src={selectedAppIcon} style={{
width: 30,
height: 30,
marginRight: 10,
borderRadius: 5,
marginTop: 13,
border: "2px solid rgba(255,255,255,0.3)",
border: themeMode === "dark" ? "2px solid rgba(255,255,255,0.3)" : "2px solid rgba(0, 0, 0, 0.1)",
}} />
</a>
</Tooltip>
@@ -1809,7 +1812,7 @@ const ParsedAction = (props) => {
title="See previous results for this action"
placement="top"
>
<ArrowLeftIcon style={{ color: "rgba(255,255,255,0.7)" }} />
<ArrowLeftIcon style={{ color: theme.palette.textPrimary }} />
</Tooltip>
</IconButton>
<IconButton
@@ -1829,7 +1832,7 @@ const ParsedAction = (props) => {
title="Find app documentation"
placement="top"
>
<DescriptionIcon style={{ color: "rgba(255,255,255,0.7)" }} />
<DescriptionIcon style={{ color: theme.palette.textPrimary }} />
</Tooltip>
</IconButton>
@@ -1863,7 +1866,7 @@ const ParsedAction = (props) => {
{autoCompleting ?
<CircularProgress style={{ height: 20, width: 20, }} />
:
<AutoFixHighIcon style={{ color: "rgba(255,255,255,0.7)", height: 24, }} />
<AutoFixHighIcon style={{ color: theme.palette.textPrimary, height: 24, }} />
}
</Tooltip>
</IconButton>
@@ -1883,7 +1886,7 @@ const ParsedAction = (props) => {
marginTop: "auto",
marginBottom: "auto",
height: 30,
marginLeft: 115,
marginLeft: 98,
textTransform: "none",
}}
disabled={autoCompleting}
@@ -1891,7 +1894,7 @@ const ParsedAction = (props) => {
if (runFromHere !== undefined) {
runFromHere(selectedAction)
} else {
toast.error("Function not available. Please contact support@shuffler.io")
toast.error(`Function not available. Please contact ${supportEmail}`)
}
}}
>
@@ -1939,6 +1942,12 @@ const ParsedAction = (props) => {
<Select
MenuProps={{
disableScrollLock: true,
PaperProps: {
sx: {
"&. MuiList-root": {
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
}
}}
}}
value={selectedAction.app_version}
onChange={(event) => {
@@ -1963,9 +1972,8 @@ const ParsedAction = (props) => {
style={{
position: "absolute",
top: 10, right: 10,
backgroundColor: theme.palette.surfaceColor,
backgroundColor: theme.palette.inputColor,
color: "white",
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
color: theme.palette.textFieldStyle.color,
height: 35,
borderRadius: theme.palette?.borderRadius,
}}
@@ -1978,9 +1986,12 @@ const ParsedAction = (props) => {
return (
<MenuItem
key={index}
style={{
backgroundColor: theme.palette.inputColor,
color: "white",
sx={{
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
color: theme.palette.textFieldStyle.color,
"&:hover": {
backgroundColor: theme.palette.hoverColor,
},
}}
value={data.version}
>
@@ -1994,12 +2005,16 @@ const ParsedAction = (props) => {
</div>
<div style={{ display: "flex" }}>
<div style={{ flex: 5 }}>
<Typography style={{ color: "rgba(255,255,255,0.7)" }}>Name</Typography>
<Typography style={{ color: theme.palette.textPrimary,}}>Name</Typography>
<TextField
style={theme.palette.textFieldStyle}
InputProps={{
style: theme.palette.innerTextfieldStyle,
disableUnderline: true,
style: {
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
color: theme.palette.textFieldStyle.color,
height: 40,
},
}}
fullWidth
color="primary"
@@ -2218,10 +2233,10 @@ const ParsedAction = (props) => {
placement="top"
>
<span>
<Typography style={{ color: "rgba(255,255,255,0.7)" }}>Delay</Typography>
<Typography style={{ color: theme.palette.textPrimary }}>Delay</Typography>
<TextField
InputProps={{
style: theme.palette.innerTextfieldStyle,
style: theme.palette.textFieldStyle,
disableUnderline: true,
}}
disabled={selectedAction?.parent_controlled === true && workflow?.parentorg_workflow?.length > 0}
@@ -2280,7 +2295,7 @@ const ParsedAction = (props) => {
<div style={{ marginTop: 15, position: "relative", }}>
<div style={{display: "flex", }}>
<Typography style={{ color: "rgba(255,255,255,0.7)", flex: 10, }}>
<Typography style={{ color: theme.palette.textPrimary, flex: 10, }}>
Authentication
</Typography>
@@ -2333,7 +2348,7 @@ const ParsedAction = (props) => {
title={
workflow?.suborg_distribution?.length > 0 && Object.getOwnPropertyNames(selectedAction?.selectedAuthentication).length !== 0 ? (
<React.Fragment>
<div style={{padding: 10, backgroundColor: theme.palette.inputColor, borderRadius: theme.palette.borderRadius, border: "1px solid rgba(255,255,255,0)"}}>
<div style={{padding: 10, backgroundColor: theme.palette.textFieldStyle.backgroundColor, borderRadius: theme.palette.borderRadius, border: theme.palette.defaultBorder}}>
<FormControlLabel
control={
<Checkbox
@@ -2351,10 +2366,18 @@ const ParsedAction = (props) => {
</React.Fragment>
) : null
} placement="left">
<div style={{ display: "flex" }}>
<div style={{ display: "flex", }}>
<Select
MenuProps={{
disableScrollLock: true,
PaperProps: {
sx: {
'& .MuiList-root': {
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
color: theme.palette.textFieldStyle.color,
},
},
},
}}
labelId="select-app-auth"
value={
@@ -2436,17 +2459,20 @@ const ParsedAction = (props) => {
}
}}
style={{
backgroundColor: theme.palette.inputColor,
color: "white",
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
color: theme.palette.textFieldStyle.color,
height: 35,
maxWidth: rightsidebarStyle.maxWidth - 80,
borderRadius: theme.palette?.borderRadius,
}}
>
<MenuItem
style={{
backgroundColor: theme.palette.inputColor,
color: "white",
sx={{
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
color: theme.palette.textFieldStyle.color,
'&:hover': {
backgroundColor: theme.palette.textFieldStyle.hoverBackgroundColor, // you define this in your theme
},
}}
value="No selection"
>
@@ -2460,11 +2486,14 @@ const ParsedAction = (props) => {
return (
<MenuItem
key={data.id}
style={{
backgroundColor: theme.palette.inputColor,
color: "white",
sx={{
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
color: theme.palette.textFieldStyle.color,
maxWidth: 500,
overflowX: "auto",
'&:hover': {
backgroundColor: theme.palette.hoverColor
},
}}
value={data}
>
@@ -2472,7 +2501,7 @@ const ParsedAction = (props) => {
{data?.validation?.valid === true ?
<Tooltip title="Authentication has been validated" placement="top">
<Chip
style={{ marginLeft: 0, padding: 0, marginRight: 10, cursor: "pointer", borderColor: green, maxHeight: 25, }}
style={{ marginLeft: 0, padding: 0, marginRight: 10, cursor: "pointer", borderColor: green, maxHeight: 25, color: theme.palette.chipStyle.color, backgroundColor: theme.palette.chipStyle.backgroundColor }}
label={"Valid"}
variant="outlined"
color="secondary"
@@ -2481,7 +2510,7 @@ const ParsedAction = (props) => {
: null}
{data?.last_modified === true ?
<Chip
style={{ marginLeft: 0, padding: 0, marginRight: 10, cursor: "pointer", maxHeight: 25, }}
style={{ marginLeft: 0, padding: 0, marginRight: 10, cursor: "pointer", maxHeight: 25, color: theme.palette.chipStyle.color, backgroundColor: theme.palette.chipStyle.backgroundColor }}
label={"Latest"}
variant="outlined"
color="secondary"
@@ -2503,9 +2532,12 @@ const ParsedAction = (props) => {
<Divider style={{ marginTop: 10, marginBottom: 10, }} />
<MenuItem
style={{
backgroundColor: theme.palette.inputColor,
color: "white",
sx={{
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
color: theme.palette.textPrimary,
'&:hover': {
backgroundColor: theme.palette.hoverColor
},
}}
value="authgroups"
disabled
@@ -2637,7 +2669,7 @@ const ParsedAction = (props) => {
{workflow.execution_variables !== undefined && workflow.execution_variables !== null && workflow.execution_variables.length > 0 ? (
<div style={{ marginTop: "20px" }}>
<Typography color="textSecondary">Runtime variable (optional)</Typography>
<Typography style={{color: theme.palette.textPrimary}}>Runtime variable (optional)</Typography>
<Select
MenuProps={{
disableScrollLock: true,
@@ -2742,8 +2774,8 @@ const ParsedAction = (props) => {
options={renderedActionOptions}
ListboxProps={{
style: {
backgroundColor: theme.palette.surfaceColor,
color: "white",
backgroundColor: theme.palette.platformColor,
color: theme.palette.textColor,
},
}}
filterOptions={(options, { inputValue }) => {
@@ -2774,7 +2806,7 @@ const ParsedAction = (props) => {
}}
style={{
backgroundColor: theme.palette.backgroundColor,
backgroundColor: theme.palette.cardBackgroundColor,
height: 35,
borderRadius: theme.palette?.borderRadius,
}}
@@ -2890,7 +2922,8 @@ const ParsedAction = (props) => {
const actionDescription = null
return (
<Tooltip title={actionDescription}
<Tooltip
title={actionDescription}
placement="right"
open={!hiddenDescription}
PopperProps={{
@@ -2913,13 +2946,18 @@ const ParsedAction = (props) => {
dataLPIgnore="true"
autoComplete="off"
color="primary"
id="checkbox-search"
variant="body1"
style={{
...theme.palette.textFieldStyle,
border: selectedAction?.parent_controlled === true && workflow?.parentorg_workflow?.length > 0 ? `1px dotted ${theme.palette.distributionColor}` : "inherit",
}}
inputProps={{
...params.inputProps,
style: {
color: theme.palette.textFieldStyle.color,
},
}}
label={isIntegration ? "Choose a category" : "Find Actions"}
variant="outlined"
name={`disable_autocomplete_${Math.random()}`}
@@ -2933,12 +2971,12 @@ const ParsedAction = (props) => {
{selectedAction?.app_name === "Shuffle AI" && selectedAction?.name === "run_llm" ?
selectedAction?.environment === "Cloud" && isCloud ? (
<Typography color="textSecondary" variant="body2" style={{ paddingTop: 25, }}>
<Typography variant="body2" style={{color: theme.palette.textPrimary, paddingTop: 25, }}>
Info: Cloud Inference processing runs with Shuffle's GPUs in EU, Netherlands, and may be unstable. Your data is NOT stored there.
</Typography>
)
:
<Typography color="textSecondary" variant="body2" style={{ paddingTop: 25, color: red, }}>
<Typography variant="body2" style={{ paddingTop: 25, color: red, color: theme.palette.textPrimary}}>
This action is slow without GPU's. Use Shuffle's Cloud Runtime location for faster processing.
</Typography>
:
@@ -2959,7 +2997,6 @@ const ParsedAction = (props) => {
apps !== undefined && apps !== null && apps.length > 0 ?
<div style={{ display: "flex", maxWidth: 335, overflowX: "auto", overflowY: "hidden", }}>
<div onClick={() => {
selectedAction.example = "noapp"
selectedAction.large_image = newimage
if (cy !== undefined && cy !== null) {
@@ -3029,7 +3066,13 @@ const ParsedAction = (props) => {
var found = false
for (var key in app.categories) {
if (app.categories[key].toLowerCase() !== newactionname) {
var localnewactionname = newactionname
if (newactionname == "comms") {
localnewactionname = "communication"
}
if (app.categories[key].toLowerCase() !== localnewactionname) {
continue
}
@@ -3042,6 +3085,9 @@ const ParsedAction = (props) => {
}
var isAppSelected = false
console.log("App found: ", app.name, app.categories, appIndex, newactionname)
const paramIndex = selectedAction.parameters.findIndex((param) => param.name === "app_name")
if (paramIndex > -1) {
// Check the actual value and if it's the same
@@ -3794,8 +3840,8 @@ const ParsedAction = (props) => {
id={clickedFieldId}
disabled={disabled}
style={{
backgroundColor: theme.palette.inputColor,
borderRadius: theme.palette?.borderRadius,
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
borderRadius: theme.palette?.textFieldStyle.borderRadius,
width: "100%",
maxHeight: multiline === true ? undefined : 40,
minHeight: 40,
@@ -3816,7 +3862,7 @@ const ParsedAction = (props) => {
<ButtonGroup color="secondary" orientation={multiline ? "vertical" : "horizontal"}>
<Tooltip title="Autocomplete text" placement="bottom">
<AddCircleOutlineIcon
style={{ color: "rgba(255,255,255,0.7)", cursor: "pointer", margin: multiline ? 5 : 0, }}
style={{ color: theme.palette.textPrimary, cursor: "pointer", margin: multiline ? 5 : 0, }}
onClick={(event) => {
event.preventDefault()
@@ -4132,15 +4178,15 @@ const ParsedAction = (props) => {
datafield = (
<TextField
style={{
backgroundColor: theme.palette.inputColor,
borderRadius: theme.palette?.borderRadius,
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
borderRadius: theme.palette?.textFieldStyle.borderRadius,
}}
InputProps={{
endAdornment: hideExtraTypes ? null : (
<InputAdornment position="end">
<Tooltip title="Autocomplete text" placement="top">
<AddCircleOutlineIcon
style={{ cursor: "pointer" }}
style={{ cursor: "pointer", color: theme.palette.textPrimary }}
onClick={(event) => {
setMenuPosition({
top: event.pageY + 10,
@@ -4199,6 +4245,14 @@ const ParsedAction = (props) => {
<Select
MenuProps={{
disableScrollLock: true,
PaperProps: {
sx: {
'& .MuiList-root': {
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
color: theme.palette.textFieldStyle.color,
},
},
},
}}
SelectDisplayProps={{
style: {
@@ -4237,9 +4291,12 @@ const ParsedAction = (props) => {
return (
<MenuItem
key={data}
style={{
sx={{
backgroundColor: selected ? theme.palette.backgroundColor : theme.palette.inputColor,
color: "white",
color: theme.palette.textFieldStyle.color,
"&:hover": {
backgroundColor: theme.palette.hoverColor
},
}}
value={data}
>
@@ -4690,7 +4747,7 @@ const ParsedAction = (props) => {
width: 24,
height: 24,
marginRight: 10,
color: "rgba(255,255,255,0.6)",
color: theme.palette.textPrimary,
}}
onClick={() => {
setAuthenticationModalOpen(true);
@@ -4708,7 +4765,7 @@ const ParsedAction = (props) => {
>
<PriorityHighIcon
style={{
color: "rgba(255,255,255,0.5)",
color: theme.palette.textPrimary,
marginRight: 0,
}} />
</Tooltip>
@@ -4720,7 +4777,7 @@ const ParsedAction = (props) => {
placement="top"
>
<AutoFixHighIcon style={{
color: "rgba(255,255,255,0.7)",
color: theme.palette.textPrimary,
marginRight: 10,
}} />
</Tooltip>
@@ -4747,7 +4804,7 @@ const ParsedAction = (props) => {
flex: "10",
marginTop: "auto",
marginBottom: "auto",
color: "#C5C5C5",
color: theme.palette.textPrimary,
}}
>
{tmpitem} <span style={{ color: theme.palette.main }}>{selectedActionParameters[count].required || selectedActionParameters[count].configuration ? "*" : ""}</span>
@@ -4776,7 +4833,7 @@ const ParsedAction = (props) => {
{((data.options !== undefined && data.options !== null && data.options.length > 0) || (selectedActionParameters[count].options !== undefined && selectedActionParameters[count].options !== null && selectedActionParameters[count].options.length > 0)) ? null :
<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, }}
style={{ color: theme.palette.textPrimary, cursor: "pointer", margin: multiline ? 5 : 0, height: 20, width: 20, }}
onMouseOver={(event) => {
const clickedField = document.getElementById(clickedFieldId)
if (clickedField !== null) {
+67 -58
View File
@@ -1,7 +1,6 @@
import React, { useState, useEffect, useContext, memo } from "react";
import { toast } from "react-toastify";
import theme from "../theme.jsx";
import { getTheme } from "../theme.jsx";
import { v4 as uuidv4, v5 as uuidv5, validate as isUUID, } from "uuid";
import {
Paper,
@@ -18,6 +17,7 @@ import {
TextField,
MenuItem,
IconButton,
Box,
} from "@mui/material";
import {
@@ -41,7 +41,8 @@ const useStyles = makeStyles({
const Priorities = memo((props) => {
const { globalUrl, userdata,clickedFromOrgTab,selectedOrganization, handleEditOrg, serverside, billingInfo, stripeKey, checkLogin, setAdminTab, setCurTab, notifications, setNotifications, } = props;
const { themeMode, brandColor } = useContext(Context);
const theme = getTheme(themeMode, brandColor);
const [showDismissed, setShowDismissed] = React.useState(false);
const [showRead, setShowRead] = React.useState(false);
const [appFramework, setAppFramework] = React.useState({});
@@ -62,7 +63,7 @@ const Priorities = memo((props) => {
);
let navigate = useNavigate();
const classes = useStyles();
const classes = useStyles();
useEffect(() => {
getFramework()
@@ -353,13 +354,13 @@ const Priorities = memo((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={{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 ? theme.palette.platformColor :null, borderRadius: clickedFromOrgTab ? '16px':null, }}>
<div style={{ maxHeight: 1700, overflowY: "auto", width: '100%', scrollbarColor: theme.palette.scrollbarColorTransparent, scrollbarWidth: 'thin'}}>
<div style={{maxWidth: "calc(100% - 20px)"}}>
<Typography variant="h5" style={{ color: "rgba(241, 241, 241, 1)", fontSize: 24, fontWeight: 600, textAlign: "left" }}>
<Typography variant="h5" style={{ fontSize: 24, fontWeight: 500, textAlign: "left" }}>
Notification Workflow
</Typography>
<Typography style={{ color: "rgba(158, 158, 158, 1)", fontSize: 16, fontWeight: 400, marginTop: 5, }}>
<Typography color="textSecondary" style={{ fontSize: 16, fontWeight: 400, marginTop: 5, }}>
The notification workflow triggers when an error occurs in one of your workflows. Each individual one will only start a workflow once every 2 minutes. <b>You can point child org notifications into the parent org notification by choosing it in the list.</b>
</Typography>
@@ -382,8 +383,9 @@ const Priorities = memo((props) => {
classes={{ inputRoot: classes.inputRoot }}
ListboxProps={{
style: {
backgroundColor: "#212121",
color: "white",
backgroundColor: theme.palette.surfaceColor,
color: theme.palette.text.primary,
borderRadius: theme.palette.borderRadius,
},
}}
getOptionLabel={(option) => {
@@ -404,8 +406,9 @@ const Priorities = memo((props) => {
options={workflows}
fullWidth
style={{
backgroundColor: "#212121",
borderRadius: theme.palette?.borderRadius,
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
borderRadius: theme.palette.textFieldStyle.borderRadius,
color: theme.palette.textFieldStyle.color,
height: 35,
marginBottom: 40,
}}
@@ -449,8 +452,8 @@ const Priorities = memo((props) => {
<MenuItem
{...props}
style={{
// backgroundColor: theme.palette.inputColor,
color: data.id === workflow.id ? "red" : "white",
backgroundColor: theme.palette.surfaceColor,
color: data.id === workflow.id ? "red" : theme.palette.text.primary,
borderBottom: data.id === "parent" ? "2px solid rgba(255,255,255,0.5)" : null
}}
value={data}
@@ -470,8 +473,9 @@ const Priorities = memo((props) => {
<TextField
{...params}
style={{
backgroundColor: "rgba(33, 33, 33, 1)",
borderRadius: 4,
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
color: theme.palette.textFieldStyle.color,
borderRadius: theme.palette.textFieldStyle.borderRadius,
height: 35,
fontSize: 16,
marginTop: "16px"
@@ -523,7 +527,8 @@ const Priorities = memo((props) => {
}
}}
style={{
backgroundColor: "rgba(33, 33, 33, 1)",
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
color: theme.palette.textFieldStyle.color,
borderRadius: 4,
height: 35,
fontSize: 16,
@@ -548,7 +553,7 @@ const Priorities = memo((props) => {
{notificationWorkflow === undefined || notificationWorkflow === null || notificationWorkflow.length === 0 ? null :
<div>
<Button variant="outlined" color="secondary" style={{marginTop: 5, textTransform: "none", }} onClick={() => {
<Button disableElevation variant="outlined" color="secondary" style={{marginTop: 5, textTransform: "none", }} onClick={() => {
if (notificationWorkflow === "parent") {
toast.error("Can't send test notifications to the parent org's notification workflow.")
return
@@ -599,21 +604,21 @@ const Priorities = memo((props) => {
</div>
}
<Typography style={{marginTop: 50, fontSize: 24, fontWeight: 'bold', display: clickedFromOrgTab?null:"inline", marginBottom: clickedFromOrgTab? 8:null, color: clickedFromOrgTab?"#ffffff":null }}>Notifications ({
<Typography variant="h5" style={{marginTop: 50, fontSize: 24, display: clickedFromOrgTab?null:"inline", marginBottom: clickedFromOrgTab? 8:null, }}>Notifications ({
notifications?.filter((notification) => showRead === true || notification.read === false).length
})</Typography>
<span style={{ fontSize: 16, marginLeft: clickedFromOrgTab?null:25, color: clickedFromOrgTab?"#9E9E9E":null, }}>
<Typography variant="body2" color="textSecondary" 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" }}
style={{ textDecoration: clickedFromOrgTab?null:"none", color: theme.palette.linkColor }}
>
Learn more
</a>
</span>
</Typography>
<div/>
<div style={{display: "flex", marginTop: 10, marginBottom: 10, }}>
<Switch
@@ -621,7 +626,7 @@ const Priorities = memo((props) => {
onChange={() => {
setShowRead(!showRead);
}}
/><span style={{marginTop: 5, }}>&nbsp; Show read </span>
/><Typography style={{marginTop: 5, }}>&nbsp; Show read </Typography>
{notifications !== undefined && notifications !== null && notifications.length > 1 ? (
<Button
color="primary"
@@ -641,18 +646,18 @@ const Priorities = memo((props) => {
{clickedFromOrgTab? null : <Divider style={{marginTop: 50, marginBottom: 50, }} />}
<h2 style={{ display: clickedFromOrgTab ? null:"inline", marginBottom: clickedFromOrgTab ? 8:null, marginTop: clickedFromOrgTab ? 60 : null, color: clickedFromOrgTab ? "#ffffff" : null }}>Suggestions</h2>
<span style={{ fontSize: 16, color: clickedFromOrgTab ?"#9E9E9E":null,marginLeft: clickedFromOrgTab ?null:25, }}>
<Typography variant="h5" style={{ display: clickedFromOrgTab ? null:"inline", marginBottom: clickedFromOrgTab ? 8:null, marginTop: clickedFromOrgTab ? 60 : null, }}>Suggestions</Typography>
<Typography variant="body2" color="texSecondary" style={{ fontSize: 16, 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" }}
style={{ textDecoration: clickedFromOrgTab ?null:"none", color: clickedFromOrgTab ? theme.palette.linkColor :"#f85a3e" }}
>
Learn more
</a>
</span>
</Typography>
<div style={{marginTop: 10, }}/>
<Switch
checked={showDismissed}
@@ -699,7 +704,8 @@ const NotificationItem = memo((props) => {
var image = "";
var orgName = "";
var orgId = "";
const { themeMode, brandColor } = useContext(Context);
const theme = getTheme(themeMode, brandColor);
var highlighted = selectedExecutionId === "" && selectedWorkflow === "" ? false : data.reference_url === undefined || data.reference_url === null || data.reference_url.length === 0 ? false : data.reference_url.includes(selectedExecutionId) || data.reference_url.includes(selectedWorkflow)
@@ -755,16 +761,21 @@ const NotificationItem = memo((props) => {
}
return (
<Paper
style={{
backgroundColor: theme.palette.inputColor.backgroundColor,
width: clickedFromOrgTab ? null :notificationWidth,
padding: 30,
borderBottom: "1px solid rgba(255,255,255,0.4)",
marginBottom: 20,
border: highlighted ? "2px solid #f85a3e" : null,
borderRadius: theme.palette?.borderRadius,
}}
<Box
style={{
backgroundColor: theme.palette.cardBackgroundColor,
width: clickedFromOrgTab ? null : notificationWidth,
padding: 30,
borderBottom: theme.palette.defaultBorder,
marginBottom: 20,
border: highlighted ? "2px solid #f85a3e" : null,
borderRadius: theme.palette?.borderRadius,
}}
sx={{
"&:hover": {
backgroundColor: theme.palette.cardHoverColor,
},
}}
>
<div style={{display: "flex", }}>
{data.amount === 1 && data.read === false ?
@@ -786,8 +797,6 @@ const NotificationItem = memo((props) => {
{data.read === false ?
<Chip
label={"Unread"}
variant="contained"
color="secondary"
style={{marginRight: 15, height: 25, }}
/>
:
@@ -813,23 +822,23 @@ const NotificationItem = memo((props) => {
</Typography >
<div style={{ display: "flex" }}>
<ButtonGroup style={{marginTop: 15, minHeight: 50, maxHeight: 50, }}>
<Button
style={{
textTransform: "none",
border: "1px solid #ff8544",
color: "#ff8544",
opacity: data.reference_url ? 1 : 0.5,
cursor: data.reference_url ? "pointer" : "not-allowed",
}}
disabled={
!data.reference_url || data.reference_url.length === 0
}
onClick={() => {
window.open(data.reference_url, "_blank");
}}
>
Explore
</Button>
<Button
variant="outlined"
color="primary"
style={{
textTransform: "none",
opacity: data.reference_url ? 1 : 0.5,
cursor: data.reference_url ? "pointer" : "not-allowed",
}}
disabled={
!data.reference_url || data.reference_url.length === 0
}
onClick={() => {
window.open(data.reference_url, "_blank");
}}
>
Explore
</Button>
{data.read === false ? (
<Button
@@ -922,7 +931,7 @@ const NotificationItem = memo((props) => {
</Typography>
</div>
</Paper>
</Box>
);
})
+11 -11
View File
@@ -1,10 +1,10 @@
import React, { useState, useEffect } from "react";
import React, { useState, useEffect, useContext } from "react";
import { toast } from 'react-toastify';
import { getTheme } from "../theme.jsx";
import ReactGA from 'react-ga4';
import theme from "../theme.jsx";
import { useNavigate, Link } from "react-router-dom";
import { findSpecificApp } from "../components/AppFramework.jsx"
import { Context } from "../context/ContextApi.jsx";
import {
Paper,
Typography,
@@ -23,7 +23,8 @@ import {
const Priority = (props) => {
const { globalUrl, clickedFromOrgTab,userdata, serverside, priority, checkLogin, setAdminTab, setCurTab, appFramework, } = props;
const { themeMode, supportEmail } = useContext(Context);
const theme = getTheme(themeMode);
const isCloud = (window.location.host === "localhost:3002" || window.location.host === "shuffler.io") ? true : (process.env.IS_SSR === "true");
let navigate = useNavigate();
@@ -113,7 +114,7 @@ const Priority = (props) => {
}
})
.catch((error) => {
toast("Failed dismissing alert. Please contact support@shuffler.io if this persists.");
toast(`Failed dismissing alert. Please contact ${supportEmail} if this persists.`);
});
}
@@ -121,10 +122,10 @@ const Priority = (props) => {
const srcSize = realignedSrc ? 35 : 30
const dstSize = realignedDst ? 35 : 30
return (
<div style={{border: priority.active === false ? "1px solid #000000" : priority.severity === 1 ? "1px solid #f85a3e" : clickedFromOrgTab ?null:"1px solid rgba(255,255,255,0.3)", borderRadius: theme.palette?.borderRadius, marginTop: 10, marginBottom: 10, padding: clickedFromOrgTab ? 24:15, textAlign: "center", minHeight: isCloud ? 70 : 100, maxHeight: isCloud ? 70 : 100, textAlign: "left", backgroundColor: clickedFromOrgTab ? "#1A1A1A": theme.palette.surfaceColor, display: "flex", }}>
<div style={{border: priority.active === false ? "1px solid #000000" : priority.severity === 1 ? "1px solid #f85a3e" : clickedFromOrgTab ?null:"1px solid rgba(255,255,255,0.3)", borderRadius: theme.palette?.borderRadius, marginTop: 10, marginBottom: 10, padding: clickedFromOrgTab ? 24:15, textAlign: "center", minHeight: isCloud ? 70 : 100, maxHeight: isCloud ? 70 : 100, textAlign: "left", backgroundColor: clickedFromOrgTab ? theme.palette.backgroundColor : theme.palette.surfaceColor, display: "flex", }}>
<div style={{flex: 2, overflow: "hidden",}}>
<span style={{display: "flex", }}>
{priority.type === "usecase" || priority.type == "apps" ? <AutoFixHighIcon style={{height: 19, width: 19, marginLeft: 3, marginRight: 10, }}/> : null}
{priority.type === "usecase" || priority.type == "apps" ? <AutoFixHighIcon style={{height: 19, width: 19, marginLeft: 3, marginRight: 10, color: theme.palette.text.primary}}/> : null}
<Typography variant="body1" >
{priority.name}
</Typography>
@@ -138,7 +139,7 @@ const Priority = (props) => {
{newdescription.split("&").length > 3 ?
<span style={{display: "flex", }}>
<ArrowForwardIcon style={{marginLeft: 15, marginRight: 15, }}/>
<ArrowForwardIcon style={{marginLeft: 15, marginRight: 15, color: theme.palette.text.primary }}/>
<img src={newdescription.split("&")[3]} alt={priority.name+"2"} style={{height: dstSize, width: dstSize, marginRight: realignedDst ? -5 : 10, borderRadius: theme.palette?.borderRadius-3, marginTop: realignedDst ? 5 : 0 }} />
<Typography variant="body2" color="textSecondary" style={{marginTop: 3}}>
{newdescription.split("&")[2]}
@@ -154,7 +155,7 @@ const Priority = (props) => {
}
</div>
<div style={{flex: 1, display: "flex", marginLeft: 30, }}>
<Button style={{height: 50, borderRadius: 4, fontSize:16, boxShadow: clickedFromOrgTab ? "none":null,textTransform: clickedFromOrgTab ? 'capitalize':null, marginTop: 8, width: 175, marginRight: 10, color: priority.active === false ? "white" :clickedFromOrgTab ?"#ff8544": "black", backgroundColor: priority.active === false ? theme.palette.inputColor :clickedFromOrgTab?"transparent":"rgba(255,255,255,0.8)", border: "1px solid #ff8544"}} variant="contained" color="secondary" onClick={() => {
<Button style={{height: 50, borderRadius: 4, fontSize:16, boxShadow: clickedFromOrgTab ? "none":null,textTransform: clickedFromOrgTab ? 'capitalize':null, marginTop: 8, width: 175, marginRight: 10, }} variant="outlined" color="primary" onClick={() => {
if (isCloud) {
ReactGA.event({
@@ -180,10 +181,9 @@ const Priority = (props) => {
Explore
</Button>
{priority.active === true ?
<Button style={{borderRadius: 25, fontSize:16, boxShadow: clickedFromOrgTab ? "none":null,textTransform: clickedFromOrgTab ? 'capitalize':null, width: 100, height: 50, marginTop: 8, }} variant="text" color="secondary" onClick={() => {
<Button style={{borderRadius: 4, fontSize:16, boxShadow: clickedFromOrgTab ? "none":null,textTransform: clickedFromOrgTab ? 'capitalize':null, width: 100, height: 50, marginTop: 8, }} variant="outlined" color="secondary" onClick={() => {
// dismiss -> get envs
changeRecommendation(priority, "dismiss")
// Check window location if it's /workflows
if (window.location.pathname === "/workflows") {
// Set local storage to hide priorities for now
+9 -4
View File
@@ -1,4 +1,4 @@
import React from "react"
import React, {useContext} from "react"
import { Link } from "react-router-dom";
import {
@@ -10,14 +10,20 @@ import {
} from "@mui/material"
import { useNavigate } from "react-router";
import theme from "../theme.jsx";
import { getTheme } from "../theme.jsx";
import {
Lock as LockIcon,
} from '@mui/icons-material';
import { Context } from "../context/ContextApi.jsx";
// onclickHandler = function override from parent onclick
const RecentWorkflow = ({ workflow, onclickHandler, leftNavOpen, currentWorkflowId, }) => {
const { themeMode } = useContext(Context);
const theme = getTheme(themeMode);
const navigate = useNavigate();
const [hovered, setHovered] = React.useState(false)
@@ -87,7 +93,7 @@ const RecentWorkflow = ({ workflow, onclickHandler, leftNavOpen, currentWorkflow
transition: "opacity 0.1s",
borderRadius: theme.palette?.borderRadius,
backgroundColor: hovered || currentWorkflowId === workflow.id ? "#1f1f1f" : "transparent",
backgroundColor: hovered || currentWorkflowId === workflow.id ? theme.palette.hoverColor : "transparent",
}}
disableRipple
>
@@ -127,7 +133,6 @@ const RecentWorkflow = ({ workflow, onclickHandler, leftNavOpen, currentWorkflow
))}
<Typography
style={{
color: "#CDCDCD",
fontSize: 16,
marginLeft: 8,
maxWidth: 180,
+7 -3
View File
@@ -1,12 +1,16 @@
import React, { useState, useEffect, useLayoutEffect } from "react";
import React, { useState, useEffect, useLayoutEffect, useContext, useMemo } from "react";
import * as cytoscape from "cytoscape";
import CytoscapeComponent from "react-cytoscapejs";
import cystyle from "../defaultCytoscapeStyle.jsx";
import defaultCytoscapeStyle from "../defaultCytoscapeStyle.jsx";
import { Context } from "../context/ContextApi.jsx";
import { getTheme } from "../theme.jsx";
const surfaceColor = "#27292D";
const CytoscapeWrapper = (props) => {
const { globalUrl, inworkflow, height, width } = props;
const {themeMode} = useContext(Context)
const theme = getTheme(themeMode)
const cystyle = useMemo(() => defaultCytoscapeStyle(theme), [themeMode]);
const [elements, setElements] = useState([]);
const [workflow, setWorkflow] = useState(inworkflow);
const [cy, setCy] = React.useState();
+40 -21
View File
@@ -1,4 +1,4 @@
import React, { useState, useEffect } from 'react';
import React, { useState, useEffect, useContext } from 'react';
import {
TextField,
@@ -17,10 +17,11 @@ import {
IconButton,
Switch,
} from '@mui/material';
import { Context } from '../context/ContextApi.jsx';
import { toast } from "react-toastify"
import { makeStyles } from "@mui/styles";
import theme from '../theme.jsx';
import {getTheme} from '../theme.jsx';
import dayjs from 'dayjs';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'
import Pagination from '@mui/material/Pagination';
@@ -39,7 +40,8 @@ import {
EditNote as EditNoteIcon,
AccountTree as AccountTreeIcon,
Cached as CachedIcon,
FilterAltOff as FilterAltOffIcon
FilterAltOff as FilterAltOffIcon,
Send as SendIcon,
} from '@mui/icons-material';
import { DataGrid, GridColDef, GridValueGetterParams } from '@mui/x-data-grid'
@@ -69,6 +71,8 @@ const RuntimeDebugger = (props) => {
const [endTime, setEndTime] = useState("")
const [startTime, setStartTime] = useState("")
const [totalCount, setTotalCount] = useState(0)
const {themeMode, supportEmail} = useContext(Context)
const theme = getTheme(themeMode)
const [workflow, setWorkflow] = useState({})
const [ignoreOrg, setIgnoreOrg] = useState(false)
@@ -107,7 +111,7 @@ const RuntimeDebugger = (props) => {
var maxworkflows = 5
console.log("Looking for MAX this amount of workflows: ", maxworkflows)
//console.log("Looking for MAX this amount of workflows: ", maxworkflows)
for (let key in workflows) {
if (key > maxworkflows) {
break
@@ -347,6 +351,9 @@ const RuntimeDebugger = (props) => {
source = "rerun of a previous run"
} else if (source === "form") {
foundSource = <EditNoteIcon style={{color: theme.palette.primary.secondary, height: imageSize, width: imageSize, }} />
} else if (source === "single_action" || source == "single_api" || source === "direct_api") {
foundSource = <SendIcon color="secondary" style={{height: imageSize-5, }} />
source = "Single API call"
} else {
source = "manual"
}
@@ -354,12 +361,12 @@ const RuntimeDebugger = (props) => {
var imageSource = "";
if (params?.row?.org?.id?.length > 0) {
if (params?.row?.org?.image?.length > 0){
imageSource = params?.row.org?.image
imageSource = params?.row?.org?.image
}else {
imageSource = "/images/no_image.png"
}
}else {
if (userdata?.active_org.image?.length > 0){
} else {
if (userdata?.active_org?.image?.length > 0){
imageSource = userdata?.active_org?.image
}else {
imageSource = "/images/no_image.png"
@@ -629,7 +636,7 @@ const RuntimeDebugger = (props) => {
</Link>
</span>
</Tooltip>
<Tooltip arrow title="Force continue workflow. Only workflows for workflows in EXECUTING state. This is NOT a rerun, but way for Shuffle to figure out the next steps automatically. If the execution doesn't finish even after trying this, please contact support@shuffler.io">
<Tooltip arrow title={`Force continue workflow. Only workflows for workflows in EXECUTING state. This is NOT a rerun, but way for Shuffle to figure out the next steps automatically. If the execution doesn't finish even after trying this, please contact ${supportEmail}`}>
<IconButton
style={{marginLeft: 5, }}
disabled={params.row.status !== "EXECUTING"}
@@ -812,7 +819,7 @@ const RuntimeDebugger = (props) => {
<div style={{display: "flex", paddingTop: 50, }}>
<div style={{display: 'flex', flexDirection: 'column'}}>
<div style={{display: "flex", width: "100%", }}>
<h1 style={{flex: 3, whiteSpace: "nowrap" }}>Workflow Run Debugger {totalCount !== 0 ? ` (~${totalCount})` : ""}</h1>
<Typography variant="h3" style={{flex: 3, whiteSpace: "nowrap" }}>Workflow Run Debugger {totalCount !== 0 ? ` (~${totalCount})` : ""}</Typography>
{selectedWorkflowExecutions.length > 0 ?
<ButtonGroup>
<Tooltip title="Reruns ALL selected workflows. This will make a new execution for them, and not continue the existing.">
@@ -890,7 +897,8 @@ const RuntimeDebugger = (props) => {
fullWidth
value={searchQuery}
style={{
backgroundColor: "#1a1a1a",
backgroundColor: theme.palette.backgroundColor,
color: theme.palette.textColor,
marginTop: 20,
marginLeft: 10,
marginRight: 12,
@@ -901,7 +909,8 @@ const RuntimeDebugger = (props) => {
}}
InputProps={{
style: {
backgroundColor: "#1a1a1a",
backgroundColor: theme.palette.backgroundColor,
color: theme.palette.textColor,
fontSize: "1em",
height: 51,
width: 693,
@@ -951,7 +960,7 @@ const RuntimeDebugger = (props) => {
}
color="secondary"
/>
<Typography variant="body2" style={{color: "white", }}>Show workflow runs from suborgs</Typography>
<Typography variant="body2">Show workflow runs from suborgs</Typography>
</div>
) : null}
</div>
@@ -999,8 +1008,8 @@ const RuntimeDebugger = (props) => {
classes={{ inputRoot: classes.inputRoot }}
ListboxProps={{
style: {
backgroundColor: "#1a1a1a",
color: "white",
backgroundColor: theme.palette.backgroundColor,
color: theme.palette.textColor,
},
}}
getOptionLabel={(option) => {
@@ -1018,7 +1027,7 @@ const RuntimeDebugger = (props) => {
options={workflows}
fullWidth
style={{
backgroundColor: "#1a1a1a",
backgroundColor: theme.palette.backgroundColor,
height: 50,
borderRadius: 4,
marginLeft: 5,
@@ -1063,8 +1072,13 @@ const RuntimeDebugger = (props) => {
}>
<MenuItem
style={{
backgroundColor: "#1a1a1a",
color: data.id === workflow.id ? "red" : "white",
backgroundColor: theme.palette.backgroundColor,
color: data.id === workflow.id ? "red" : theme.palette.textColor,
}}
sx={{
"&:hover": {
backgroundColor: theme.palette.hoverColor,
},
}}
value={data}
onClick={(e) => {
@@ -1082,9 +1096,16 @@ const RuntimeDebugger = (props) => {
return (
<TextField
style={{
backgroundColor: "#1a1a1a",
backgroundColor: theme.palette.backgroundColor,
color: theme.palette.textColor,
borderRadius: 4,
}}
inputProps={{
style: {
backgroundColor: theme.palette.backgroundColor,
color: theme.palette.textColor,
},
}}
{...params}
label="Workflow"
variant="outlined"
@@ -1128,9 +1149,7 @@ const RuntimeDebugger = (props) => {
<Tooltip title="Clear all filters and search parameters">
<Button
style={{ marginLeft: 10, minHeight: 60, marginTop: 10, backgroundColor: "#1a1a1a", border: "1px solid #424242", boxShadow: 'none', borderRadius: 4, width: 81, height: 51, marginRight: 15 }}
variant="contained"
color="primary"
style={{ marginLeft: 10, minHeight: 60, marginTop: 10, backgroundColor: theme.palette.backgroundColor, border: "1px solid #424242", boxShadow: 'none', borderRadius: 4, width: 81, height: 51, marginRight: 15 }}
onClick={() => {
setWorkflowId("")
setWorkflow({"id": "", "name": "All Workflows"})
+59 -58
View File
@@ -1,5 +1,5 @@
import React, { forwardRef, memo, useContext, useEffect } from 'react';
import theme from "../theme.jsx";
import {getTheme} from "../theme.jsx";
import { toast } from "react-toastify" ;
import {
Divider,
@@ -33,6 +33,9 @@ const SchedulesTab = memo((props) => {
const [pipelineModalOpen, setPipelineModalOpen] = React.useState(false);
const [newPipelineValue, setNewPipelineValue] = React.useState("export | sigma /tmp/sigma_rules | to SHUFFLE_WEBHOOK");
const { themeMode, brandColor } = useContext(Context);
const theme = getTheme(themeMode, brandColor);
useEffect(() => {
if (allSchedules.length === 0 && webHooks.length === 0 && pipelines.length === 0) {
handleGetAllTriggers()
@@ -142,11 +145,11 @@ const SchedulesTab = memo((props) => {
}}
>
<DialogTitle>
<span style={{ color: "white" }}>
<Typography variant='h5' color="textPrimary" >
Run a Tenzir pipeline
</span>
</Typography>
<Typography variant="body2" color="textSecondary" style={{marginTop: 10, }}>
Alpha feature. Deploys to the first available Orborus location. <a href="https://docs.tenzir.com/pipelines" target="_blank" rel="noopener noreferrer" style={{ color: "#ff8544" }}>Explore Tenzir Pipelines</a>. The example below exports everything in the Tenzir database, runs Sigma rules on it, and forwards the results to a Shuffle webhook.
Alpha feature. Deploys to the first available Orborus location. <a href="https://docs.tenzir.com/pipelines" target="_blank" rel="noopener noreferrer" style={{ color: theme.palette.primary.main }}>Explore Tenzir Pipelines</a>. The example below exports everything in the Tenzir database, runs Sigma rules on it, and forwards the results to a Shuffle webhook.
</Typography>
</DialogTitle>
<DialogContent>
@@ -173,17 +176,16 @@ const SchedulesTab = memo((props) => {
</DialogContent>
<DialogActions>
<Button
style={{ borderRadius: "2px", fontSize: 16, textTransform: "none", color: "#ff8544" }}
style={{ borderRadius: "2px", fontSize: 16, textTransform: "none", color: theme.palette.primary.main }}
onClick={() => {
setPipelineModalOpen(false)
}}
color="primary"
>
Cancel
</Button>
<Button
variant="contained"
style={{ borderRadius: "2px", fontSize: 16, textTransform: "none", backgroundColor: "#ff8544", color: "#1a1a1a" }}
style={{ borderRadius: "2px", fontSize: 16, textTransform: "none", }}
onClick={() => {
submitPipelineWrapper(newPipelineValue)
}}
@@ -472,7 +474,7 @@ const SchedulesTab = memo((props) => {
height: "100%",
transition: 'width 0.3s ease',
padding: '27px 10px 27px 27px',
backgroundColor: '#212121',
backgroundColor: theme.palette.platformColor,
borderTopRightRadius: 8,
borderBottomRightRadius: 8,
borderLeft: '1px solid #494949',
@@ -480,36 +482,36 @@ const SchedulesTab = memo((props) => {
{NewPipelineView}
<div style={{height: "100%", maxHeight: 1700, overflowY: "auto", scrollbarColor: '#494949 transparent', scrollbarWidth: 'thin'}}>
<h2 style={{ marginBottom: 8, marginTop: 0, color: "#FFFFFF" }}>
<div style={{height: "100%", maxHeight: 1700, overflowY: "auto", scrollbarColor: theme.palette.scrollbarColorTransparent, scrollbarWidth: 'thin'}}>
<Typography variant='h5' color="textPrimary" style={{ marginBottom: 8, marginTop: 0, }}>
Triggers
</h2>
<Typography variant="body1" style={{ marginBottom: 50, marginTop: 0, }} color="textSecondary">
</Typography>
<Typography variant="body2" style={{ marginBottom: 50, marginTop: 0, }} color="textSecondary">
Triggers are Automatic Workflow starters. <b>Status: Schedules ({allSchedules.length}), Webhooks ({webHooks.length}), Pipelines ({pipelines.length})</b>
</Typography>
<div>
<h4 style={{ marginBottom: 8, marginTop: 0, color: "#FFFFFF" }}>
<Typography variant='h6' color="textPrimary" style={{ marginBottom: 8, marginTop: 0, fontWeight: 500}}>
Schedules
</h4>
<span style={{color:textColor}}>
</Typography>
<Typography variant='body2' color="textSecondary">
Schedules used in Workflows. Makes locating and control easier.{" "}
<a
target="_blank"
rel="noopener noreferrer"
href="/docs/organizations#schedules"
style={{ color:"#FF8444" }}
style={{ color: theme.palette.primary.main }}
>
Learn more
</a>
</span>
</Typography>
</div>
<div style={{height: "100%", width: "calc(100% - 20px)", scrollbarColor: '#494949 transparent', scrollbarWidth: 'thin'}}>
<div style={{height: "100%", width: "calc(100% - 20px)", scrollbarColor: theme.palette.scrollbarColorTransparent, scrollbarWidth: 'thin'}}>
<div
style={{
borderRadius: 4,
marginTop: 24,
border: "1px solid #494949",
border: theme.palette.defaultBorder,
width: "100%",
overflowX: "auto",
paddingBottom: 0,
@@ -525,7 +527,7 @@ const SchedulesTab = memo((props) => {
overflowX: "auto",
paddingBottom: 0
}}>
<ListItem style={{width: "100%", paddingTop: 10, paddingBottom: 10, paddingRight: 10, borderBottom: "1px solid #494949", display: 'table-row'}}>
<ListItem style={{width: "100%", paddingTop: 10, paddingBottom: 10, paddingRight: 10, borderBottom: theme.palette.defaultBorder, display: 'table-row'}}>
{["Name", "Interval", "Environment", "Workflow", "Argument", "Action"].map((header, index) => (
<ListItemText
key={index}
@@ -535,7 +537,7 @@ const SchedulesTab = memo((props) => {
padding: index === 0 ? "0px 8px 8px 15px": "0px 8px 8px 8px",
whiteSpace: "nowrap",
textOverflow: "ellipsis",
borderBottom: "1px solid #494949",
borderBottom: theme.palette.defaultBorder,
}}
/>
))}
@@ -546,7 +548,7 @@ const SchedulesTab = memo((props) => {
key={rowIndex}
style={{
display: "table-row",
backgroundColor: "#212121",
backgroundColor: theme.palette.platformColor,
}}
>
{Array(6)
@@ -563,7 +565,7 @@ const SchedulesTab = memo((props) => {
variant="text"
animation="wave"
sx={{
backgroundColor: "#1a1a1a",
backgroundColor: theme.palette.loaderColor,
height: "20px",
borderRadius: "4px",
}}
@@ -575,13 +577,13 @@ const SchedulesTab = memo((props) => {
):(
allSchedules?.length === 0 ? (
<div style={{ textAlign: 'center'}}>
<Typography style={{color: "#FFFFFF", fontSize: 16, padding: 20, textAlign: 'center'}}>No schedules found</Typography>
<Typography style={{color: theme.palette.text.primary, fontSize: 16, padding: 20, textAlign: 'center'}}>No schedules found</Typography>
</div>
):(
allSchedules.map((schedule, index) => {
var bgColor = "#212121"
var bgColor = themeMode === "dark" ? "#212121" : "#FFFFFF";
if (index % 2 === 0) {
bgColor = "#1a1a1a";
bgColor = themeMode === "dark" ? "#1A1A1A" : "#EAEAEA";
}
return (
@@ -645,7 +647,7 @@ const SchedulesTab = memo((props) => {
rel="noopener noreferrer"
style={{
textDecoration: "none",
color: "#f85a3e",
color: theme.palette.primary.main,
}}
href={`/workflows/${schedule.workflow_id}`}
target="_blank"
@@ -658,7 +660,7 @@ const SchedulesTab = memo((props) => {
style={{
color:
schedule.workflow_id !== "global"
? "#FF8444"
? theme.palette.primary.main
: "grey",
}}
/>
@@ -698,8 +700,7 @@ const SchedulesTab = memo((props) => {
style={{
textTransform: 'none',
fontSize: 16,
color: schedule.status === "running" ? '#1a1a1a' : null,
backgroundColor: schedule.status === "running" ? '#ff8544' : null,
width: 150,
}}
color={schedule.status === "running" ? "secondary" : "primary"}
variant={schedule.status === "running" ? "contained" : "outlined"}
@@ -725,23 +726,23 @@ const SchedulesTab = memo((props) => {
</div>
<div style={{ marginTop: 50, marginBottom: 20 }}>
<h4 style={{color: "#FFFFFF"}} >Webhooks</h4>
<span> Webhooks used in Shuffle workflows.&nbsp;
<Typography variant='h6' color="textPrimary">Webhooks</Typography>
<Typography variant='body2' color="textSecondary"> Webhooks used in Shuffle workflows.&nbsp;
<a
target="_blank"
rel="noopener noreferrer"
href="/docs/triggers#webhooks"
style={{ textDecoration: "none", color: "#f85a3e" }}
style={{ color: theme.palette.primary.main }}
>
Learn more
</a>
</span>
</Typography>
</div>
<div
style={{
borderRadius: 4,
marginTop: 24,
border: "1px solid #494949",
border: theme.palette.defaultBorder,
width: "100%",
overflowX: "auto",
paddingBottom: 0,
@@ -757,7 +758,7 @@ const SchedulesTab = memo((props) => {
paddingBottom: 0,
minWidth: 600,
}}>
<ListItem style={{width:"100%", borderBottom:"1px solid #494949", display: "table-row"}}>
<ListItem style={{width:"100%", borderBottom:theme.palette.defaultBorder, display: "table-row"}}>
{["Name", "Environment", "Workflow", "URL", "Action"].map((header, index) => (
<ListItemText
key={index}
@@ -767,7 +768,7 @@ const SchedulesTab = memo((props) => {
padding: index === 0 ? "0px 8px 8px 15px": "0px 8px 8px 8px",
whiteSpace: "nowrap",
textOverflow: "ellipsis",
borderBottom: "1px solid #494949",
borderBottom: theme.palette.defaultBorder,
position: "sticky",
}}
/>
@@ -779,7 +780,7 @@ const SchedulesTab = memo((props) => {
key={rowIndex}
style={{
display: "table-row",
backgroundColor: "#212121",
backgroundColor: theme.palette.platformColor,
}}
>
{Array(5)
@@ -796,7 +797,7 @@ const SchedulesTab = memo((props) => {
variant="text"
animation="wave"
sx={{
backgroundColor: "#1a1a1a",
backgroundColor: theme.palette.loaderColor,
height: "20px",
borderRadius: "4px",
}}
@@ -808,13 +809,13 @@ const SchedulesTab = memo((props) => {
):(
webHooks?.length === 0 ? (
<div style={{textAlign: "center"}}>
<Typography style={{color: "#FFFFFF", padding: 20, fontSize: 16, textAlign: 'center'}}>No webhooks found</Typography>
<Typography style={{color: theme.palette.text.primary, padding: 20, fontSize: 16, textAlign: 'center'}}>No webhooks found</Typography>
</div>
):(
webHooks.map((webhook, index) => {
var bgColor = "#212121"
var bgColor = themeMode === "dark" ? "#212121" : "#FFFFFF";
if (index % 2 === 0) {
bgColor = "#1a1a1a";
bgColor = themeMode === "dark" ? "#1A1A1A" : "#EAEAEA";
}
return (
@@ -840,7 +841,7 @@ const SchedulesTab = memo((props) => {
rel="noopener noreferrer"
style={{
textDecoration: "none",
color: "#f85a3e",
color: theme.palette.primary.main,
}}
href={`/workflows/${webhook.workflows[0]}`}
target="_blank"
@@ -853,7 +854,7 @@ const SchedulesTab = memo((props) => {
style={{
color:
webhook.workflows[0].workflow_id !== "global"
? "#FF8444"
? theme.palette.primary.main
: "grey",
}}
/>
@@ -904,6 +905,7 @@ const SchedulesTab = memo((props) => {
fontSize: 16,
color:webhook.status === "running" ? '#1a1a1a' : null,
backgroundColor: webhook.status === "running" ? '#ff8544' : null,
width: 150,
}}
color={webhook.status === "running" ? "secondary" : "primary"}
variant={webhook.status === "running" ? "contained" : "outlined"}
@@ -928,24 +930,24 @@ const SchedulesTab = memo((props) => {
</List>
</div>
<div style={{ marginTop: 50, marginBottom: 20 }}>
<h4 style={{color: "#FFFFFF"}}>Pipelines</h4>
<Typography variant='h6' color="textPrimary" >Pipelines</Typography>
<span>
<Typography variant='body2' color="textSecondary" >
Controls Pipelines on your Orborus Runners, e.g. for Log Ingestion, MQ subscriptions or Sigma Detections.{" "}
<a
target="_blank"
rel="noopener noreferrer"
href="/docs/triggers#pipelines"
style={{ textDecoration: "none", color: "#f85a3e" }}
style={{ color: theme.palette.primary.main }}
>
Learn more
</a>
</span>
</Typography>
<div style={{marginBottom: 10, marginTop: 10, }}/>
<Button
style={{ backgroundColor: '#ff8544', color: "#1a1a1a", borderRadius: 4, textTransform: "capitalize", fontSize: 16, }}
style={{ borderRadius: 4, textTransform: "capitalize", fontSize: 16, }}
variant="contained"
color="primary"
onClick={() => setPipelineModalOpen(true)}
@@ -958,7 +960,7 @@ const SchedulesTab = memo((props) => {
style={{
borderRadius: 4,
marginTop: 24,
border: "1px solid #494949",
border: theme.palette.defaultBorder,
width: "100%",
overflowX: pipelines?.length === 0 ? "hidden" : "auto",
paddingBottom: 0,
@@ -974,7 +976,7 @@ const SchedulesTab = memo((props) => {
overflowX: "auto",
paddingBottom: 0
}}>
<ListItem style={{width:"100%", borderBottom:"1px solid #494949", display: "table-row"}}>
<ListItem style={{width:"100%", borderBottom:theme.palette.defaultBorder, display: "table-row"}}>
{["Command", "Environment", "Total Runs", "Actions"].map((header, index) => (
<ListItemText
key={index}
@@ -984,7 +986,7 @@ const SchedulesTab = memo((props) => {
padding: index === 0 ? "0px 8px 8px 15px": "0px 8px 8px 8px",
whiteSpace: "nowrap",
textOverflow: "ellipsis",
borderBottom: "1px solid #494949",
borderBottom: theme.palette.defaultBorder,
position: "sticky",
}}
/>
@@ -997,7 +999,7 @@ const SchedulesTab = memo((props) => {
key={rowIndex}
style={{
display: "table-row",
backgroundColor: "#212121",
backgroundColor: theme.palette.platformColor,
}}
>
{Array(5)
@@ -1015,7 +1017,7 @@ const SchedulesTab = memo((props) => {
variant="text"
animation="wave"
sx={{
backgroundColor: "#1a1a1a",
backgroundColor: theme.palette.loaderColor,
height: "20px",
borderRadius: "4px",
}}
@@ -1031,14 +1033,14 @@ const SchedulesTab = memo((props) => {
): (
pipelines?.length === 0 ? (
<div style={{width: "100%", textAlign: "center", }}>
<Typography style={{color: "#FFFFFF", padding: 20,width: "100%", fontSize: 16, textAlign: 'center'}}>No pipeline trigger found</Typography>
<Typography style={{color: theme.palette.text.primary, padding: 20,width: "100%", fontSize: 16, textAlign: 'center'}}>No pipeline trigger found</Typography>
</div>
):(
pipelines.map((pipeline, index) => {
var bgColor = "#212121"
var bgColor = themeMode === "dark" ? "#212121" : "#FFFFFF";
if (index % 2 === 0) {
bgColor = "#1a1a1a";
bgColor = themeMode === "dark" ? "#1A1A1A" : "#EAEAEA";
}
return (
@@ -1094,4 +1096,3 @@ const SchedulesTab = memo((props) => {
});
export default SchedulesTab;
+31 -19
View File
@@ -1,6 +1,6 @@
import React, { useState, useEffect, useRef, useContext } from 'react';
import theme from '../theme.jsx';
import {getTheme} from '../theme.jsx';
import { useNavigate, Link, useParams } from "react-router-dom";
import { toast } from "react-toastify"
@@ -58,6 +58,8 @@ const SearchData = props => {
const [oldPath, setOldPath] = useState("")
const [value, setValue] = useState("");
const isDocSearchModal = isDocSearchModalOpen;
const {themeMode, supportEmail} = useContext(Context);
const theme = getTheme(themeMode)
const handleLinkClick = () => {
if (searchBarModalOpen) {
@@ -140,14 +142,13 @@ const SearchData = props => {
>
<TextField
fullWidth
style={{ zIndex: 1100, marginTop: -20, marginBottom: 200, position: "fixed", backgroundColor: theme.palette.inputColor, borderRadius: borderRadius, width: 690, }}
style={{ zIndex: 1100, marginTop: -20, marginBottom: 200, position: "fixed", backgroundColor: theme.palette.textFieldStyle.backgroundColor, borderRadius: borderRadius, width: 690, }}
InputProps={{
style: {
color: "white",
color: theme.palette.textFieldStyle.color,
fontSize: "1em",
height: 50,
margin: 0,
fontSize: "0.9em",
paddingLeft: 10,
},
disableUnderline: true,
@@ -217,12 +218,12 @@ const SearchData = props => {
const baseImage = <CodeIcon />
return (
<Card elevation={0} style={{ marginRight: 10, marginTop: 50, color: "white", zIndex: 1002, backgroundColor: theme.palette.inputColor, width: "100%", left: 75, boxShadows: "none", }}>
<Card elevation={0} style={{ marginRight: 10, marginTop: 50, color: theme.palette.text.primary, zIndex: 1002, backgroundColor: theme.palette.textFieldStyle.backgroundColor, width: "100%", left: 75, boxShadows: "none", }}>
<Typography variant="h6" style={{ margin: "10px 10px 0px 20px", color: "#FF8444", borderBottom: "1px solid", width: 105 }}>
Workflows
</Typography>
<List style={{ backgroundColor: theme.palette.inputColor, }}>
<List style={{ backgroundColor: themeMode === "dark" ? "#2A2A2A" : "#F5F5F5", }}>
{hits.length === 0 ?
<ListItem style={outerlistitemStyle}>
<ListItemAvatar onClick={() => console.log(hits)}>
@@ -242,7 +243,7 @@ const SearchData = props => {
overflowX: "hidden",
overflowY: "hidden",
borderBottom: "1px solid rgba(255,255,255,0.4)",
backgroundColor: mouseHoverIndex === index ? "#1f2023" : "inherit",
backgroundColor: mouseHoverIndex === index ? theme.palette.hoverColor : "inherit",
cursor: "pointer",
marginLeft: 5,
marginRight: 5,
@@ -396,14 +397,14 @@ const SearchData = props => {
})
.then((response) => {
if (response.status !== 200) {
toast(`Failed to ${type} the app for your organization. Please try again or contact support@shuffler.io`)
toast(`Failed to ${type} the app for your organization. Please try again or contact ${supportEmail}`)
}
return response.json()
})
.then((responseJson) => {
if (responseJson.success === false) {
toast(`Failed to ${type} the app for your organization. Please try again or contact support@shuffler.io for more info`)
toast(`Failed to ${type} the app for your organization. Please try again or contact ${supportEmail} for more info`)
} else {
toast(`App successfully ${type}d. It may now be used in your workflows.`)
}
@@ -438,7 +439,7 @@ const SearchData = props => {
const baseImage = <LibraryBooksIcon />
return (
<Card elevation={0} style={{ marginRight: 10, color: "white", zIndex: 999, backgroundColor: theme.palette.inputColor, width: 685, boxShadows: "none", }}>
<Card elevation={0} style={{ marginRight: 10, color: "white", zIndex: 999, backgroundColor: theme.palette.textFieldStyle.backgroundColor, width: 685, boxShadows: "none", }}>
{/* <IconButton style={{ zIndex: 5000, position: "absolute", right: 14, color: "grey" }} onClick={() => {
setSearchOpen(false)
}}>
@@ -448,7 +449,7 @@ const SearchData = props => {
Apps
</Typography>
<List style={{ backgroundColor: theme.palette.inputColor, }}>
<List style={{ backgroundColor: themeMode === "dark" ? "#2A2A2A" : "#F5F5F5", }}>
{hits.length === 0 ?
<ListItem style={outerlistitemStyle}>
<ListItemAvatar onClick={() => console.log(hits)}>
@@ -468,7 +469,7 @@ const SearchData = props => {
overflowX: "hidden",
overflowY: "hidden",
borderBottom: "1px solid rgba(255,255,255,0.4)",
backgroundColor: mouseHoverIndex === index ? "#1f2023" : "inherit",
backgroundColor: mouseHoverIndex === index ? theme.palette.hoverColor : "inherit",
cursor: "pointer",
marginLeft: 5,
marginRight: 5,
@@ -630,7 +631,7 @@ const SearchData = props => {
//console.log(type, hits.length, hits)
return (
<Card elevation={0} style={{ marginRight: 10, marginTop: isDocSearchModal ? 0 : 50, color: "white", zIndex: 1002, backgroundColor: theme.palette.inputColor, width: "100%", left: 470, boxShadows: "none", }}>
<Card elevation={0} style={{ marginRight: 10, marginTop: isDocSearchModal ? 0 : 50, color: "white", zIndex: 1002, backgroundColor: theme.palette.textFieldStyle.backgroundColor, width: "100%", left: 470, boxShadows: "none", }}>
{/* <IconButton style={{ zIndex: 5000, position: "absolute", right: 14, color: "grey" }} onClick={() => {
setSearchOpen(false)
}}>
@@ -650,7 +651,7 @@ const SearchData = props => {
<DeleteIcon />
</IconButton>
*/}
<List style={{ backgroundColor: theme.palette.inputColor, marginTop: isDocSearchModal ? 35 : 0, }}>
<List style={{ backgroundColor: themeMode === "dark" ? "#2A2A2A" : "#F5F5F5", marginTop: isDocSearchModal ? 35 : 0, }}>
{hits.length === 0 ?
<ListItem style={outerlistitemStyle}>
<ListItemAvatar onClick={() => console.log(hits)}>
@@ -670,7 +671,7 @@ const SearchData = props => {
overflowX: "hidden",
overflowY: "hidden",
borderBottom: "1px solid rgba(255,255,255,0.4)",
backgroundColor: mouseHoverIndex === index ? "#1f2023" : "inherit",
backgroundColor: mouseHoverIndex === index ? theme.palette.hoverColor : "inherit",
cursor: "pointer",
marginLeft: 5,
marginRight: 5,
@@ -787,13 +788,20 @@ const SearchData = props => {
window.open(modifiedUrl, '_blank');
};
return (
<Card elevation={0} style={{ marginRight: 10, marginTop: 50, color: "white", zIndex: 1002, backgroundColor: theme.palette.inputColor, width: "100%", left: 470, boxShadows: "none", }}>
<Card elevation={0} style={{ marginRight: 10, marginTop: 50, color: "white", zIndex: 1002, backgroundColor: theme.palette.textFieldStyle.backgroundColor, width: "100%", left: 470, boxShadows: "none", }}>
<Typography variant="h6" style={{ margin: "10px 10px 0px 20px", color: "#FF8444", borderBottom: "1px solid", width: 152 }}>
Discord Chat
</Typography>
<List style={{ backgroundColor: theme.palette.inputColor, }}>
<List style={{ backgroundColor: themeMode === "dark" ? "#2A2A2A" : "#F5F5F5", }}>
{hits.length === 0 ?
<ListItem style={outerlistitemStyle}>
<ListItem
sx={{
"&:hover": {
backgroundColor: theme.palette.hoverColor
}
}}
style={outerlistitemStyle}
>
<ListItemAvatar onClick={() => console.log(hits)}>
<Avatar>
<FolderIcon />
@@ -805,7 +813,11 @@ const SearchData = props => {
/>
</ListItem>:
hits.map((chat, index) => (
<ListItem onClick={() => handleHitClick(chat.url)} key={index} style={{ cursor: "pointer", borderBottom: "1px solid rgba(255,255,255,0.4)" }} onMouseOver={() => setMouseHoverIndex(index)}>
<ListItem sx={{
"&:hover": {
backgroundColor: theme.palette.hoverColor
}
}} onClick={() => handleHitClick(chat.url)} key={index} style={{ cursor: "pointer", borderBottom: "1px solid rgba(255,255,255,0.4)" }} onMouseOver={() => setMouseHoverIndex(index)}>
<ListItemAvatar>
<Avatar src="/discord-logo.png" />
</ListItemAvatar>
+4 -3
View File
@@ -1,6 +1,6 @@
import React, { useState, useEffect, useRef, useContext } from 'react';
import theme from '../theme.jsx';
import {getTheme} from '../theme.jsx';
import { useNavigate, Link, useParams } from "react-router-dom";
import SearchBox from "../components/SearchData.jsx";
@@ -46,7 +46,8 @@ const chipStyle = {
const SearchField = props => {
const { serverside, userdata, isMobile, isLoaded, globalUrl, isHeader, isLoggedIn, small, rounded } = props
const {themeMode} = useContext(Context);
const theme = getTheme(themeMode);
const {searchBarModalOpen, setSearchBarModalOpen} = useContext(Context);
let navigate = useNavigate();
@@ -87,7 +88,7 @@ const SearchField = props => {
height: 785,
borderRadius: 16,
border: "1px solid var(--Container-Stroke, #494949)",
background: "var(--Container, #000000)",
background: theme.palette.DialogStyle.backgroundColor,
boxShadow: "0px 16px 24px 8px rgba(0, 0, 0, 0.25)",
zIndex: 13000,
},
+29 -19
View File
@@ -1,4 +1,4 @@
import React, { useRef, useState, useEffect, useLayoutEffect, } from 'react';
import React, { useRef, useState, useEffect, useLayoutEffect, useContext } from 'react';
import { toast } from 'react-toastify';
import '../codeeditor-index.css';
import {
@@ -17,8 +17,8 @@ import {
ButtonGroup,
Collapse,
} from '@mui/material';
import theme from '../theme.jsx';
import { Context } from '../context/ContextApi.jsx';
import {getTheme} from '../theme.jsx';
import Checkbox from '@mui/material/Checkbox';
import { isMobile } from "react-device-detect"
import { NestedMenuItem } from "mui-nested-menu"
@@ -149,6 +149,8 @@ const CodeEditor = (props) => {
// value: localcodedata,
//})
// const {codelang, setcodelang} = props
const {themeMode, supportEmail} = useContext(Context)
const theme = getTheme(themeMode)
const [validation, setValidation] = React.useState(false);
const [expOutput, setExpOutput] = React.useState(" ");
@@ -1396,7 +1398,7 @@ const CodeEditor = (props) => {
const usedposition = e.offsetY
if (usedposition === undefined || usedposition === null) {
toast.info("Error: LayerY is undefined or null. Please contact support@shuffler.io")
toast.info(`Error: LayerY is undefined or null. Please contact ${supportEmail}`)
return
}
@@ -1523,17 +1525,17 @@ const CodeEditor = (props) => {
setActiveDialog("codeeditor")
}
},
style: {
sx: {
// zIndex: 12501,
pointerEvents: "auto",
color: "white",
minWidth: isMobile ? "100%" : isFileEditor ? 650 : "80%",
maxWidth: isMobile ? "100%" : isFileEditor ? 650 : 1100,
color: theme.palette.DialogStyle.color,
minWidth: isMobile ? "100%" : isFileEditor ? "650px" : "80%",
maxWidth: isMobile ? "100%" : isFileEditor ? "650px" : "1100px",
minHeight: isMobile ? "100%" : "auto",
maxHeight: isMobile ? "100%" : 700,
maxHeight: isMobile ? "100%" : "700px",
border: "3px solid rgba(255,255,255,0.3)",
padding: isMobile ? "25px 10px 25px 10px" : 25,
backgroundColor: "black",
padding: isMobile ? "25px 10px 25px 10px" : "25px",
backgroundColor: themeMode === "dark" ? "black" : theme.palette.DialogStyle.backgroundColor,
},
}}
>
@@ -1584,7 +1586,10 @@ const CodeEditor = (props) => {
color: "grey",
}}
onClick={() => {
navigate("")
if (isFileEditor !== true) {
navigate("")
}
setExpansionModalOpen(false)
}}
>
@@ -1728,6 +1733,7 @@ const CodeEditor = (props) => {
style={{
textTransform: "none",
width: 175,
textWrap: 'nowrap'
}}
onClick={(event) => {
setSourceDataOpen(!sourceDataOpen)
@@ -1747,6 +1753,7 @@ const CodeEditor = (props) => {
style={{
textTransform: "none",
width: 120,
textWrap: "nowrap"
}}
onClick={(event) => {
setAnchorEl(event.currentTarget);
@@ -1783,6 +1790,7 @@ const CodeEditor = (props) => {
style={{
textTransform: "none",
width: 145,
textWrap: "nowrap"
}}
onClick={(event) => {
setAnchorEl3(event.currentTarget);
@@ -1800,6 +1808,7 @@ const CodeEditor = (props) => {
style={{
textTransform: "none",
width: 130,
textWrap: "nowrap",
}}
onClick={(event) => {
setMenuPosition({
@@ -2266,7 +2275,9 @@ const CodeEditor = (props) => {
paddingLeft: 10,
paddingTop: 0,
display: "flex",
cursor: "move"
cursor: "move",
color: theme.palette.DialogStyle.color,
backgroundColor: "transparent",
}}
>
<div>
@@ -2294,7 +2305,7 @@ const CodeEditor = (props) => {
{
selectedEdge && Object.keys(selectedEdge).length > 0 ?
<ArrowForwardIcon style={{
color: "rgba(255,255,255,0.7)",
color: theme.palette.textPrimary,
fontSize: 18,
marginLeft: -5,
marginRight: -5,
@@ -2319,7 +2330,7 @@ const CodeEditor = (props) => {
}
</div>
:
<span style={{ color: "white" }}>
<span style={{ color: theme.palette.text.primary }}>
{selectedAction.name === "execute_python" || selectedAction.name === "execute_bash" ?
"Code to run" :
triggerId ?
@@ -2337,9 +2348,7 @@ const CodeEditor = (props) => {
<Tooltip title="Try it! This runs the Shuffle Tools 'repeat back to me' or 'execute python' action with what you see in the expected output window. Commonly used to test your Python scripts or Liquid filters, not requiring the full workflow to run again." placement="top">
<Button
id="try-it-button"
variant="outlined"
disabled={executing}
color="primary"
style={{
border: `1px solid rgba(255, 255, 255, 0.15)`,
position: "absolute",
@@ -2351,11 +2360,12 @@ const CodeEditor = (props) => {
fontWeight: 500,
fontSize: 14,
textTransform: "none",
backgroundColor: "rgba(33, 33, 33, 0.95)",
backgroundColor: theme.palette.platformColor,
backdropFilter: "blur(8px)",
boxShadow: "0 4px 6px rgba(0, 0, 0, 0.1), 0 1px 3px rgba(0, 0, 0, 0.08)",
transition: "all 0.2s ease",
paddingRight: 20,
color: "#FF8544",
borderRadius: theme.palette?.borderRadius,
"&:hover": {
backgroundColor: "rgba(45, 45, 45, 0.95)",
@@ -2376,7 +2386,7 @@ const CodeEditor = (props) => {
<span>
<PlayArrowIcon style={{ height: 18, width: 18, marginBottom: -4, marginLeft: 5, }} />
{selectedAction === undefined ? "Try it" : selectedAction.name === "execute_python" ? "Run Python Code" : selectedAction.name === "execute_bash" ? "Run Bash" : "Try it"}
{selectedAction === undefined ? <Typography style={{color: "inherit"}}>Try it</Typography> : selectedAction.name === "execute_python" ? "Run Python Code" : selectedAction.name === "execute_bash" ? "Run Bash" : "Try it"}
<span
style={{
color: "#C8C8C8",
+71 -60
View File
@@ -1,5 +1,6 @@
import React, { memo, useContext, useEffect, useState } from 'react';
import theme from "../theme.jsx";
import {getTheme} from "../theme.jsx";
import { Context } from '../context/ContextApi.jsx';
import {
FormControl,
Card,
@@ -63,6 +64,8 @@ const TenantsTab = memo((props) => {
const [suborglistOpen, setSuborglistOpen] = React.useState(false);
const [allTenantsOpen, setAllTenantsOpen] = React.useState(false);
const itemColor = "black";
const { themeMode, supportEmail, brandColor } = useContext(Context);
const theme = getTheme(themeMode, brandColor);
useEffect(() => {
if(parentOrg !== null && parentOrgFlag === null) {
@@ -84,6 +87,9 @@ const TenantsTab = memo((props) => {
} else if (regiontag === "ca") {
regiontag = "CA";
regionCode = "ca";
}else if (regiontag === "au") {
regiontag = "AUS";
regionCode = "au"
}
}
setParentOrgFlag(regionCode);
@@ -168,6 +174,9 @@ const TenantsTab = memo((props) => {
} else if (regiontag === "ca") {
regiontag = "CA";
regionCode = "ca";
}else if (regiontag === "au") {
regiontag = "AUS";
regionCode = "au"
}
}
setParentOrgFlag(regionCode);
@@ -613,7 +622,7 @@ const TenantsTab = memo((props) => {
}}
>
<DialogTitle>
<span style={{ color: "white" }}>Add Sub-Organization</span>
<Typography variant='h5' color="textPrimary">Add Sub-Organization</Typography>
</DialogTitle>
<DialogContent>
<div>
@@ -625,7 +634,7 @@ const TenantsTab = memo((props) => {
InputProps={{
style: {
height: "50px",
color: "white",
color: theme.palette.textFieldStyle.color,
fontSize: "1em",
},
}}
@@ -644,15 +653,14 @@ const TenantsTab = memo((props) => {
</DialogContent>
<DialogActions>
<Button
style={{ borderRadius: "2px", fontSize: 16, textTransform: "none", color: "#ff8544" }}
style={{ borderRadius: "2px", fontSize: 16, textTransform: "none", color: theme.palette.primary.main }}
onClick={() => setModalOpen(false)}
color="primary"
>
Cancel
</Button>
<Button
variant="contained"
style={{ borderRadius: "2px", fontSize: 16, textTransform: "none", backgroundColor: "#ff8544", color: "#1a1a1a"}}
style={{ borderRadius: "2px", fontSize: 16, textTransform: "none",}}
onClick={() => {
createSubOrg(selectedOrganization.id, orgName);
}}
@@ -739,7 +747,7 @@ const TenantsTab = memo((props) => {
}
style={{ marginLeft: 15, height: 50, borderRadius: "2px", color: "#1a1a1a", backgroundColor: (!selectedOrganization.cloud_sync &&
cloudSyncApikey.length === 0) ||
loading ? "rgba(200, 200, 200, 0.5)" : "#ff8544", fontSize: 16, textTransform: "none" }}
loading ? "rgba(200, 200, 200, 0.5)" : theme.palette.primary.main, fontSize: 16, textTransform: "none" }}
onClick={() => {
setLoading(true);
enableCloudSync(
@@ -784,31 +792,31 @@ const TenantsTab = memo((props) => {
const textColor = "#9E9E9E !important";
return (
<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",}}>
<div style={{ width: "100%", minHeight: 1100, boxSizing: 'border-box', padding: "27px 10px 19px 27px", height:"100%", backgroundColor: theme.palette.platformColor, borderTopRightRadius: '8px', borderBottomRightRadius: 8, borderLeft: theme.palette.defaultBorder,}}>
{modalView}
{cloudSyncModal}
<div style={{height: "100%", maxHeight: 1700, overflowY: "auto",overflowX: 'hidden', scrollbarColor: '#494949 transparent', scrollbarWidth: 'thin'}}>
<div style={{ height: "100%", width: "calc(100% - 20px)", scrollbarColor: '#494949 transparent', scrollbarWidth: 'thin' }}>
<div style={{height: "100%", maxHeight: 1700, overflowY: "auto",overflowX: 'hidden', scrollbarColor: theme.palette.scrollbarColorTransparent, scrollbarWidth: 'thin'}}>
<div style={{ height: "100%", width: "calc(100% - 20px)", scrollbarColor: theme.palette.scrollbarColorTransparent, scrollbarWidth: 'thin' }}>
<div style={{ marginBottom: 20 }}>
<h2 style={{ marginBottom: 8, marginTop: 0, color: "#ffffff" }}>Tenants</h2>
<span style={{ color: textColor }}>
<Typography variant='h5' color="textPrimary" style={{ marginBottom: 8, marginTop: 0 }}>Tenants</Typography>
<Typography variant='body2' color="textSecondary">
Create, manage and change to sub-organizations (tenants)! {" "}
{isCloud
? "You can only make a sub organization if you are a customer of shuffle or running a POC of the platform. Please contact support@shuffler.io to try it out."
? `You can only make a sub organization if you are a customer of shuffle or running a POC of the platform. Please contact ${supportEmail} to try it out.`
: ''}&nbsp;
<a
href="/docs/organizations"
target="_blank"
rel="noopener noreferrer"
style={{ color: "#FF8444" }}
style={{ color: theme.palette.linkColor }}
>
Learn more
</a>
</span>
</Typography>
</div>
<Button
style={{ backgroundColor: '#ff8544', textTransform: 'none', fontSize: 16, color: "#1a1a1a", borderRadius: 4, width: 212, height: 40 }}
style={{ textTransform: 'none', fontSize: 16, borderRadius: 4, width: 212, height: 40 }}
variant="contained"
color="primary"
disabled={userdata.admin !== 'true'}
@@ -835,17 +843,15 @@ const TenantsTab = memo((props) => {
marginTop: 20,
}}
>
<h3
<Typography
variant='h6'
color="textPrimary"
style={{
margin: 0,
fontSize: '1.2rem',
fontWeight: 'bold',
letterSpacing: '1px',
color: "#ffffff"
}}
>
Your Parent Organization
</h3>
</Typography>
</div>
<div>
@@ -859,7 +865,7 @@ const TenantsTab = memo((props) => {
/> */}
<div style={{ borderRadius: 4, marginTop: 24, border: "1px solid #494949", width: "100%", overflowX: "auto", paddingBottom: 0 }}>
<div style={{ borderRadius: 4, marginTop: 24, border: theme.palette.defaultBorder, width: "100%", overflowX: "auto", paddingBottom: 0 }}>
<List
style={{
width: "100%",
@@ -890,7 +896,7 @@ const TenantsTab = memo((props) => {
display: "table-cell",
padding: "0px 8px 8px 8px",
textAlign: "center",
borderBottom: "1px solid #494949",
borderBottom: theme.palette.defaultBorder,
verticalAlign: "middle",
}}
/>
@@ -903,7 +909,7 @@ const TenantsTab = memo((props) => {
padding: "0px 8px 8px 8px",
whiteSpace: "nowrap",
textOverflow: "ellipsis",
borderBottom: "1px solid #494949",
borderBottom: theme.palette.defaultBorder,
verticalAlign: "middle",
textAlign: "center",
}}
@@ -915,7 +921,7 @@ const TenantsTab = memo((props) => {
minWidth: 100,
maxWidth: 100,
display: "table-cell",
borderBottom: "1px solid #494949",
borderBottom: theme.palette.defaultBorder,
padding: "0px 8px 8px 8px",
}}
/>
@@ -929,7 +935,7 @@ const TenantsTab = memo((props) => {
padding: "0px 8px 8px 8px",
whiteSpace: "nowrap",
textOverflow: "ellipsis",
borderBottom: "1px solid #494949",
borderBottom: theme.palette.defaultBorder,
verticalAlign: "middle",
}}
/>
@@ -942,7 +948,7 @@ const TenantsTab = memo((props) => {
padding: "0px 8px 8px 8px",
whiteSpace: "nowrap",
textOverflow: "ellipsis",
borderBottom: "1px solid #494949",
borderBottom: theme.palette.defaultBorder,
verticalAlign: "middle",
}}
/>
@@ -954,7 +960,7 @@ const TenantsTab = memo((props) => {
key={rowIndex}
style={{
display: "flex",
backgroundColor: "#212121",
backgroundColor: theme.palette.platformColor,
height: 30,
}}
>
@@ -976,7 +982,7 @@ const TenantsTab = memo((props) => {
variant="text"
animation="wave"
sx={{
backgroundColor: "#1a1a1a",
backgroundColor: theme.palette.loaderColor,
borderRadius: "4px",
}}
/>
@@ -987,7 +993,7 @@ const TenantsTab = memo((props) => {
) : parentOrg?.id?.length > 0 ? (
<ListItem
style={{
backgroundColor: "#1A1A1A",
backgroundColor: theme.palette.platformColor,
display: "table-row",
padding: 8,
verticalAlign: "middle",
@@ -1095,7 +1101,7 @@ const TenantsTab = memo((props) => {
padding: "10px",
whiteSpace: "nowrap",
}}
primary={index === 1 ? "Parent Organization not found or May be you are not part of parent org. Please contact support@shuffler.io." : null}
primary={index === 1 ? `Parent Organization not found or May be you are not part of parent org. Please contact ${supportEmail}` : null}
colSpan={index === 0 ? 5 : undefined}
/>
))}
@@ -1123,17 +1129,15 @@ const TenantsTab = memo((props) => {
marginTop: 20,
}}
>
<h3
<Typography
variant='h6'
color="textPrimary"
style={{
margin: 0,
fontSize: '1.2rem',
fontWeight: 'bold',
letterSpacing: '1px',
color: "#ffffff"
}}
>
Sub Organizations of the Current Organization ({subOrgs.length})
</h3>
</Typography>
</div>
{/* <Divider
@@ -1144,7 +1148,7 @@ const TenantsTab = memo((props) => {
}}
/> */}
<div style={{borderRadius: 4, marginTop: 24, border: "1px solid #494949", width: "100%", overflowX: "auto", paddingBottom: 0 }}>
<div style={{borderRadius: 4, marginTop: 24, border: theme.palette.defaultBorder, width: "100%", overflowX: "auto", paddingBottom: 0 }}>
<List
style={{
width: '100%',
@@ -1188,7 +1192,7 @@ const TenantsTab = memo((props) => {
display: "table-cell",
padding: "0px 8px 8px 8px",
textAlign: "center",
borderBottom: "1px solid #494949",
borderBottom: theme.palette.defaultBorder,
verticalAlign: "middle",
}}
/>
@@ -1214,7 +1218,7 @@ const TenantsTab = memo((props) => {
display: "table-cell",
padding: "0px 8px 8px 8px",
textAlign: "center",
borderBottom: "1px solid #494949",
borderBottom: theme.palette.defaultBorder,
verticalAlign: "middle",
}} />
<ListItemText
@@ -1226,7 +1230,7 @@ const TenantsTab = memo((props) => {
padding: "0px 8px 8px 8px",
whiteSpace: "nowrap",
textOverflow: "ellipsis",
borderBottom: "1px solid #494949",
borderBottom: theme.palette.defaultBorder,
verticalAlign: "middle",
textAlign: "center",
}} />
@@ -1237,7 +1241,7 @@ const TenantsTab = memo((props) => {
minWidth: 100,
maxWidth: 100,
display: "table-cell",
borderBottom: "1px solid #494949",
borderBottom: theme.palette.defaultBorder,
padding: "0px 8px 8px 8px",
}}
/>
@@ -1251,7 +1255,7 @@ const TenantsTab = memo((props) => {
padding: "0px 8px 8px 8px",
whiteSpace: "nowrap",
textOverflow: "ellipsis",
borderBottom: "1px solid #494949",
borderBottom: theme.palette.defaultBorder,
verticalAlign: "middle",
}}
/>
@@ -1264,7 +1268,7 @@ const TenantsTab = memo((props) => {
padding: "0px 8px 8px 8px",
whiteSpace: "nowrap",
textOverflow: "ellipsis",
borderBottom: "1px solid #494949",
borderBottom: theme.palette.defaultBorder,
verticalAlign: "middle",
}}
/>
@@ -1290,9 +1294,13 @@ const TenantsTab = memo((props) => {
}
}
}
var bgColor = themeMode === "dark" ? "#212121" : "#FFFFFF";
if (index % 2 === 0) {
bgColor = themeMode === "dark" ? "#1A1A1A" : "#EAEAEA";
}
return (
<ListItem key={index} style={{ backgroundColor: index % 2 === 0 ? '#1A1A1A' : '#212121', width: "100%", borderBottomLeftRadius: 8, display:'table-row', borderBottomRightRadius: 8 }}>
<ListItem key={index} style={{ backgroundColor: bgColor, width: "100%", borderBottomLeftRadius: 8, display:'table-row', borderBottomRightRadius: 8 }}>
<ListItemText primary={<img alt={data?.name} src={data.image || theme.palette.defaultImage} style={imageStyle} />} style={{ width: 100,
minWidth: 100,
maxWidth: 100,
@@ -1374,17 +1382,15 @@ const TenantsTab = memo((props) => {
/>
<div style={{ textAlign: 'left', width: '100%', padding: '10px', marginTop: 20 }}>
<h3
<Typography
variant='h6'
color="textPrimary"
style={{
margin: 0,
fontSize: '1.2rem',
fontWeight: 'bold',
letterSpacing: '1px',
color: "#ffffff"
}}
>
All Tenants
</h3>
</Typography>
</div>
{/* <Divider
@@ -1398,7 +1404,7 @@ const TenantsTab = memo((props) => {
style={{
borderRadius: 4,
marginTop: 24,
border: "1px solid #494949",
border: theme.palette.defaultBorder,
width: "100%",
overflowX: "auto",
paddingBottom: 0,
@@ -1448,7 +1454,7 @@ const TenantsTab = memo((props) => {
display: "table-cell",
padding: "0px 8px 8px 8px",
textAlign: "center",
borderBottom: "1px solid #494949",
borderBottom: theme.palette.defaultBorder,
verticalAlign: "middle",
}}
/>
@@ -1475,7 +1481,7 @@ const TenantsTab = memo((props) => {
display: "table-cell",
padding: "0px 8px 8px 8px",
textAlign: "center",
borderBottom: "1px solid #494949",
borderBottom: theme.palette.defaultBorder,
verticalAlign: "middle",
}}
/>
@@ -1488,7 +1494,7 @@ const TenantsTab = memo((props) => {
padding: "0px 8px 8px 8px",
whiteSpace: "nowrap",
textOverflow: "ellipsis",
borderBottom: "1px solid #494949",
borderBottom: theme.palette.defaultBorder,
verticalAlign: "middle",
textAlign: "center",
}}
@@ -1500,7 +1506,7 @@ const TenantsTab = memo((props) => {
minWidth: 100,
maxWidth: 100,
display: "table-cell",
borderBottom: "1px solid #494949",
borderBottom: theme.palette.defaultBorder,
padding: "0px 8px 8px 8px",
}}
/>
@@ -1514,7 +1520,7 @@ const TenantsTab = memo((props) => {
padding: "0px 8px 8px 8px",
whiteSpace: "nowrap",
textOverflow: "ellipsis",
borderBottom: "1px solid #494949",
borderBottom: theme.palette.defaultBorder,
verticalAlign: "middle",
}}
/>
@@ -1527,7 +1533,7 @@ const TenantsTab = memo((props) => {
padding: "0px 8px 8px 8px",
whiteSpace: "nowrap",
textOverflow: "ellipsis",
borderBottom: "1px solid #494949",
borderBottom: theme.palette.defaultBorder,
verticalAlign: "middle",
}}
/>
@@ -1590,6 +1596,11 @@ const TenantsTab = memo((props) => {
}
}
var bgColor = themeMode === "dark" ? "#212121" : "#FFFFFF";
if (index % 2 === 0) {
bgColor = themeMode === "dark" ? "#1A1A1A" : "#EAEAEA";
}
return (
<ListItem
key={index}
@@ -1597,7 +1608,7 @@ const TenantsTab = memo((props) => {
display: "table-row",
verticalAlign: "middle",
padding: 8,
backgroundColor: index % 2 === 0 ? "#1A1A1A" : "#212121",
backgroundColor: bgColor,
borderBottomLeftRadius:
userdata?.orgs?.length - 1 === index ? 8 : 0,
borderBottomRightRadius:
+77 -49
View File
@@ -1,6 +1,6 @@
import React, { useState, useEffect, useContext, memo } from "react";
import { toast } from 'react-toastify';
import { Context } from "../context/ContextApi.jsx";
import {
FormControl,
InputLabel,
@@ -35,7 +35,7 @@ import {
import ModeEditOutlineOutlinedIcon from '@mui/icons-material/ModeEditOutlineOutlined';
import ContentCopyOutlinedIcon from '@mui/icons-material/ContentCopyOutlined';
import theme from "../theme.jsx";
import {getTheme} from "../theme.jsx";
const ITEM_HEIGHT = 48;
const ITEM_PADDING_TOP = 8;
const MenuProps = {
@@ -75,12 +75,17 @@ const UserManagmentTab = memo((props) => {
const [logsViewModal, setLogsViewModal] = React.useState(false);
const [ipSelected, setIpSelected] = React.useState("");
const [userLogViewing, setUserLogViewing] = React.useState({});
const { themeMode, supportEmail, brandColor } = useContext(Context);
const theme = getTheme(themeMode, brandColor);
useEffect(() => {
if (selectedOrganization?.mfa_required !== MFARequired) {
setMFARequired(selectedOrganization?.mfa_required);
}
}, [selectedOrganization]);
useEffect(() => { if(users?.length === 0){
getUsers();
} }, []);
@@ -363,7 +368,7 @@ const UserManagmentTab = memo((props) => {
toast("Failed to deactivate user: " + responseJson.reason);
} else if (responseJson.success === false) {
toast(
"Failed to deactivate user. Please contact support@shuffler.io if this persists.",
`Failed to deactivate user. Please contact ${supportEmail} if this persists.`,
);
} else {
toast("Changed activation for user " + data.id);
@@ -602,7 +607,7 @@ const UserManagmentTab = memo((props) => {
}}
>
<DialogTitle>
<Typography style={{ color: "white", textTransform: 'none', fontSize: 24 }}>
<Typography variant="h5" style={{ textTransform: 'none', fontSize: 24 }}>
Add user
</Typography>
</DialogTitle>
@@ -619,7 +624,7 @@ const UserManagmentTab = memo((props) => {
InputProps={{
style: {
height: "50px",
color: "white",
color: theme.palette.textFieldStyle.color,
fontSize: "1em",
},
}}
@@ -682,17 +687,17 @@ const UserManagmentTab = memo((props) => {
</div>
{loginInfo}
</DialogContent>
<Box sx={{ display: 'flex', justifyContent: 'flex-end', p: 2, backgroundColor: "#212121" }}>
<Box sx={{ display: 'flex', justifyContent: 'flex-end', p: 2, backgroundColor: theme.palette.platformColor }}>
<Button
style={{ borderRadius: "2px", textTransform: 'none', fontSize:16, color: "#ff8544", marginRight: 5 }}
style={{ borderRadius: "2px", textTransform: 'none', fontSize:16, marginRight: 5, color: theme.palette.primary.main }}
onClick={() => setModalOpen(false)}
color="primary"
>
Cancel
</Button>
<Button
variant="contained"
style={{ borderRadius: "2px", textTransform: 'none', fontSize:16, color: "#1a1a1a", backgroundColor: "#ff8544" }}
color="primary"
style={{ borderRadius: "2px", textTransform: 'none', fontSize:16,}}
onClick={() => {
if (isCloud) {
inviteUser(modalUser);
@@ -700,7 +705,6 @@ const UserManagmentTab = memo((props) => {
submitUser(modalUser);
}
}}
color="primary"
>
Submit
</Button>
@@ -751,7 +755,7 @@ const UserManagmentTab = memo((props) => {
}}
>
<DialogTitle style={{ maxWidth: "800px", width: "100%", textAlign: "center", margin: "auto", backgroundColor: theme?.palette?.DialogStyle?.backgroundColor}}>
<span style={{ color: "white", backgroundColor: theme?.palette?.DialogStyle?.backgroundColor }}>
<span style={{ color: theme.palette.text.primary, backgroundColor: theme?.palette?.DialogStyle?.backgroundColor }}>
<EditIcon style={{ marginTop: 5 }} /> Editing {selectedUser.username}
</span>
</DialogTitle>
@@ -845,9 +849,10 @@ const UserManagmentTab = memo((props) => {
backgroundColor: theme.palette.inputColor,
}}
/>
<div style={{ margin: "auto", maxWidth: 450 }}>
<Button
style={{textTransform: 'none', fontSize: 16}}
<div style={{ display: 'flex', flexDirection: 'column', justifyContent: 'center', alignItems: 'center', margin: "auto", maxWidth: 450 }}>
<div style={{ display: "flex", justifyContent: "space-between", width: "100%" }}>
<Button
style={{textTransform: 'none', fontSize: 16, whiteSpace: 'nowrap', textWrap: 'nowarp'}}
variant="outlined"
color="primary"
disabled={selectedUser.username === userdata.username}
@@ -859,7 +864,7 @@ const UserManagmentTab = memo((props) => {
{selectedUser.active ? "Delete from org" : "Delete from org"}
</Button>
<Button
style={{ textTransform: 'none', fontSize: 16 }}
style={{ textTransform: 'none', fontSize: 16, whiteSpace: 'nowrap', textWrap: 'nowarp', }}
variant="outlined"
color="primary"
disabled={
@@ -880,7 +885,7 @@ const UserManagmentTab = memo((props) => {
}
variant="outlined"
color="primary"
style={{textTransform: 'none', fontSize: 16}}
style={{textTransform: 'none', fontSize: 16, whiteSpace: 'nowrap', textWrap: 'nowarp', }}
>
{selectedUser.mfa_info !== undefined &&
selectedUser.mfa_info !== null &&
@@ -888,6 +893,7 @@ const UserManagmentTab = memo((props) => {
? "Disable 2FA"
: "Enable 2FA"}
</Button>
</div>
{isCloud && userdata.support && selectedUser.id !== userdata.id ? (
<Button
@@ -897,6 +903,8 @@ const UserManagmentTab = memo((props) => {
marginTop: 50,
border: "1px solid #d52b2b",
textTransform: "none",
whiteSpace: 'nowrap',
textWrap: 'nowarp',
color:
showDeleteAccountTextbox === true &&
deleteAccountText?.length > 0 &&
@@ -1269,23 +1277,23 @@ const UserManagmentTab = memo((props) => {
) : null
return (
<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", }}>
<div style={{ width: "100%", minHeight: 1100, boxSizing: 'border-box', padding: "27px 10px 19px 27px", height:"100%", backgroundColor: theme.palette.platformColor, borderTopRightRadius: '8px', borderBottomRightRadius: 8, borderLeft: "1px solid #494949", }}>
{modalView}
{editUserModal}
{logview}
<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={{ height: "100%", maxHeight: 1700, overflowY: "auto", scrollbarColor: theme.palette.scrollbarColorTransparent, scrollbarWidth: 'thin'}}>
<div style={{ height: "100%", width: "calc(100% - 20px)", scrollbarColor: theme.palette.scrollbarColorTransparent, scrollbarWidth: 'thin' }}>
<div style={{display: 'flex', justifyContent: 'space-between'}}>
<div>
<div style={{ marginBottom: 20 }}>
<h2 style={{ marginBottom: 8, marginTop: 0, color: "#FFFFFF" }}>User Management</h2>
<span style={{ color: "#9E9E9E" }}>
<Typography variant="h5" style={{ marginBottom: 8, marginTop: 0, }}>User Management</Typography>
<Typography variant="body2" color="textSecondary">
Add, edit, distribute or remove users from your organization.{" "}
<a
target="_blank"
rel="noopener noreferrer"
href="/admin?admin_tab=sso"
style={{ color: "#FF8444" }}
style={{ color: theme.palette.linkColor }}
>
Configure SSO
</a>
@@ -1296,15 +1304,15 @@ const UserManagmentTab = memo((props) => {
target="_blank"
rel="noopener noreferrer"
href="/docs/organizations#user_management"
style={{ color: "#FF8444" }}
style={{ color: theme.palette.linkColor }}
>
learn more about users
</a>
</span>
</Typography>
</div>
<div />
<Button
style={{ color: "#1a1a1a", backgroundColor: "#ff8544",fontSize: 16, textTransform: 'none', borderRadius: 4, width: 162, height: 40, boxShadow: 'none' }}
style={{ fontSize: 16, textTransform: 'none', borderRadius: 4, width: 162, height: 40, boxShadow: 'none' }}
variant="contained"
color="primary"
onClick={() => setModalOpen(true)}
@@ -1312,9 +1320,9 @@ const UserManagmentTab = memo((props) => {
Add user
</Button>
<Button
style={{ backgroundColor: "#2F2F2F", boxShadow: 'none', borderRadius: 4, width: 81, height: 40, marginLeft: 16, marginRight: 15 }}
style={{ boxShadow: 'none', borderRadius: 4, width: 81, height: 40, marginLeft: 16, marginRight: 15 }}
variant="contained"
color="primary"
color="secondary"
onClick={() => getUsers()}
>
<CachedIcon />
@@ -1334,7 +1342,7 @@ const UserManagmentTab = memo((props) => {
style={{
borderRadius: 4,
marginTop: 24,
border: "1px solid #494949",
border: theme.palette.defaultBorder,
width: "100%",
overflowX: "auto",
paddingBottom: 0,
@@ -1350,7 +1358,7 @@ const UserManagmentTab = memo((props) => {
paddingBottom: 0,
}}
>
<ListItem style={{ width: "100%", padding: "10px 10px 10px 0px", verticalAlign: 'middle', borderBottom: "1px solid #494949", display: "table-row" }}>
<ListItem style={{ width: "100%", padding: "10px 10px 10px 0px", verticalAlign: 'middle', borderBottom: theme.palette.defaultBorder, display: "table-row" }}>
{["Username", /*"API Key",*/ "Role", /*"Active",*/ "Type", "MFA", ...(selectedOrganization?.child_orgs?.length > 0 ? ["Suborgs"]: []), "Actions", "Last Login"].map((header, index) => (
<ListItemText
key={index}
@@ -1360,7 +1368,7 @@ const UserManagmentTab = memo((props) => {
padding: index === 0 ? "0px 8px 8px 15px": "0px 8px 8px 8px",
whiteSpace: "nowrap",
textOverflow: "ellipsis",
borderBottom: "1px solid #494949",
borderBottom: theme.palette.defaultBorder,
position: "sticky",
verticalAlign: "middle",
}}
@@ -1373,7 +1381,7 @@ const UserManagmentTab = memo((props) => {
key={rowIndex}
style={{
display: "table-row",
backgroundColor: "#212121",
backgroundColor: theme.palette.platformColor,
}}
>
{Array(9)
@@ -1390,7 +1398,7 @@ const UserManagmentTab = memo((props) => {
variant="text"
animation="wave"
sx={{
backgroundColor: "#1a1a1a",
backgroundColor: theme.palette.loaderColor,
height: "20px",
borderRadius: "4px",
}}
@@ -1401,9 +1409,9 @@ const UserManagmentTab = memo((props) => {
))
): users === 0 ? null
: users?.map((data, index) => {
var bgColor = "#212121";
var bgColor = themeMode === "dark" ? "#212121" : "#FFFFFF";
if (index % 2 === 0) {
bgColor = "#1A1A1A";
bgColor = themeMode === "dark" ? "#1A1A1A" : "#EAEAEA";
}
const timeNow = new Date().getTime();
@@ -1434,8 +1442,7 @@ const UserManagmentTab = memo((props) => {
style={{
cursor: "pointer",
textDecoration: "underline",
textDecorationColor: "#F76742",
color: "#F76742",
color: theme.palette.linkColor,
}}
onClick={() => {
setLogsViewModal(true);
@@ -1465,7 +1472,7 @@ const UserManagmentTab = memo((props) => {
maxWidth: 150,
minWidth: 100,
width: 'auto',
color: "#FF8444",
color: theme.palette.primary.main,
textOverflow: "ellipsis",
whiteSpace: "nowrap",
overflow: "hidden",
@@ -1522,8 +1529,8 @@ const UserManagmentTab = memo((props) => {
setUser(data.id, "role", e.target.value);
}}
sx={{
backgroundColor: "#1A1A1A",
color: "white",
backgroundColor: theme.palette.backgroundColor,
color: theme.palette.textColor,
height: "50px",
borderRadius: "4px",
marginTop: "8px",
@@ -1542,27 +1549,35 @@ const UserManagmentTab = memo((props) => {
>
<MenuItem
sx={{
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
color: "white",
backgroundColor: theme.palette.backgroundColor,
color: theme.palette.textColor,
"&:hover": {
backgroundColor: theme.palette.hoverColor,
},
}}
value={"admin"}
>
Org Admin
</MenuItem>
<MenuItem
style={{
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
color: "white",
sx={{
backgroundColor: theme.palette.backgroundColor,
color: theme.palette.textColor,
"&:hover": {
backgroundColor: theme.palette.hoverColor,
},
}}
value={"user"}
>
Org User
</MenuItem>
<MenuItem
style={{
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
color: "white",
sx={{
backgroundColor: theme.palette.backgroundColor,
color: theme.palette.textColor,
"&:hover": {
backgroundColor: theme.palette.hoverColor,
},
}}
value={"org-reader"}
>
@@ -1664,7 +1679,20 @@ const UserManagmentTab = memo((props) => {
}
}}
>
<img src="/icons/editIcon.svg" alt="edit icon" style={{width: 24, height: 24}} />
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M16.1038 4.66848C16.3158 4.45654 16.5674 4.28843 16.8443 4.17373C17.1212 4.05903 17.418 4 17.7177 4C18.0174 4 18.3142 4.05903 18.5911 4.17373C18.868 4.28843 19.1196 4.45654 19.3315 4.66848C19.5435 4.88041 19.7116 5.13201 19.8263 5.40891C19.941 5.68582 20 5.9826 20 6.28232C20 6.58204 19.941 6.87882 19.8263 7.15573C19.7116 7.43263 19.5435 7.68423 19.3315 7.89617L8.43807 18.7896L4 20L5.21038 15.5619L16.1038 4.66848Z"
stroke={themeMode=== "dark" ? "#F1F1F1" : "#333"}
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
</IconButton>
{/* <Button
onClick={() => {
@@ -1,11 +1,12 @@
import React, { useState, useEffect } from "react";
import React, { useState, useEffect, useContext } from "react";
import { toast } from "react-toastify"
import theme from '../theme.jsx';
import {getTheme} from '../theme.jsx';
import { useNavigate, Link, useParams } from "react-router-dom";
import AppSearchButtons from "../components/AppSearchButtons.jsx";
import { isMobile } from "react-device-detect";
import RenderCytoscape from "../components/RenderCytoscape.jsx";
import { Context } from "../context/ContextApi.jsx";
import {
Button,
Typography,
@@ -72,7 +73,8 @@ const WorkflowTemplatePopup = (props) => {
const [loadingWorkflow, setLoadingWorkflow] = React.useState(false)
const [workflow, setWorkflow] = useState(inputWorkflow !== undefined && inputWorkflow !== null && inputWorkflow.id !== undefined && inputWorkflow.id !== null && inputWorkflow.id !== "" ? inputWorkflow : {})
const [_, setUpdate] = useState(0)
const { themeMode } = useContext(Context);
const theme = getTheme(themeMode)
const fetchWorkflow = (id) => {
if (id === undefined || id === null || id === "") {
return
@@ -549,12 +551,10 @@ const WorkflowTemplatePopup = (props) => {
open={modalOpen}
onClose={handleClose}
PaperProps={{
style: {
backgroundColor: "black",
color: "white",
minWidth: isHomePage ? null : isMobile ? 300 : 750,
maxWidth: isHomePage ? null : isMobile ? 300 : 750,
paddingTop: isMobile ? null : 75,
sx: {
backgroundColor: theme.palette.drawer.backgroundColor,
minWidth: isHomePage ? null : isMobile ? "300px" : "750px",
maxWidth: isHomePage ? null : isMobile ? "300px" : "750px",
itemAlign: "center",
},
}}
@@ -571,7 +571,7 @@ const WorkflowTemplatePopup = (props) => {
>
<CloseIcon />
</IconButton>
<DialogContent style={{marginTop: 0, marginLeft: isHomePage ? null : isMobile ? null : 75, maxWidth: 470, marginTop: 20 }}>
<DialogContent style={{paddingTop: isMobile ? null : "75px", backgroundColor: theme.palette.drawer.backgroundColor, marginTop: 0, paddingLeft: isHomePage ? null : isMobile ? null : 75, maxWidth: "100%",}}>
<Typography variant="h4" style={{ fontSize: isMobile ? 20 : null}}>
<b>Configure Workflow</b>
</Typography>
@@ -790,7 +790,7 @@ const WorkflowTemplatePopup = (props) => {
width: isHomePage? isMobile ? null : "100%" : "99%",
borderRadius: 8,
textTransform: "none",
backgroundColor: isHomePage ? null : theme.palette.inputColor,
backgroundColor: isHomePage ? null : theme.palette.platformColor,
border: borderStyle,
cursor: isActive ? errorMessage !== "" ? "not-allowed" : "pointer" : "pointer",
position: "relative",
@@ -878,7 +878,7 @@ const WorkflowTemplatePopup = (props) => {
</div>
<div style={{ marginLeft: 20, overflow: "hidden", maxHeight: 30, marginTop: visualOnly ? 12 : showTryitOut && !isActive ? 8 : 23, }}>
<Typography variant="body1" style={{ marginTop: parsedDescription.length === 0 ? 10 : 0, fontSize: isMobile ? 13 : 16, fontWeight: isHomePage ? 600 : null, textTransform: 'capitalize', color: isHomePage ? "var(--White-text, #F1F1F1)" : "rgba(241, 241, 241, 1)"}} >
<Typography variant="body1" color="textPrimary" style={{ marginTop: parsedDescription.length === 0 ? 10 : 0, fontSize: isMobile ? 13 : 16, fontWeight: isHomePage ? 600 : null, textTransform: 'capitalize', }} >
<b>{parsedTitle}</b>
</Typography>
</div>
+98 -43
View File
@@ -1,4 +1,4 @@
import { useEffect } from "react";
import { useEffect, useContext } from "react";
import React from "react";
import {
Typography,
@@ -13,6 +13,8 @@ import { makeStyles } from "@mui/styles";
import { Link } from "react-router-dom";
import theme from "../theme.jsx";
import { toast } from "react-toastify";
import { Context } from "../context/ContextApi.jsx";
import { getTheme } from "../theme.jsx";
const useStyles = makeStyles({
notchedOutline: {
@@ -26,6 +28,7 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle
const classes = useStyles();
const [show2faSetup, setShow2faSetup] = React.useState(false);
const [autoPrivision, setAutoProvision] = React.useState(selectedOrganization?.sso_config?.auto_provision)
const [roleRequired, setRoleRequired] = React.useState(selectedOrganization?.sso_config?.role_required || false);
const [showOpenIdCred, setShowOpenIdCred] = React.useState(false);
const [showSamlCred, setShowSamlCred] = React.useState(false);
const [ssoEntrypoint, setSsoEntrypoint] = React.useState(
@@ -84,6 +87,9 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle
: selectedOrganization.sso_config.openid_token
)
const { themeMode, supportEmail, brandColor } = useContext(Context);
const theme = getTheme(themeMode, brandColor);
useEffect(()=>{
if (openidClientSecret !== selectedOrganization?.sso_config?.client_secret) {
@@ -116,12 +122,17 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle
if (autoPrivision !== selectedOrganization?.sso_config?.auto_provision) {
setAutoProvision(selectedOrganization?.sso_config?.auto_provision)
}
if (roleRequired !== selectedOrganization?.sso_config?.role_required) {
setRoleRequired(selectedOrganization?.sso_config?.role_required)
}
},[selectedOrganization])
const orgSaveButton = (
<Tooltip title="Save any unsaved data" placement="bottom">
<Button
style={{ width: 244, height: 51, flex: 1, textTransform: 'capitalize', padding: "16px, 24px, 16px, 24px", borderRadius: 4, backgroundColor: "#ff8544", color: "#1a1a1a", fontSize: 16, }}
style={{ width: 244, height: 51, flex: 1, textTransform: 'capitalize', padding: "16px, 24px, 16px, 24px", borderRadius: 4, fontSize: 16, }}
variant="contained"
color="primary"
disabled={
@@ -158,6 +169,7 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle
openid_token: openidToken,
SSORequired: SSORequired,
auto_provision: autoPrivision,
role_required: roleRequired,
}
)
}
@@ -205,6 +217,21 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle
toast.info("Toggled Auto Provisioning. Remember to save.");
}
};
const handleChangeRoleRequired = (event) => {
if (
openidAuthorization?.length === 0 &&
openidToken?.length === 0
) {
toast.error(
"Please fill in fields for OpenID connect before continuing. "
);
return;
} else {
setRoleRequired((prev)=> !prev);
toast.info("Toggled Role Required. Remember to save.");
}
}
const HandleTestSSO = () => {
const url = `${globalUrl}/api/v1/orgs/${selectedOrganization?.id}/change`;
@@ -227,7 +254,7 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle
.then((response) => {
if (response.status !== 200) {
toast.error(
"Failed to test SSO. Please try again later or contact support@shuffler.io if issue persists.",
`Failed to test SSO. Please try again later or contact ${supportEmail} if issue persists.`,
{ duration: 3000 }
);
return null;
@@ -264,10 +291,10 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle
};
return (
<div style={{ width: "100%", height: "100%",boxSizing: 'border-box', padding: "27px 10px 19px 27px", height:"100%", backgroundColor: '#212121', borderRadius: '16px', }}>
<div style={{ height: "100%", width: "100%", overflowX: 'hidden', scrollbarColor: '#494949 transparent', scrollbarWidth: 'thin'}} >
<div style={{ width: "100%", height: "100%",boxSizing: 'border-box', padding: "27px 10px 19px 27px", backgroundColor: theme.palette.platformColor , borderRadius: '16px', }}>
<div style={{ height: "100%", width: "100%", overflowX: 'hidden', scrollbarColor: theme.palette.scrollbarColorTransparent, scrollbarWidth: 'thin'}} >
<div style={{ width: "100%", overflowX: 'hidden', maxWidth: 883}}>
<Typography style={{ width: "100%", fontWeight: 'bold', fontSize: 24}}>
<Typography variant="h5" style={{ width: "100%", fontWeight: 500, fontSize: 24}}>
SSO Configuration
</Typography>
<div
@@ -282,7 +309,7 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle
<Typography
variant="body2"
color="textSecondary"
style={{ marginTop: 5, marginBottom: 5, color: "rgba(158, 158, 158, 1)", fontSize: 16, fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)", fontWeight: 400 }}
style={{ marginTop: 5, marginBottom: 5, fontSize: 16, fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)", fontWeight: 400 }}
>
Make SAML SSO or OpenID Authentication Required or Optional for Your Organization.
</Typography>
@@ -312,7 +339,7 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle
<Typography
variant="body2"
color="textSecondary"
style={{ marginTop: 5, marginBottom: 5, color: "rgba(158, 158, 158, 1)", fontSize: 16, fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)", fontWeight: 400 }}
style={{ marginTop: 5, marginBottom: 5, fontSize: 16, fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)", fontWeight: 400 }}
>
Auto-provisioning of users in SSO. By default, users are auto-provisioned in SSO when they login. If you enable this, no new user will be added in your organization when they login via SSO.
</Typography>
@@ -327,6 +354,36 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle
/>
</div>
</div>
<div
style={{
display: "flex",
flexDirection: "column",
width: "100%",
justifyContent: 'flex-start',
marginTop: 30
}}
>
<Typography
variant="body2"
color="textSecondary"
style={{ marginTop: 5, marginBottom: 5, color: "rgba(158, 158, 158, 1)", fontSize: 16, fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)", fontWeight: 400 }}
>
Restrict user login to SSO if no valid role is assigned by the SSO provider. When enabled, users will not be allowed to log in via SSO if their assigned role doesn't matches one of the following: shuffle-user, shuffle-admin, or shuffle-org-reader. Currently, available for OpenId Connect only.
<a href="https://shuffler.io/docs/extensions#how-to-assign-a-role-to-a-new-user-from-an-sso-provider-(openid-connect)-in-shuffle" target="_blank" style={{ color: theme.palette.linkColor }}> Learn more</a>
</Typography>
<div>
<Switch
checked={roleRequired}
onChange={handleChangeRoleRequired}
sx={{marginBottom: 0.6, marginTop: 0.6}}
name="onOffSwitch"
color="primary"
title="Disable auto-provisioning of users in SSO"
/>
</div>
</div>
<div
@@ -338,7 +395,7 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle
paddingBottom: 10,
}}
>
<Typography style={{color: "rgba(158, 158, 158, 1)", margin: "5px 0px 5px 0px", fontSize: 16, fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)" }}>
<Typography variant="body2" color="textSecondary" style={{ margin: "5px 0px 5px 0px", fontSize: 16, fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)" }}>
You can test your SSO configuration by clicking the button below.
Before testing, ensure you have set Open ID Connect or SAML SSO
credentials.
@@ -359,7 +416,7 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle
<Button
variant="outlined"
color="primary"
style={{ width: 100, textTransform: "none", margin: "10px 10px 10px 0px" }}
style={{ width: 100, textTransform: "none", margin: "10px 10px 10px 0px", whiteSpace: "nowrap" }}
disabled={
!(
ssoEntrypoint?.length > 0 ||
@@ -377,28 +434,28 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle
</div>
<Grid item xs={12} sx={{marginTop: 2}}>
<span style={{ display: "flex", flexDirection: "column" }}>
<Typography style={{ textAlign: "left", color: "rgba(241, 241, 241, 1)", fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)", fontSize: 24, fontWeight: "bold", }}>OpenID connect</Typography>
<span style={{ marginTop: 8, color: "rgba(158, 158, 158, 1)", fontSize: 16, fontWeight: 400 }}>
<Typography variant="h5" color="textPrimary" style={{ textAlign: "left", fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)", fontSize: 24, fontWeight: 500, }}>OpenID connect</Typography>
<span style={{ marginTop: 8, color: theme.palette.text.secondary, fontSize: 16, fontWeight: 400 }}>
Configure and Authorize SAML / SSO or OpenID connect. {" "}
<a
target="_blank"
href="/docs/extensions#single-signon"
style={{ color: "rgba(255, 132, 68, 1)" }}
style={{ color: theme.palette.linkColor }}
>
Learn more
</a>
</span>
</span>
<Typography style={{ textAlign: "left", fontSize: 16, marginTop: 8, color: "rgba(158, 158, 158, 1)", fontWeight: 400 }}>
IdP URL for Shuffle OpenID: <Link to={`${globalUrl}/api/v1/login_openid`} target="_blank" style={{ color: "rgba(241, 241, 241, 1)", textDecoration: "none", fontSize: 16,}}>{`${globalUrl}/api/v1/login_openid`}</Link>
<Typography variant="body2" color="tehttp://localhost:5002/api/v1/login_openidxtSecondary" style={{ textAlign: "left", fontSize: 16, marginTop: 8, fontWeight: 400 }}>
IdP URL for Shuffle OpenID: <Link to={`${globalUrl}/api/v1/login_openid`} target="_blank" style={{ color: theme.palette.text.secondary, textDecoration: "none", fontSize: 16,}}>{`${globalUrl}/api/v1/login_openid`}</Link>
</Typography>
<div style={{ display: 'flex', marginTop: 10, }}>
<Typography
<Typography
color="textSecondary"
style={{
textAlign: "left",
fontSize: 16,
marginTop: 8,
color: "rgba(158, 158, 158, 1)",
fontWeight: 400,
}}
>
@@ -409,20 +466,19 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle
onChange={(e) => setShowOpenIdCred(e.target.checked)}
name="showOpenIdCred"
color="primary"
style={{ color: "rgba(255, 255, 255, 1)", }}
/>
</div>
<Grid container style={{ marginTop: 8, }} spacing={2}>
<Grid item xs={6} style={{}}>
<span>
<Typography style={{ color: "rgba(255, 255, 255, 1)", fontSize: 16, fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)", fontWeight: 400, fontSize: 16 }}>Client ID</Typography>
<Typography style={{ fontSize: 16, fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)", fontWeight: 400, }}>Client ID</Typography>
<TextField
required
style={{
flex: "1",
marginTop: "5px",
marginRight: "15px",
backgroundColor: isEditOrgTab ? "rgba(33, 33, 33, 1)" : theme.palette.inputColor,
backgroundColor: isEditOrgTab ? theme.palette.textFieldStyle.backgroundColor : theme.palette.inputColor,
}}
fullWidth={true}
multiline={true}
@@ -440,11 +496,11 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle
notchedOutline: isEditOrgTab ? null : classes.notchedOutline,
},
style: {
color: "white",
color: theme.palette.textFieldStyle.color,
fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)",
fontWeight: 400,
fontSize: 16,
borderRadius: 4,
borderRadius: theme.palette.textFieldStyle.borderRadius,
},
}}
/>
@@ -452,14 +508,14 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle
</Grid>
<Grid item xs={6} style={{}}>
<span>
<Typography style={{ color: "rgba(255, 255, 255, 1)", fontSize: 16, fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)", fontWeight: 400, fontSize: 16 }}>Client Secret</Typography>
<Typography style={{ fontSize: 16, fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)", fontWeight: 400, }}>Client Secret</Typography>
<TextField
required
style={{
flex: "1",
marginTop: "5px",
marginRight: "15px",
backgroundColor: isEditOrgTab ? "rgba(33, 33, 33, 1)" : theme.palette.inputColor,
backgroundColor: isEditOrgTab ? theme.palette.textFieldStyle.backgroundColor : theme.palette.inputColor,
}}
fullWidth={true}
type={showOpenIdCred ? "text" : "password"}
@@ -480,11 +536,11 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle
notchedOutline: isEditOrgTab ? null : classes.notchedOutline,
},
style: {
color: "white",
color: theme.palette.textFieldStyle.color,
fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)",
fontWeight: 400,
fontSize: 16,
borderRadius: 4,
borderRadius: theme.palette.textFieldStyle.borderRadius,
},
}}
/>
@@ -494,14 +550,14 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle
<Grid container style={{ marginTop: 10, }} spacing={2}>
<Grid item xs={6} style={{}}>
<span>
<Typography style={{ color: "rgba(255, 255, 255, 1)", fontSize: 16, fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)", fontWeight: 400, fontSize: 16 }}>Authorization URL</Typography>
<Typography style={{ fontSize: 16, fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)", fontWeight: 400, fontSize: 16 }}>Authorization URL</Typography>
<TextField
required
style={{
flex: "1",
marginTop: "5px",
marginRight: "15px",
backgroundColor: isEditOrgTab ? "rgba(33, 33, 33, 1)" : theme.palette.inputColor,
backgroundColor: isEditOrgTab ? theme.palette.textFieldStyle.backgroundColor : theme.palette.inputColor,
}}
fullWidth={true}
type={showOpenIdCred ? "text" : "password"}
@@ -522,7 +578,7 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle
notchedOutline: isEditOrgTab ? null : classes.notchedOutline,
},
style: {
color: "white",
color: theme.palette.textFieldStyle.color,
fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)",
fontWeight: 400,
fontSize: 16,
@@ -534,14 +590,14 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle
</Grid>
<Grid item xs={6} style={{}}>
<span>
<Typography style={{ color: "rgba(255, 255, 255, 1)", fontSize: 16, fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)", fontWeight: 400, fontSize: 16 }}>Token URL</Typography>
<Typography style={{ fontSize: 16, fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)", fontWeight: 400, }}>Token URL</Typography>
<TextField
required
style={{
flex: "1",
marginTop: "5px",
marginRight: "15px",
backgroundColor: isEditOrgTab ? "rgba(33, 33, 33, 1)" : theme.palette.inputColor,
backgroundColor: isEditOrgTab ? theme.palette.textFieldStyle.backgroundColor : theme.palette.inputColor,
}}
fullWidth={true}
type={showOpenIdCred ? "text" : "password"}
@@ -562,7 +618,7 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle
notchedOutline: isEditOrgTab ? null : classes.notchedOutline,
},
style: {
color: "white",
color:theme.palette.textFieldStyle.color,
fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)",
fontWeight: 400,
fontSize: 16,
@@ -577,17 +633,17 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle
{/**/}
{/*isCloud ? null : */}
<Grid item xs={12} sx={{ marginTop: 3.5 }} >
<Typography variant="h4" style={{ textAlign: "left", color: "rgba(241, 241, 241, 1)", fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)", fontSize: 24, fontWeight: 600, }}>SAML SSO (v1.1)</Typography>
<Typography variant="body2" style={{ textAlign: "left", marginTop: 8, color: "rgba(241, 241, 241, 1)", fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)", fontSize: 16, fontWeight: 400, color: "rgba(158, 158, 158, 1)" }} color="textSecondary">
IdP URL for Shuffle SAML/SSO: <Link to={`${globalUrl}/api/v1/login_sso`} target="_blank" style={{ color: "rgba(241, 241, 241, 1)", textDecoration: "none" }}>{`${globalUrl}/api/v1/login_sso`}</Link>
<Typography variant="h5" color="textPrimary" style={{ textAlign: "left", fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)", fontSize: 24, fontWeight: 500, }}>SAML SSO (v1.1)</Typography>
<Typography variant="body2" color="textSecondary" style={{ textAlign: "left", marginTop: 8, fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)", fontSize: 16, fontWeight: 400, }}>
IdP URL for Shuffle SAML/SSO: <Link to={`${globalUrl}/api/v1/login_sso`} target="_blank" style={{ color: theme.palette.text.secondary, textDecoration: "none" }}>{`${globalUrl}/api/v1/login_sso`}</Link>
</Typography>
<div style={{ display: 'flex', marginTop: 10, }}>
<Typography
color="textSecondary"
style={{
textAlign: "left",
fontSize: 16,
marginTop: 8,
color: "rgba(158, 158, 158, 1)",
fontWeight: 400,
}}
>
@@ -598,20 +654,19 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle
onChange={(e) => setShowSamlCred(e.target.checked)}
name="showSamlCred"
color="primary"
style={{ color: "rgba(255, 255, 255, 1)", }}
/>
</div>
<Grid container style={{ marginTop: 10, }} spacing={2}>
<Grid item xs={6} style={{}}>
<span>
<Typography style={{ color: "rgba(255, 255, 255, 1)", fontSize: 16, fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)", fontWeight: 400, fontSize: 16 }}>SSO Entrypoint (IdP)</Typography>
<Typography style={{ fontSize: 16, fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)", fontWeight: 400, }}>SSO Entrypoint (IdP)</Typography>
<TextField
required
style={{
flex: "1",
marginTop: "5px",
marginRight: "15px",
backgroundColor: isEditOrgTab ? "rgba(33, 33, 33, 1)" : theme.palette.inputColor,
backgroundColor: isEditOrgTab ? theme.palette.textFieldStyle.backgroundColor : theme.palette.inputColor,
}}
fullWidth={true}
type={showSamlCred ? "text" : "password"}
@@ -632,7 +687,7 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle
notchedOutline: isEditOrgTab ? null : classes.notchedOutline,
},
style: {
color: "white",
color: theme.palette.textFieldStyle.color,
fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)",
fontWeight: 400,
fontSize: 16,
@@ -644,14 +699,14 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle
</Grid>
<Grid item xs={6} style={{}}>
<span>
<Typography style={{ color: "rgba(255, 255, 255, 1)", fontSize: 16, fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)", fontWeight: 400, fontSize: 16 }}>SSO Certificate (X509)</Typography>
<Typography style={{ fontSize: 16, fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)", fontWeight: 400, fontSize: 16 }}>SSO Certificate (X509)</Typography>
<TextField
required
style={{
flex: "1",
marginTop: "5px",
marginRight: "15px",
backgroundColor: isEditOrgTab ? "rgba(33, 33, 33, 1)" : theme.palette.inputColor,
backgroundColor: isEditOrgTab ? theme.palette.textFieldStyle.backgroundColor: theme.palette.inputColor,
}}
fullWidth={true}
type={showSamlCred ? "text" : "password"}
@@ -672,7 +727,7 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle
notchedOutline: isEditOrgTab ? null : classes.notchedOutline,
},
style: {
color: "white",
color: theme.palette.textFieldStyle.color,
fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)",
fontWeight: 400,
fontSize: 16,
+73 -1
View File
@@ -1,4 +1,6 @@
import React, { createContext, useState, useEffect } from 'react';
import useMediaQuery from '@mui/material/useMediaQuery';
export const Context = createContext();
export const AppContext = (props) => {
@@ -11,6 +13,14 @@ export const AppContext = (props) => {
const [isDocSearchModalOpen, setIsDocSearchModalOpen] = useState(false);
const [leftSideBarOpenByClick, setLeftSideBarOpenByClick] = useState(currentLocation?.includes('/workflows/') ? false : true)
const [windowWidth, setWindowWidth] = useState(serverside === true ? 100 : window.innerWidth);
const [brandColor, setBrandColor] = useState(() => localStorage.getItem("brandColor") || "#ff8544");
const [brandName, setBrandName] = useState(()=> localStorage.getItem("brandName") || "Shuffle");
const [themeMode, setThemeMode] = useState(
() => localStorage.getItem("theme") || "dark"
);
const [supportEmail, setSupportEmail] = useState("support@shuffler.io");
const [logoutUrl, setLogoutUrl] = useState("");
useEffect(() => {
if (currentLocation?.includes('/workflows/') && leftSideBarOpenByClick === true) {
@@ -35,6 +45,57 @@ export const AppContext = (props) => {
};
}, []);
const handleThemeChange = (theme) => {
if (!theme || theme === "null" || theme === "undefined") {
localStorage.setItem("theme", "dark");
setThemeMode("dark");
return;
}
const darkMediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
const applySystemTheme = () => {
const isDark = darkMediaQuery.matches;
setThemeMode(isDark ? "dark" : "light");
localStorage.setItem("theme", isDark ? "dark" : "light");
};
if (theme === "system") {
applySystemTheme();
darkMediaQuery.addEventListener("change", applySystemTheme);
return () => {
darkMediaQuery.removeEventListener("change", applySystemTheme);
};
} else {
setThemeMode(theme);
localStorage.setItem("theme", theme);
}
};
useEffect(() => {
if (serverside === true) return;
const theme = localStorage.getItem("theme");
if (!theme || theme === "null" || theme === "undefined") {
localStorage.setItem("theme", "dark");
setThemeMode("dark");
return;
}
let cleanup;
if (theme === "system") {
cleanup = handleThemeChange("system");
} else {
handleThemeChange(theme);
}
return () => {
if (cleanup) cleanup();
};
}, []);
return (
<Context.Provider value={{
@@ -42,9 +103,20 @@ export const AppContext = (props) => {
setIsDocSearchModalOpen,
searchBarModalOpen,
setSearchBarModalOpen,
supportEmail,
setSupportEmail,
logoutUrl,
setLogoutUrl,
leftSideBarOpenByClick,
setLeftSideBarOpenByClick,
windowWidth
windowWidth,
themeMode,
setThemeMode,
handleThemeChange,
brandColor,
setBrandColor,
brandName,
setBrandName,
}}>
{props.children}
</Context.Provider>
File diff suppressed because it is too large Load Diff
+295 -1
View File
@@ -1,4 +1,3 @@
import React from "react";
import { createTheme, adaptV4Theme } from "@mui/material/styles";
const theme = createTheme(adaptV4Theme({
@@ -153,3 +152,298 @@ const theme = createTheme(adaptV4Theme({
}));
export default theme;
export const getTheme = (themeMode, brandColor) =>
createTheme({
palette: {
mode: themeMode,
main: brandColor || "#FF8544",
primary: {
main: brandColor || "#FF8544",
contrastText: "#ffffff",
},
secondary: {
main: "rgba(255,255,255,0.7)",
contrastText:"#000000",
},
text: {
primary: themeMode === "dark" ? "#ffffff" : "#1A1A1A",
secondary: themeMode === "dark" ? "#9E9E9E" : "#616161",
},
type: themeMode,
inputColor: themeMode === "dark" ? "rgba(39,41,45,1)" : "rgba(245, 245, 245, 1)",
textColor: themeMode === "dark" ? "#F1F1F1" : "#1A1A1A",
textPrimary: themeMode === "dark" ? "rgba(255, 255, 255, 0.8)" : "rgba(26, 26, 26, 0.8)",
surfaceColor: themeMode === "dark" ? "#27292d" : "#EFEFEF",
platformColor: themeMode === "dark" ? "#212121" : "#ffffff",
backgroundColor: themeMode === "dark" ? "#1a1a1a" : "#f1f1f1",
distributionColor: themeMode === "dark" ? "#40E0D0" : "#008080",
cardBackgroundColor: themeMode === "dark" ? "#1e1e1e" : "#eaeaea",
cardHoverColor: themeMode === "dark" ? "#323232" : "#F0F0F0",
hoverColor: themeMode === "dark" ? "#323232" : "#D6D6D6",
green: themeMode === "dark" ? "#5cc879" : "#008000",
defaultBorder: themeMode === "dark" ? '1px solid #494949' : '1px solid #CCCCCC',
linkColor: brandColor === "#ff8544" ? "#f86a3e" : brandColor,
borderRadius: 10,
loaderColor: themeMode === "dark" ? "#1a1a1a" : "#E0E0E0",
jsonIconStyle: "round",
jsonTheme: themeMode === "dark" ? "summerfruit" : {
base00: "#ffffff", // background
base01: "#f0f0f0", // very light grey
base02: "#f5f5f5", // light grey
base03: "#999999", // dim text
base04: "#444444", // bold keys
base05: "#333333", // normal text
base06: "#1a1a1a", // darker text
base07: "#000000", // black
base08: "#f14c4c", // red
base09: "#f58c1f", // orange
base0A: "#f2c032", // yellow
base0B: "#51975d", // green
base0C: "#2aa198", // teal
base0D: "#007acc", // blue (keys!)
base0E: "#c586c0", // purple
base0F: "#d16969", // brown
},
jsonCollapseStringsAfterLength: 100,
drawer: {
backgroundColor: themeMode === "dark" ? "#262626" : "#f9f9f9"
},
reactJsonStyle: {
padding: 5,
width: "98%",
borderRadius: 5,
border: themeMode === "dark" ? "1px solid rgba(255,255,255,0.7)" : "1px solid rgba(0,0,0,0.3)",
backgroundColor: themeMode === "dark"
? "#1A1A1A"
: "#f1f1f1",
color: themeMode === "dark"
? "#F1F1F1"
: "#1A1A1A",
overflowX: "auto",
},
textFieldStyle: {
backgroundColor: themeMode === "dark" ? "#212121" : "#FFFFFF",
color: themeMode === "dark" ? "#ffffff" : "#000000",
borderRadius: "5px",
height: 40,
border: themeMode === "dark" ? "1px solid #4D4D4D" : "1px solid #E0E0E0",
},
DialogStyle: {
backgroundColor: themeMode === "dark" ? "#212121" : "#ffffff",
borderRadius: 2,
boxShadow: themeMode === "dark" ? "0px 0px 10px 0px rgba(0,0,0,0.75)" : "0px 0px 10px 0px rgba(0,0,0,0.2)",
border: themeMode === "dark" ? "1px solid #494949" : "1px solid #cccccc",
},
innerTextfieldStyle: {
height: 40,
fontSize: 16,
backgroundColor: themeMode === "dark" ? "#212121" : "#f5f5f5",
},
tooltip: {
backgroundColor: themeMode === "dark" ? "#212121" : "#ffffff",
color: themeMode === "dark" ? "#ffffff" : "#000000",
border: themeMode === "dark" ? "1px solid #494949" : "1px solid #cccccc",
},
chipStyle: {
backgroundColor: themeMode === "dark" ? "#333333" : "#F5F5F5",
borderColor: themeMode === "dark" ? "#444444" : "#E0E0E0",
color: themeMode === "dark" ? "#FFFFFF" : "#333333",
},
defaultImage: "/images/no_image.png",
singulOrange: "/images/singul_orange.png",
singulGreen: "/images/singul_green.png",
singulBlackWhite: "/images/singul_black_white.png",
scrollbarColor: themeMode === "dark" ? "#494949 #2f2f2f": "#c1c1c1 #f1f1f1",
scrollbarColorTransparent: themeMode === "dark" ? '#494949 transparent': "#c1c1c1 transparent",
},
typography: {
fontFamily: `"inter", "Roboto", "Helvetica", "Arial", sans-serif`,
color: themeMode === "dark" ? "#ffffff" : "#000000",
useNextVariants: true,
fontWeightLight: 300,
fontWeightRegular: 400,
fontWeightMedium: 500,
fontWeightSemiBold: 600,
fontWeightBold: 700,
allVariants: {
color: themeMode === "dark" ? "#ffffff" : "#1A1A1A",
},
h1: {
fontSize: 40,
color: themeMode === "dark" ? "#ffffff" : "#1A1A1A"
},
h2: {
fontSize: 36,
color: themeMode === "dark" ? "#ffffff" : "#1A1A1A"
},
h3: {
fontSize: 32,
color: themeMode === "dark" ? "#ffffff" : "#1A1A1A"
},
h4: {
fontSize: 30,
fontWeight: 500,
color: themeMode === "dark" ? "#ffffff" : "#1A1A1A"
},
h6: {
fontSize: 22,
color: themeMode === "dark" ? "#ffffff" : "#1A1A1A"
},
body1: {
fontSize: 16,
color: themeMode === "dark" ? "#ffffff" : "#1A1A1A"
},
body2: {
fontSize: 14,
color: themeMode === "dark" ? "#ffffff" : "#1A1A1A"
},
},
components: {
MuiButton: {
styleOverrides: {
root: {
textTransform: 'none',
borderRadius: '4px',
},
},
variants: [
{
props: { variant: 'text', color: 'primary' },
style: {
color: themeMode === "dark" ? "#ffffff" : "#1A1A1A",
whiteSpace: "nowrap",
textWrap: "normal",
},
},
{
props: { variant: 'text', color: 'secondary' },
style: {
color: themeMode === "dark" ? "#9E9E9E" : "#616161",
whiteSpace: "nowrap",
textWrap: "normal",
},
},
{
props: { variant: 'contained', color: 'primary' },
style: {
backgroundColor: themeMode === "dark" ? brandColor || '#ff8544' : brandColor || '#FF7C35',
color: themeMode === "dark" ? '#1a1a1a': '#FFFFFF',
borderRadius: '4px',
whiteSpace: "nowrap",
textWrap: "normal",
'&:hover': {
fontWeight: 600,
backgroundColor: themeMode === 'dark' ? brandColor || "#ff955c" : brandColor || '#FF8D4F',
color: themeMode === "dark" ? '#1a1a1a': '#FFFFFF',
},
},
},
{
props: { variant: 'contained', color: 'secondary' },
style: {
backgroundColor: themeMode === "dark" ? '#494949' : '#C9C9C9',
color: themeMode === "dark" ? '#ffffff' : '#4C4C4C',
borderRadius: '4px',
boxShadow: 'none',
whiteSpace: "nowrap",
textWrap: "normal",
'&:hover': {
fontWeight: 600,
border: themeMode === "dark" ? '1px solid #f1f1f1' : 'none',
backgroundColor: themeMode === "dark" ? '#494949' : '#C9C9C9',
color: themeMode === "dark" ? '#ffffff' : '#4C4C4C',
},
},
},
{
props: { variant: 'outlined', color: 'primary' },
style: {
borderColor: themeMode === "dark" ? brandColor || "#ff8544" : brandColor || "#cc5f1f",
color: themeMode === "dark" ? brandColor || "#ff8544" : brandColor || "#cc5f1f",
whiteSpace: "nowrap",
fontWeight: 'normal',
textWrap: "normal",
'&:hover': {
backgroundColor: themeMode === "dark" ? brandColor || "#ff8544" : "#ffe8dc",
color: themeMode === "dark" ? "#1a1a1a" : "#8a3d00",
fontWeight: 600,
},
},
},
{
props: { variant: 'outlined', color: 'secondary' },
style: {
border: '1px solid #C5C5C5',
color: themeMode === "dark" ? '#C5C5C5' : '#2D2D2D',
whiteSpace: "nowrap",
textWrap: "normal",
'&:hover': {
backgroundColor: themeMode === "dark" ? '#C5C5C5' : '#EFEFEF',
borderColor: themeMode === "dark" ? '#C5C5C5' : '#2D2D2D',
fontWeight: 600,
color: themeMode === "dark" ? '#1a1a1a' : '#1A1A1A',
},
},
},
],
},
MuiTab: {
styleOverrides: {
root: {
color: themeMode === "dark" ? "#C5C5C5" : "#1A1A1A",
},
},
},
},
overrides: {
MuiMenu: {
list: {
backgroundColor: themeMode === "dark" ? "#27292d" : "#ffffff",
},
},
MuiCssBaseline: {
MuiCssBaseline: {
styleOverrides: `
@font-face {
font-family: 'Roboto';
font-style: normal;
font-display: swap;
font-weight: 300;
src: local('Roboto Light'), local('Roboto-Light');
}
@font-face {
font-family: 'Roboto';
font-style: normal;
font-display: swap;
font-weight: 400;
src: local('Roboto'), local('Roboto-Regular');
}
@font-face {
font-family: 'Roboto';
font-style: normal;
font-display: swap;
font-weight: 500;
src: local('Roboto Medium'), local('Roboto-Medium');
}
@font-face {
font-family: 'Roboto';
font-style: normal;
font-display: swap;
font-weight: 600;
src: local('Roboto SemiBold'), local('Roboto-SemiBold');
}
@font-face {
font-family: 'Roboto';
font-style: normal;
font-display: swap;
font-weight: 700;
src: local('Roboto Bold'), local('Roboto-Bold');
}
`,
},
},
},
});
+10 -5
View File
@@ -1,6 +1,7 @@
import React, { useEffect, useState } from 'react';
import React, { useContext, useEffect, useState } from 'react';
import AdminNavBar from '../components/AdminNavBar.jsx';
import { toast } from "react-toastify";
import { Context } from '../context/ContextApi.jsx';
const Admin2 = (props) => {
// Destructure props if needed
@@ -10,13 +11,15 @@ const Admin2 = (props) => {
const [selectedOrganization, setSelectedOrganization] = useState({});
const [organizationFeatures, setOrganizationFeatures] = useState({});
const [orgRequest, setOrgRequest] = React.useState(true);
const [isOrgLoaded, setIsOrgLoaded] = React.useState(false);
const {brandName} = useContext(Context)
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
if (document !== undefined) {
if (selectedOrganization?.name !== undefined) {
document.title = selectedOrganization?.name + " - Admin - Shuffle"
if (selectedOrganization?.name !== undefined) {
document.title = brandName?.length > 0 ? selectedOrganization?.name + ` - Admin - ${brandName}` : selectedOrganization?.name + ` - Admin - Shuffle`;
} else {
document.title = "Admin - Shuffle"
document.title = brandName?.length > 0 ? `Admin - ${brandName}` : `Admin - Shuffle`;
}
}
@@ -151,6 +154,8 @@ const Admin2 = (props) => {
.catch((error) => {
console.log("Error getting org: ", error);
toast("Error getting current organization");
}).finally(() => {
setIsOrgLoaded(true)
});
};
@@ -330,7 +335,7 @@ const Admin2 = (props) => {
return (
<div style={{ display: 'flex', justifyContent: 'center', paddingTop: 29, zoom: 0.9}}>
<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}/>
<AdminNavBar userdata={userdata} isLoaded={isLoaded} isOrgLoaded={isOrgLoaded} selectedStatus={selectedStatus} setSelectedStatus={setSelectedStatus} selectedTab={selectedTab} orgId={selectedOrganization.id} handleStatusChange={handleStatusChange} handleEditOrg={handleEditOrg} handleGetOrg={handleGetOrg} setSelectedOrganization={setSelectedOrganization} selectedOrganization={selectedOrganization} setNotifications={setNotifications} stripeKey={stripeKey} notifications={notifications} checkLogin={checkLogin} globalUrl={globalUrl} isCloud={isCloud}/>
</div>
);
};
File diff suppressed because it is too large Load Diff
+5 -4
View File
@@ -59,6 +59,7 @@ const ApiExplorer = React.lazy(() => import("../components/ApiExplorer.jsx"));
const ApiExplorerWrapper = (props) => {
const { globalUrl, serverside, userdata, isLoggedIn, isLoaded} = props;
const { supportEmail } = useContext(Context);
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"
const location = useLocation();
const navigate = useNavigate();
@@ -165,7 +166,7 @@ const ApiExplorerWrapper = (props) => {
}
if (!found) {
toast.error(`Failed to get API data for '${appname}' (1). Contact support@shuffler.io if this persists.`, {
toast.error(`Failed to get API data for '${appname}' (1). Contact ${supportEmail} if this persists.`, {
"autoClose": 10000,
})
@@ -174,7 +175,7 @@ const ApiExplorerWrapper = (props) => {
},3000)
}
} else {
toast.error(`Failed to get API data for '${appname}' (2). Contact support@shuffler.io if this persists.`, {
toast.error(`Failed to get API data for '${appname}' (2). Contact ${supportEmail} if this persists.`, {
"autoClose": 10000,
})
setTimeout(()=>{
@@ -539,7 +540,7 @@ const ApiExplorerWrapper = (props) => {
}else if (openapi?.id?.length > 0) {
appid = openapi?.id;
}else{
toast.error("App id is missing and we can't run the API. Please contact support@shuffler.io if this persists.");
toast.error(`App id is missing and we can't run the API. Please contact ${supportEmail} if this persists.`);
return;
}
@@ -673,7 +674,7 @@ const ApiExplorerWrapper = (props) => {
if (data.result.includes("custom_action doesn't exist")) {
// No timeout error
toast.info("This API is being rebuilt due to missing functionality. Please wait a minute or two, then try again. If this persists, please report to support@shuffler.io", {
toast.info(`This API is being rebuilt due to missing functionality. Please wait a minute or two, then try again. If this persists, please report to ${supportEmail}`, {
"autoClose": 90000,
})
} else if (data.result.includes("authentication") && data.result.includes("Oauth2")) {
+170 -142
View File
@@ -1,7 +1,7 @@
import React, { useState, useEffect } from "react";
import React, { useState, useEffect, useContext } from "react";
import { makeStyles } from "@mui/styles";
import { BrowserView, MobileView } from "react-device-detect";
import theme from '../theme.jsx';
import { getTheme } from '../theme.jsx';
import {
Paper,
@@ -57,6 +57,7 @@ import { ToastContainer, toast } from "react-toastify"
import words from "shellwords";
import AvatarEditor from "react-avatar-editor";
import { Context } from "../context/ContextApi.jsx";
const surfaceColor = "#27292D";
const inputColor = "#383B40";
@@ -67,33 +68,7 @@ const bodyDivStyle = {
zoom: 0.8,
};
const actionListStyle = {
paddingLeft: "10px",
paddingRight: "10px",
paddingBottom: "10px",
paddingTop: "10px",
marginTop: "5px",
display: "flex",
color: "white",
position: "relative",
backgroundColor: theme.palette.platformColor,
};
const boxStyle = {
color: "white",
flex: "1",
marginLeft: "10px",
marginRight: "10px",
paddingLeft: "30px",
paddingRight: "30px",
paddingBottom: "30px",
paddingTop: "30px",
display: "flex",
flexDirection: "column",
backgroundColor: theme.palette.backgroundColor,
};
const dividerStyle = {
marginBottom: "10px",
@@ -427,9 +402,40 @@ const AppCreator = (defaultprops) => {
const [actionAmount, setActionAmount] = useState(increaseAmount);
const [newAppGroup, setNewAppGroup] = useState("")
const { themeMode, supportEmail, brandColor } = useContext(Context);
const theme = getTheme(themeMode, brandColor);
const [oauth2Scopes, setOauth2Scopes] = useState([]);
const [oauth2Type, setOauth2Type] = useState("delegated");
const actionListStyle = {
paddingLeft: "10px",
paddingRight: "10px",
paddingBottom: "10px",
paddingTop: "10px",
marginTop: "5px",
display: "flex",
color: theme.palette.text.primary,
position: "relative",
backgroundColor: theme.palette.platformColor,
};
const boxStyle = {
color: theme.palette.text.primary,
flex: "1",
marginLeft: "10px",
marginRight: "10px",
paddingLeft: "30px",
paddingRight: "30px",
paddingBottom: "30px",
paddingTop: "30px",
display: "flex",
flexDirection: "column",
backgroundColor: theme.palette.platformColor,
};
//client_credentials
const [oauth2GrantType, setOauth2GrantType] = useState("");
const defaultAuth = {
@@ -2626,7 +2632,7 @@ const AppCreator = (defaultprops) => {
if (response.status === 403) {
var urlParams = new URLSearchParams(window.location.search)
if (urlParams.has("id")) {
toast.error("Please log in to build this app. If this error persists, please contact support@shuffler.io")
toast.error(`Please log in to build this app. If this error persists, please contact ${supportEmail}`)
} else {
toast.error("Failed to save the app as you are not the owner. Redirecting you to the forking page. When there, save again.")
if (props.match.params.appid !== undefined && props.match.params.appid !== null && props.match.params.appid.length > 0) {
@@ -2678,12 +2684,12 @@ const AppCreator = (defaultprops) => {
const bearerAuth =
authenticationOption === "Bearer auth" ? (
<div style={{ color: "white" }}>
<div style={{ color: theme.palette.text.primary }}>
<h4>
<a
target="_blank"
href="https://swagger.io/docs/specification/authentication/bearer-authentication/"
style={{ textDecoriation: "none", color: "#f85a3e" }}
style={{ textDecoriation: "none", color: theme.palette.linkColor }}
>
Bearer auth
</a>
@@ -2696,12 +2702,12 @@ const AppCreator = (defaultprops) => {
// Basicauth
const basicAuth =
authenticationOption === "Basic auth" ? (
<div style={{ color: "white" }}>
<div style={{ color: theme.palette.text.primary }}>
<h4>
<a
target="_blank"
href="https://swagger.io/docs/specification/authentication/basic-authentication/"
style={{ textDecoriation: "none", color: "#f85a3e" }}
style={{ textDecoriation: "none", color: theme.palette.linkColor }}
>
Basic authentication
</a>
@@ -2795,7 +2801,7 @@ const AppCreator = (defaultprops) => {
flex: 2,
marginTop: 0,
marginBottom: 0,
backgroundColor: inputColor,
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
marginRight: 5,
}}
fullWidth={true}
@@ -2813,7 +2819,8 @@ const AppCreator = (defaultprops) => {
notchedOutline: classes.notchedOutline,
},
style: {
color: "white",
color: theme.palette.textFieldStyle.color,
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
minHeight: 50,
marginLeft: 5,
maxWidth: "95%",
@@ -2828,7 +2835,7 @@ const AppCreator = (defaultprops) => {
marginTop: 0,
marginBottom: 0,
flex: 2,
backgroundColor: inputColor,
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
marginRight: 5,
}}
fullWidth={true}
@@ -2843,7 +2850,8 @@ const AppCreator = (defaultprops) => {
}}
InputProps={{
style: {
color: "white",
color: theme.palette.textFieldStyle.color,
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
minHeight: 50,
marginLeft: 5,
maxWidth: "95%",
@@ -2861,9 +2869,9 @@ const AppCreator = (defaultprops) => {
value={extraAuth[index].type}
style={{
flex: 1,
backgroundColor: inputColor,
backgroundColor: theme.palette.backgroundColor,
paddingLeft: "10px",
color: "white",
color: theme.palette.text.primary,
height: 50,
borderRadius: theme.shape.borderRadius,
}}
@@ -2874,14 +2882,14 @@ const AppCreator = (defaultprops) => {
>
<MenuItem
key={index}
style={{ backgroundColor: inputColor, color: "white" }}
style={{ backgroundColor: theme.palette.backgroundColor, color: theme.palette.text.primary }}
value={"header"}
>
Header
</MenuItem>
<MenuItem
key={index}
style={{ backgroundColor: inputColor, color: "white" }}
style={{ backgroundColor: theme.palette.backgroundColor, color: theme.palette.text.primary }}
value={"query"}
>
Query
@@ -2929,7 +2937,7 @@ const AppCreator = (defaultprops) => {
const jwtAuth =
authenticationOption === "JWT" ? (
<div style={{ color: "white", marginTop: 20 }}>
<div style={{ color: theme.palette.text.primary, marginTop: 20 }}>
<Typography variant="body1">JWT authentication</Typography>
<Typography
variant="body2"
@@ -2948,7 +2956,7 @@ const AppCreator = (defaultprops) => {
variant="outlined"
defaultValue={parameterName}
helperText={
<span style={{ color: "white", marginBottom: "2px" }}>
<span style={{ color: theme.palette.text.primary, marginBottom: "2px" }}>
Must start with / and be a valid path
</span>
}
@@ -2958,7 +2966,7 @@ const AppCreator = (defaultprops) => {
notchedOutline: classes.notchedOutline,
},
style: {
color: "white",
color: theme.palette.text.primary,
},
}}
/>
@@ -2979,7 +2987,7 @@ const AppCreator = (defaultprops) => {
variant="outlined"
defaultValue={parameterName}
helperText={
<span style={{ color: "white", marginBottom: "2px" }}>
<span style={{ color: theme.palette.text.primary, marginBottom: "2px" }}>
Must use 'key=value&key=value' format
</span>
}
@@ -2991,7 +2999,7 @@ const AppCreator = (defaultprops) => {
notchedOutline: classes.notchedOutline,
},
style: {
color: "white",
color: theme.palette.text.primary,
},
}}
/>
@@ -3001,7 +3009,7 @@ const AppCreator = (defaultprops) => {
const oauth2Auth =
authenticationOption === "Oauth2" ? (
<div style={{ color: "white", marginTop: 20 }}>
<div style={{ color: theme.palette.text.primary, marginTop: 20 }}>
<Typography variant="body1">Oauth2 authentication</Typography>
<Typography
variant="body2"
@@ -3060,7 +3068,7 @@ const AppCreator = (defaultprops) => {
notchedOutline: classes.notchedOutline,
},
style: {
color: "white",
color: theme.palette.text.primary,
},
}}
/>
@@ -3111,7 +3119,7 @@ const AppCreator = (defaultprops) => {
notchedOutline: classes.notchedOutline,
},
style: {
color: "white",
color: theme.palette.text.primary,
},
}}
/>
@@ -3160,7 +3168,7 @@ const AppCreator = (defaultprops) => {
}}
InputProps={{
style: {
color: "white",
color: theme.palette.text.primary,
},
}}
/>
@@ -3177,7 +3185,7 @@ const AppCreator = (defaultprops) => {
required
InputProps={{
style: {
color: "white",
color: theme.palette.text.primary,
maxHeight: 160,
},
}}
@@ -3202,7 +3210,7 @@ const AppCreator = (defaultprops) => {
const apiKey =
authenticationOption === "API key" ? (
<div style={{ color: "white", marginTop: 20 }}>
<div style={{ color: theme.palette.text.primary, marginTop: 20 }}>
<Typography variant="body1">API key authentication</Typography>
<Typography variant="body2" color="textSecondary">
<b>Do NOT put your actual API-key.</b> Add the name of the field used for authentication, e.g. "X-APIKEY".
@@ -3221,7 +3229,7 @@ const AppCreator = (defaultprops) => {
variant="outlined"
value={parameterName}
helperText={
<span style={{ color: "white", marginBottom: "2px" }}>
<span style={{ color: theme.palette.text.primary, marginBottom: "2px" }}>
Can't be empty or contain any of the following: !#$%&'^"+-._~|]+$:=
</span>
}
@@ -3238,7 +3246,7 @@ const AppCreator = (defaultprops) => {
notchedOutline: classes.notchedOutline,
},
style: {
color: "white",
color: theme.palette.text.primary,
},
}}
/>
@@ -3255,7 +3263,7 @@ const AppCreator = (defaultprops) => {
borderRadius: theme.shape.borderRadius,
backgroundColor: inputColor,
paddingLeft: 10,
color: "white",
color: theme.palette.text.primary,
height: 57,
}}
inputProps={{
@@ -3271,7 +3279,7 @@ const AppCreator = (defaultprops) => {
return (
<MenuItem
key={index}
style={{ backgroundColor: inputColor, color: "white" }}
style={{ backgroundColor: inputColor, color: theme.palette.text.primary }}
value={data}
>
{data}
@@ -3463,15 +3471,14 @@ const AppCreator = (defaultprops) => {
<Tooltip title={chipRequired ? "Make not required" : "Make required"}>
<Chip
style={{
backgroundColor: chipRequired ? "#f86a3e" : "#3d3f43",
backgroundColor: chipRequired ? "#f86a3e" : theme.palette.chipStyle.backgroundColor,
height: 30,
margin: 3,
paddingLeft: 5,
paddingRight: 5,
height: 28,
cursor: "pointer",
borderColor: "#3d3f43",
color: "white",
borderColor: theme.palette.chipStyle.borderColor,
color: theme.palette.chipStyle.color,
}}
label={parsedChip}
onClick={() => {
@@ -3557,7 +3564,7 @@ const AppCreator = (defaultprops) => {
placeholder={"Query name (key)"}
label={"Query Key"}
helperText={
<span style={{ color: "white", marginBottom: "2px" }}>
<span style={{ color: theme.palette.text.primary, marginBottom: "2px" }}>
Click required to flip
</span>
}
@@ -3569,7 +3576,7 @@ const AppCreator = (defaultprops) => {
style={{flex: 3}}
InputProps={{
style: {
color: "white",
color: theme.palette.text.primary,
},
}}
/>
@@ -3589,7 +3596,7 @@ const AppCreator = (defaultprops) => {
style={{flex: 2}}
InputProps={{
style: {
color: "white",
color: theme.palette.text.primary,
},
}}
/>
@@ -3662,7 +3669,7 @@ const AppCreator = (defaultprops) => {
)}
<TextField
required
style={{ flex: "1", marginRight: "15px", backgroundColor: inputColor }}
style={{ flex: "1", marginRight: "15px", backgroundColor: theme.palette.textFieldStyle.backgroundColor }}
fullWidth={true}
placeholder={
'{\n\t"example": "${example}",\n\t"apikey": "${apikey}",\n\t"search": "1.2.3.5"\n}'
@@ -3678,7 +3685,7 @@ const AppCreator = (defaultprops) => {
}}
key={currentAction}
helperText={
<span style={{ color: "white", marginBottom: "2px" }}>
<span style={{ color: theme.palette.text.primary, marginBottom: "2px" }}>
Shows an example body to the user. ${} creates variables.
</span>
}
@@ -3687,7 +3694,8 @@ const AppCreator = (defaultprops) => {
notchedOutline: classes.notchedOutline,
},
style: {
color: "white",
color: theme.palette.textFieldStyle.color,
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
},
}}
/>
@@ -3700,7 +3708,7 @@ const AppCreator = (defaultprops) => {
<b>Example success response</b>
<TextField
required
style={{ flex: "1", marginRight: "15px", backgroundColor: inputColor }}
style={{ flex: "1", marginRight: "15px", backgroundColor: theme.palette.textFieldStyle.backgroundColor }}
fullWidth={true}
placeholder={
'{\n\t"email": "testing@test.com",\n\t"firstname": "testing"\n}'
@@ -3712,14 +3720,15 @@ const AppCreator = (defaultprops) => {
defaultValue={currentAction["example_response"]}
onChange={(e) => setActionField("example_response", e.target.value)}
helperText={
<span style={{ color: "white", marginBottom: "2px" }}>
<span style={{ color: theme.palette.text.primary, marginBottom: "2px" }}>
Helps with autocompletion and understanding of the endpoint
</span>
}
key={currentAction}
InputProps={{
style: {
color: "white",
color: theme.palette.textFieldStyle.color,
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
},
}}
/>
@@ -3794,8 +3803,8 @@ const AppCreator = (defaultprops) => {
fullWidth
PaperProps={{
style: {
backgroundColor: surfaceColor,
color: "white",
backgroundColor: theme.palette.drawer.backgroundColor,
color: theme.palette.text.primary,
minWidth: 700,
maxWidth: 700,
},
@@ -3812,15 +3821,15 @@ const AppCreator = (defaultprops) => {
setFileUploadEnabled(false);
}}
>
<FormControl style={{ backgroundColor: surfaceColor, color: "white" }}>
<FormControl style={{ backgroundColor: theme.palette.drawer.backgroundColor, color: theme.palette.text.primary }}>
<DialogTitle style={{marginTop: 45, }}>
<div style={{ color: "white" }}>New action</div>
<div style={{ color: theme.palette.text.primary }}>New action</div>
</DialogTitle>
<DialogContent style={{paddingBottom: 100, }}>
<a
target="_blank"
href="https://shuffler.io/docs/app_creation#actions"
style={{ textDecoration: "none", color: "#f85a3e" }}
style={{ textDecoration: "none", color: theme.palette.linkColor }}
>
Learn more about actions
</a>
@@ -3832,7 +3841,8 @@ const AppCreator = (defaultprops) => {
flex: "1",
marginTop: 5,
marginRight: 15,
backgroundColor: inputColor,
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
color: theme.palette.textFieldStyle.color,
}}
fullWidth={true}
placeholder="Name"
@@ -3871,7 +3881,8 @@ const AppCreator = (defaultprops) => {
notchedOutline: classes.notchedOutline,
},
style: {
color: "white",
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
color: theme.palette.textFieldStyle.color,
},
}}
/>
@@ -3883,7 +3894,8 @@ const AppCreator = (defaultprops) => {
flex: "1",
marginTop: 5,
marginRight: "15px",
backgroundColor: inputColor,
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
color: theme.palette.textFieldStyle.color,
}}
fullWidth={true}
placeholder="Description"
@@ -3895,7 +3907,8 @@ const AppCreator = (defaultprops) => {
onChange={(e) => setActionField("description", e.target.value)}
InputProps={{
style: {
color: "white",
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
color: theme.palette.textFieldStyle.color,
},
}}
/>
@@ -3917,14 +3930,18 @@ const AppCreator = (defaultprops) => {
}}
value={currentActionMethod}
style={{
backgroundColor: inputColor,
paddingLeft: "10px",
color: "white",
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
color: theme.palette.textFieldStyle.color,
height: "50px",
}}
inputProps={{
name: "Method",
id: "method-option",
style: {
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
color: theme.palette.textFieldStyle.color,
},
}}
>
@@ -3939,7 +3956,7 @@ const AppCreator = (defaultprops) => {
>
<Chip
style={{
color: "white",
color: theme.palette.text.primary,
borderRadius: theme.shape.borderRadius,
minWidth: 80,
marginRight: 10,
@@ -3963,7 +3980,8 @@ const AppCreator = (defaultprops) => {
flex: "1",
marginRight: "15px",
marginTop: "5px",
backgroundColor: inputColor,
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
color: theme.palette.textFieldStyle.color,
}}
fullWidth={true}
placeholder="URL path"
@@ -3976,7 +3994,7 @@ const AppCreator = (defaultprops) => {
setUrlPath(e.target.value);
}}
helperText={
<span style={{ color: "white", marginBottom: "2px" }}>
<span style={{ color: theme.palette.text.primary, marginBottom: "2px" }}>
The path to use. Must start with /. Use {"{variablename}"} to
have path variables
</span>
@@ -3987,7 +4005,8 @@ const AppCreator = (defaultprops) => {
input: classes.input,
},
style: {
color: "white",
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
color: theme.palette.textFieldStyle.color,
},
}}
onBlur={(event) => {
@@ -4270,7 +4289,7 @@ const AppCreator = (defaultprops) => {
defaultValue={currentAction["file_field"]}
onChange={(e) => setActionField("file_field", e.target.value)}
helperText={
<span style={{ color: "white", marginBottom: "2px" }}>
<span style={{ color: theme.palette.text.primary, marginBottom: "2px" }}>
The File field to interact with
</span>
}
@@ -4279,7 +4298,7 @@ const AppCreator = (defaultprops) => {
notchedOutline: classes.notchedOutline,
},
style: {
color: "white",
color: theme.palette.text.primary,
},
}}
/>
@@ -4294,7 +4313,8 @@ const AppCreator = (defaultprops) => {
flex: "1",
marginRight: "15px",
marginTop: "5px",
backgroundColor: inputColor,
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
color: theme.palette.textFieldStyle.color,
}}
fullWidth={true}
placeholder={
@@ -4308,13 +4328,14 @@ const AppCreator = (defaultprops) => {
minRows="2"
onChange={(e) => setActionField("headers", e.target.value)}
helperText={
<span style={{ color: "white", marginBottom: "2px" }}>
<span style={{ color: theme.palette.text.primary, marginBottom: "2px" }}>
Headers that are part of the request. Default: EMPTY
</span>
}
InputProps={{
style: {
color: "white",
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
color: theme.palette.textFieldStyle.color,
},
}}
/>
@@ -4323,14 +4344,14 @@ const AppCreator = (defaultprops) => {
{bodyInfo}
<Divider
style={{
backgroundColor: "rgba(255,255,255,0.5)",
backgroundColor: theme.palette.defaultBorder,
marginTop: 15,
marginBottom: 15,
}}
/>
{exampleResponse}
</DialogContent>
<div style={{position: "fixed", backgroundColor: theme.palette.surfaceColor, bottom: 0, width: "100%", padding: 25, borderTop: "1px solid rgba(255,255,255,0.3)", }}>
<div style={{position: "fixed", backgroundColor: theme.palette.drawer.backgroundColor, bottom: 0, width: "100%", padding: 25, borderTop: theme.palette.defaultBorder, }}>
<Button
color="primary"
variant={urlPath.length > 0 ? "contained" : "outlined"}
@@ -4449,7 +4470,7 @@ const AppCreator = (defaultprops) => {
<Chip
style={{
backgroundColor: bgColor,
color: "white",
color: theme.palette.text.primary,
borderRadius: theme.shape.borderRadius,
minWidth: 80,
marginRight: 10,
@@ -4501,9 +4522,9 @@ const AppCreator = (defaultprops) => {
style={{
border: data.action_label === undefined || data.action_label === "No Label" ? "" : `2px solid ${bgColor}`,
borderRadius: theme.shape.borderRadius,
backgroundColor: inputColor,
backgroundColor: theme.palette.backgroundColor,
paddingLeft: 10,
color: "white",
color: theme.palette.text.primary,
height: 30,
maxWidth: 35,
marginLeft: 10,
@@ -4620,13 +4641,13 @@ const AppCreator = (defaultprops) => {
const tagView = (
<div style={{ color: "white" }}>
<div style={{ color: theme.palette.text.primary }}>
{/*
<ChipInput
style={{marginTop: 10}}
InputProps={{
style:{
color: "white",
color: theme.palette.text.primary,
},
}}
placeholder="Categories"
@@ -4661,7 +4682,7 @@ const AppCreator = (defaultprops) => {
}}
value={newWorkflowCategories.length === 0 ? "Select a category" : newWorkflowCategories[0]}
style={{ backgroundColor: inputColor, color: "white", height: "50px" }}
style={{ backgroundColor: theme.palette.backgroundColor, color: theme.palette.text.primary, height: "50px" }}
>
{categories.map((data, index) => {
if (data === undefined || data === null || data === "" || data === undefined || data === null || data === "") {
@@ -4671,7 +4692,7 @@ const AppCreator = (defaultprops) => {
return (
<MenuItem
key={index}
style={{ backgroundColor: inputColor, color: "white" }}
sx={{ backgroundColor: theme.palette.backgroundColor, color: theme.palette.text.primary, "&:hover" : { backgroundColor: theme.palette.hoverColor} }}
value={data.name}
>
{data.name}
@@ -4701,7 +4722,7 @@ const AppCreator = (defaultprops) => {
}}
InputProps={{
style: {
color: "white",
color: theme.palette.text.primary,
},
}}
/>
@@ -4713,7 +4734,7 @@ const AppCreator = (defaultprops) => {
style={{ marginTop: 10 }}
InputProps={{
style: {
color: "white",
color: theme.palette.text.primary,
},
}}
placeholder="Tags"
@@ -4786,7 +4807,7 @@ const AppCreator = (defaultprops) => {
}}
InputProps={{
style: {
color: "white",
color: theme.palette.text.primary,
height: 50,
fontSize: "1em",
},
@@ -5101,7 +5122,7 @@ const AppCreator = (defaultprops) => {
/*
{selectedAction.authentication.map(data => (
<MenuItem key={data.id} style={{backgroundColor: inputColor, color: "white"}} value={data}>
<MenuItem key={data.id} style={{backgroundColor: inputColor, color: theme.palette.text.primary}} value={data}>
*/
};
@@ -5120,7 +5141,7 @@ const AppCreator = (defaultprops) => {
target="_blank"
rel="norefferer"
href="https://shuffler.io/docs/app_creation#authentication"
style={{ textDecoration: "none", color: "#f85a3e" }}
style={{ textDecoration: "none", color: theme.palette.linkColor }}
>
What is this?
</a>
@@ -5135,7 +5156,7 @@ const AppCreator = (defaultprops) => {
}}
InputProps={{
style: {
color: "white",
color: theme.palette.text.primary,
marginLeft: "5px",
maxWidth: "95%",
height: 50,
@@ -5175,7 +5196,7 @@ const AppCreator = (defaultprops) => {
}}
InputProps={{
style: {
color: "white",
color: theme.palette.text.primary,
marginLeft: "5px",
maxWidth: "95%",
height: 50,
@@ -5225,7 +5246,7 @@ const AppCreator = (defaultprops) => {
};
const actionView = (
<div style={{ color: "white", position: "relative" }}>
<div style={{ color: theme.palette.text.primary, position: "relative" }}>
<div style={{ position: "absolute", right: 0, top: 0 }}>
{actionAmount > 0 && actionAmount < filteredActions.length ? (
<Button
@@ -5277,15 +5298,14 @@ const AppCreator = (defaultprops) => {
key={index}
style={{
backgroundColor:
tag === selectedCategory ? "#f86a3e" : "#3d3f43",
tag === selectedCategory ? "#f86a3e" : theme.palette.chipStyle.backgroundColor,
height: 30,
margin: 3,
paddingLeft: 5,
paddingRight: 5,
height: 28,
cursor: "pointer",
borderColor: "#3d3f43",
color: "white",
borderColor: theme.palette.chipStyle.borderColor,
color: theme.palette.chipStyle.color,
}}
label={newname}
onClick={() => {
@@ -5411,13 +5431,13 @@ const AppCreator = (defaultprops) => {
);
const testView = (
<div style={{ color: "white" }}>
<div style={{ color: theme.palette.text.primary }}>
<h2>Test</h2>
Test an action to see whether it performs in an expected way.
<a
target="_blank"
href="https://shuffler.io/docs/app_creation#testing"
style={{ textDecoration: "none", color: "#f85a3e" }}
style={{ textDecoration: "none", color: theme.palette.linkColor }}
>
&nbsp;TBD: Click here to learn more about testing
</a>
@@ -5572,7 +5592,7 @@ const AppCreator = (defaultprops) => {
PaperProps={{
style: {
backgroundColor: surfaceColor,
color: "white",
color: theme.palette.text.primary,
minWidth: "300px",
minHeight: "300px",
},
@@ -5721,7 +5741,7 @@ const AppCreator = (defaultprops) => {
PaperProps={{
style: {
backgroundColor: surfaceColor,
color: "white",
color: theme.palette.text.primary,
minWidth: "800px",
minHeight: "320px",
},
@@ -5741,7 +5761,7 @@ const AppCreator = (defaultprops) => {
margin="normal"
InputProps={{
style: {
color: "white",
color: theme.palette.text.primary,
height: "50px",
fontSize: "1em",
},
@@ -5768,7 +5788,7 @@ const AppCreator = (defaultprops) => {
setOpenApi(e.target.value);
}}
helperText={
<span style={{ color: "white", marginBottom: "2px" }}>
<span style={{ color: theme.palette.text.primary, marginBottom: "2px" }}>
Must point to a version 2 or 3 OpenAPI specification.
</span>
}
@@ -5832,14 +5852,14 @@ const AppCreator = (defaultprops) => {
// Random names for type & autoComplete. Didn't research :^)
const landingpageDataBrowser = (
<div style={{ paddingBottom: 100, color: "white" }}>
<div style={{ paddingBottom: 100, color: theme.palette.text.primary, }}>
<Breadcrumbs
aria-label="breadcrumb"
separator=""
style={{ color: "white" }}
style={{ color: theme.palette.text.primary }}
>
<Link to="/apps" style={{ textDecoration: "none", color: "inherit" }}>
<h2 style={{ color: "rgba(255,255,255,0.5)" }}>
<h2 style={{ color: theme.palette.textColor }}>
<AppsIcon style={{ marginRight: 10 }} />
Apps
</h2>
@@ -5860,10 +5880,10 @@ const AppCreator = (defaultprops) => {
ref={(ref) => (upload = ref)}
onChange={editHeaderImage}
/>
<Paper style={boxStyle}>
<div style={boxStyle}>
<div style={{display: "flex", }}>
<div style={{flex: 1, }}>
<h2 style={{ marginBottom: "10px", color: "white" }}>
<h2 style={{ marginBottom: "10px", color: theme.palette.text.primary }}>
General information
</h2>
</div>
@@ -5907,13 +5927,13 @@ const AppCreator = (defaultprops) => {
<a
target="_blank"
href="https://shuffler.io/docs/app_creation#app-creator-instructions"
style={{ textDecoration: "none", color: "#f85a3e" }}
style={{ textDecoration: "none", color: theme.palette.linkColor }}
>
Click to learn more about app creation
</a>
<div
style={{
color: "white",
color: theme.palette.text.primary,
flex: "1",
display: "flex",
flexDirection: "row",
@@ -5944,7 +5964,7 @@ const AppCreator = (defaultprops) => {
/>
</div>
</Tooltip>
<div style={{ flex: "3", color: "white", marginLeft: 20, }}>
<div style={{ flex: "3", color: theme.palette.text.primary, marginLeft: 20, }}>
<div style={{ marginTop: "10px" }} />
Name
<TextField
@@ -5953,7 +5973,7 @@ const AppCreator = (defaultprops) => {
flex: "1",
marginTop: "5px",
marginRight: "15px",
backgroundColor: inputColor,
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
}}
fullWidth={true}
placeholder="Name"
@@ -5989,7 +6009,8 @@ const AppCreator = (defaultprops) => {
color="primary"
InputProps={{
style: {
color: "white",
color: theme.palette.textFieldStyle.color,
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
height: "50px",
fontSize: "1em",
},
@@ -6005,13 +6026,19 @@ const AppCreator = (defaultprops) => {
style={{
marginTop: 5,
marginRight: 15,
backgroundColor: inputColor,
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
maxHeight: 250,
overflowY: "auto"
}}
fullWidth={true}
type="name"
id="outlined-with-placeholder"
inputProps={{
style: {
color: theme.palette.textFieldStyle.color,
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
}
}}
margin="normal"
multiline
variant="outlined"
@@ -6033,7 +6060,7 @@ const AppCreator = (defaultprops) => {
/>
<Typography
variant="h6"
style={{ marginTop: 10, marginBottom: 10, color: "white" }}
style={{ marginTop: 10, marginBottom: 10, color: theme.palette.text.primary }}
>
API information
</Typography>
@@ -6042,14 +6069,15 @@ const AppCreator = (defaultprops) => {
</Typography>
<TextField
color="primary"
style={{ backgroundColor: inputColor, marginTop: "5px" }}
style={{ backgroundColor: theme.palette.textFieldStyle.backgroundColor, marginTop: "5px" }}
InputProps={{
classes: {
notchedOutline: classes.notchedOutline,
},
style: {
height: "50px",
color: "white",
color: theme.palette.textFieldStyle.color,
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
fontSize: "1em",
},
}}
@@ -6061,7 +6089,7 @@ const AppCreator = (defaultprops) => {
variant="outlined"
value={baseUrl}
helperText={
<span style={{ color: "white", marginBottom: "2px" }}>
<span style={{ color: theme.palette.text.primary, marginBottom: "2px" }}>
Must start with http(s):// and CANT end with /.{" "}
</span>
}
@@ -6098,7 +6126,7 @@ const AppCreator = (defaultprops) => {
<a
target="_blank"
href="https://shuffler.io/docs/app_creation#authentication"
style={{ textDecoration: "none", color: "#f85a3e" }}
style={{ textDecoration: "none", color: theme.palette.linkColor }}
>
Learn more about app authentication
</a>
@@ -6122,15 +6150,15 @@ const AppCreator = (defaultprops) => {
}}
value={authenticationOption}
style={{
backgroundColor: inputColor,
color: "white",
backgroundColor: theme.palette.backgroundColor,
color: theme.palette.text.primary,
height: "50px",
}}
>
{authenticationOptions.map((data, index) => (
<MenuItem
key={index}
style={{ backgroundColor: inputColor, color: "white" }}
sx={{ backgroundColor: theme.palette.backgroundColor, color: theme.palette.text.primary, "&:hover": {backgroundColor: theme.palette.backgroundColor} }}
value={data}
>
{data}
@@ -6165,14 +6193,14 @@ const AppCreator = (defaultprops) => {
value={oauth2Type}
style={{
backgroundColor: inputColor,
color: "white",
color: theme.palette.text.primary,
height: "50px",
}}
>
{["delegated", "application"].map((data, index) => (
<MenuItem
key={index}
style={{ backgroundColor: inputColor, color: "white" }}
style={{ backgroundColor: inputColor, color: theme.palette.text.primary }}
value={data}
>
{data}
@@ -6193,14 +6221,14 @@ const AppCreator = (defaultprops) => {
value={oauth2GrantType}
style={{
backgroundColor: inputColor,
color: "white",
color: theme.palette.text.primary,
height: "50px",
}}
>
{["client_credentials", "password"].map((data, index) => (
<MenuItem
key={index}
style={{ backgroundColor: inputColor, color: "white" }}
style={{ backgroundColor: inputColor, color: theme.palette.text.primary }}
value={data}
>
{data}
@@ -6225,8 +6253,8 @@ const AppCreator = (defaultprops) => {
{/*authenticationOption === "No authentication" ? null :
<FormControlLabel
style={{color: "white", marginBottom: 0, marginTop: 20}}
label=<div style={{color: "white"}}>Authentication required (default true)</div>
style={{color: theme.palette.text.primary, marginBottom: 0, marginTop: 20}}
label=<div style={{color: theme.palette.text.primary}}>Authentication required (default true)</div>
control={<Switch checked={authenticationRequired} onChange={() => {
setAuthenticationRequired(!authenticationRequired)
}} />}
@@ -6321,7 +6349,7 @@ const AppCreator = (defaultprops) => {
{errorCode.length > 0 ? `Upload Error: ${errorCode}` : null}
</Typography>
</Paper>
</div>
</div>
);
+84 -72
View File
@@ -1,6 +1,6 @@
import React, { useState, useEffect, useContext } from "react";
import theme from "../theme.jsx";
import {getTheme} from "../theme.jsx";
import ReactGA from "react-ga4";
import Markdown from "react-markdown";
import algoliasearch from "algoliasearch/lite";
@@ -86,53 +86,6 @@ import { sortByKey } from "../views/AngularWorkflow.jsx";
import { v4 as uuidv4 } from "uuid";
import aa from "search-insights";
const surfaceColor = "#27292D";
const inputColor = "#383B40";
const chipStyle = {
marginTop: 5,
backgroundColor: "#3d3f43",
height: 30,
marginRight: 5,
paddingLeft: 5,
paddingRight: 5,
height: 28,
cursor: "pointer",
borderColor: "#3d3f43",
color: "white",
};
const actionListStyle = {
paddingLeft: 10,
paddingRight: 10,
paddingTop: 10,
marginTop: 5,
backgroundColor: inputColor,
display: "flex",
color: "white",
maxWidth: 350,
minWidth: 350,
maxHeight: 54,
overflow: "hidden",
};
const boxStyle = {
color: "white",
flex: "3",
margin: 10,
paddingLeft: 30,
paddingRight: 30,
paddingBottom: 30,
paddingTop: 30,
display: "flex",
flexDirection: "column",
position: "relative",
maxHeight: 180,
overflow: "hidden",
};
const buttonBackground = "linear-gradient(to right, #f86a3e, #f34079)";
// AppTypes:
// 0 = OpenAPI (VALID)
@@ -162,7 +115,54 @@ const AppExplorer = (props) => {
const classes = useStyles()
let navigate = useNavigate()
const { leftSideBarOpenByClick, } = useContext(Context);
const { leftSideBarOpenByClick, themeMode, supportEmail } = useContext(Context);
const theme = getTheme(themeMode);
const surfaceColor = "#27292D";
const inputColor = theme.palette.textFieldStyle.backgroundColor;
const chipStyle = {
marginTop: 5,
backgroundColor: theme.palette.chipStyle.backgroundColor,
height: 30,
marginRight: 5,
paddingLeft: 5,
paddingRight: 5,
cursor: "pointer",
borderColor: theme.palette.chipStyle.borderColor,
color: theme.palette.chipStyle.color,
};
const actionListStyle = {
paddingLeft: 10,
paddingRight: 10,
paddingTop: 10,
marginTop: 5,
backgroundColor: inputColor,
display: "flex",
color: theme.palette.text.primary,
maxWidth: 350,
minWidth: 350,
maxHeight: 54,
overflow: "hidden",
};
const boxStyle = {
color: theme.palette.text.primary,
flex: "3",
margin: 10,
paddingLeft: 30,
paddingRight: 30,
paddingBottom: 30,
paddingTop: 30,
display: "flex",
flexDirection: "column",
position: "relative",
maxHeight: 180,
overflow: "hidden",
};
const buttonBackground = "linear-gradient(to right, #f86a3e, #f34079)";
const params = useParams();
//var props = JSON.parse(JSON.stringify(defaultprops))
@@ -385,9 +385,9 @@ const AppExplorer = (props) => {
extraInfo = (
<div
style={{
backgroundColor: theme.palette.inputColor,
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
padding: 15,
borderRadius: theme.palette?.borderRadius,
borderRadius: theme.palette?.textFieldStyle.borderRadius,
marginBottom: 30,
display: "flex",
}}
@@ -555,7 +555,9 @@ const AppExplorer = (props) => {
}}
style={{
cursor: "pointer",
color: "white",
backgroundColor: theme.palette.chipStyle.backgroundColor,
borderColor: theme.palette.chipStyle.borderColor,
color: theme.palette.chipStyle.color,
borderRadius: 40,
minWidth: 80,
marginRight: 10,
@@ -586,7 +588,9 @@ const AppExplorer = (props) => {
disabled={included === false}
style={{
cursor: included ? "pointer" : "default",
color: "white",
color: theme.palette.chipStyle.color,
borderColor: theme.palette.chipStyle.borderColor,
backgroundColor: theme.palette.chipStyle.backgroundColor,
borderRadius: 40,
minWidth: 80,
marginRight: 10,
@@ -776,7 +780,7 @@ const AppExplorer = (props) => {
if (responseJson.reason !== undefined) {
toast("Failed to perform action: "+responseJson.reason);
} else {
toast("Failed to perform action. Please try again or contact support@shuffler.io");
toast(`Failed to perform action. Please try again or contact ${supportEmail}`);
}
}
} else {
@@ -3093,7 +3097,7 @@ const AppExplorer = (props) => {
margin: 10,
padding: 30,
backgroundColor: boxStyle.backgroundColor,
color: "white",
color: theme.palette.text.primary,
textAlign: "left",
paddingBottom: 50,
overflow: "hidden",
@@ -3117,12 +3121,12 @@ const AppExplorer = (props) => {
style={{ marginBottom: 0, marginLeft: 0, marginRight: 0, minWidth: 800, maxWidth: 800, margin: "auto", }}
aria-label="disabled tabs example"
>
<Tab style={{marginLeft: 0, }} icon={<DescriptionIcon />} label="Docs" />
<Tab style={{marginLeft: 0, color: theme.palette.text.primary }} icon={<DescriptionIcon />} label="Docs" />
<Tab icon={appType === 0 || appType === 2 ? <OpenInNewIcon /> : <AppsIcon />} label={appType === 0 || appType === 2 ? "Explore the API" : "Try it out"} />
<Tab icon={<ShowChartIcon />} label="Stats" />
<Tab icon={<PolylineIcon />} disabled label="Integrations" />
<Tab icon={<PersonIcon />} disabled={userdata.support !== true} label="Creator" value={4} />
<Tab icon={<ShowChartIcon />} style={{color: theme.palette.text.primary}} label="Stats" />
<Tab icon={<PolylineIcon />} disabled style={{color: theme.palette.text.secondary}} label="Integrations" />
<Tab icon={<PersonIcon />} disabled={userdata.support !== true} style={{color: userdata.support !== true ? theme.palette.text.secondary: theme.palette.text.primary}} label="Creator" value={4} />
</Tabs>
<div style={{ marginTop: 25 }}>
{selectedTab === 1 && app.skipped_build == false ? (
@@ -3883,12 +3887,12 @@ const AppExplorer = (props) => {
</div>
<Button
variant="contained"
style={{ borderRadius: "2px", textTransform: 'none', fontSize:16, color: "#1a1a1a", backgroundColor: "#ff8544" }}
color="primary"
style={{ borderRadius: "2px", textTransform: 'none', fontSize:16, }}
onClick={() => {
updateAppField(selectedApp.id, "sharing", true);
setPublishModalOpen(false);
}}
color="primary"
>
Yes
</Button>
@@ -3919,13 +3923,13 @@ const AppExplorer = (props) => {
<Breadcrumbs
aria-label="breadcrumb"
separator=""
style={{ color: "white", marginLeft: 15, flex: 100 }}
style={{ color: theme.palette.text.primary, marginLeft: 15, flex: 100 }}
>
<Link
to="/search"
style={{ textDecoration: "none", color: "inherit" }}
>
<h2 style={{ color: "rgba(255,255,255,0.5)" }}>
<h2 style={{ color: theme.palette.text.primary }}>
<AppsIcon style={{ marginRight: 10 }} />
Apps
</h2>
@@ -3971,9 +3975,7 @@ const AppExplorer = (props) => {
) : null}
{appType === 0 || appType === 2 ? (
<IconButton
color="primary"
style={{ marginRight: 20 }}
variant="contained"
onClick={() => {
const data = openapi;
let exportFileDefaultName = name + ".json";
@@ -4020,7 +4022,7 @@ const AppExplorer = (props) => {
}}
>
<Tooltip title="Download OpenAPI" placement="top">
<CloudDownloadIcon color="secondary" />
<CloudDownloadIcon color={theme.palette.text.primary} />
</Tooltip>
</IconButton>
) : null}
@@ -4036,7 +4038,7 @@ const AppExplorer = (props) => {
}}
>
<Tooltip title="Public Authentication link for the current Organization. Times out every 24 hours." placement="top">
<LockOpenIcon />
<LockOpenIcon color={theme.palette.text.primary}/>
</Tooltip>
</IconButton>
: null}
@@ -4197,8 +4199,8 @@ const AppExplorer = (props) => {
}}
style={{
width: 150,
backgroundColor: theme.palette.surfaceColor,
color: "white",
backgroundColor: theme.palette.backgroundColor,
color: theme.palette.text.primary,
height: 40,
marginTop: 5
}}
@@ -4213,7 +4215,7 @@ const AppExplorer = (props) => {
return (
<MenuItem
key={data}
style={{ backgroundColor: inputColor, color: "white", display: 'flex' }}
sx={{ backgroundColor: inputColor, color: theme.palette.text.primary, display: 'flex', "&:hover": { backgroundColor: theme.palette.hoverColor } }}
value={data}
>
{data}
@@ -4373,8 +4375,8 @@ const AppExplorer = (props) => {
style={{
borderRadius: 25,
fontSize: 11,
color: "white",
backgroundColor: "rgba(255,255,255,0)",
color: theme.palette.text.primary,
backgroundColor: theme.palette.platformColor,
border: "1px solid #ddf4e1",
textTransform: "none",
marginLeft: 4,
@@ -4466,6 +4468,11 @@ const AppExplorer = (props) => {
color: "white",
textAlign: "center",
}}
sx={{
"&:hover": {
backgroundColor: theme.palette.hoverColor,
},
}}
>
<CardActionArea
component={Link}
@@ -4553,6 +4560,11 @@ const AppExplorer = (props) => {
color: "white",
textAlign: "center",
}}
sx={{
"&:hover": {
backgroundColor: theme.palette.hoverColor,
},
}}
>
<CardActionArea
component={Link}
+78 -51
View File
@@ -1,5 +1,5 @@
import React, { useState, useEffect, useContext, useCallback, memo, useMemo, useRef } from "react";
import theme from "../theme.jsx";
import {getTheme} from "../theme.jsx";
import { isMobile } from "react-device-detect";
import { useLocation, useNavigate } from "react-router-dom";
@@ -55,7 +55,9 @@ const AppCard = ({ data, index, mouseHoverIndex, setMouseHoverIndex, globalUrl,
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 = `/apps/${data.id}`
const { themeMode } = useContext(Context);
const theme = getTheme(themeMode);
var canEditApp = userdata?.support || userdata?.id === data?.owner ||
(userdata?.admin === "true" && userdata?.active_org?.id === data?.reference_org) || data?.contributors?.includes(userdata?.id)
@@ -104,7 +106,7 @@ const AppCard = ({ data, index, mouseHoverIndex, setMouseHoverIndex, globalUrl,
display: "flex",
fontFamily: theme?.typography?.fontFamily,
width: '100%',
backgroundColor: mouseHoverIndex === index ? "#2F2F2F" : "#212121"
backgroundColor: mouseHoverIndex === index ? theme.palette.hoverColor : theme.palette.platformColor,
}}
onClick={() => {
handleAppClick(data);
@@ -150,7 +152,7 @@ const AppCard = ({ data, index, mouseHoverIndex, setMouseHoverIndex, globalUrl,
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
color: '#F1F1F1'
color: theme.palette.text.primary,
}}>
{data.name.replace(/_/g, ' ').replace(/\b\w/g, char => char.toUpperCase())}
</div>
@@ -161,7 +163,7 @@ const AppCard = ({ data, index, mouseHoverIndex, setMouseHoverIndex, globalUrl,
textOverflow: "ellipsis",
whiteSpace: "nowrap",
marginLeft: 8,
color: "rgba(158, 158, 158, 1)"
color: theme.palette.text.secondary,
}}>
{data.categories ? data.categories.join(", ") : "NA"}
</div>
@@ -172,7 +174,7 @@ const AppCard = ({ data, index, mouseHoverIndex, setMouseHoverIndex, globalUrl,
whiteSpace: "nowrap",
width: "100%",
marginLeft: 8,
color: "rgba(158, 158, 158, 1)",
color: theme.palette.text.secondary,
display: "flex",
justifyContent: 'space-between',
paddingRight: 15,
@@ -202,7 +204,7 @@ const AppCard = ({ data, index, mouseHoverIndex, setMouseHoverIndex, globalUrl,
}}>
{
canEditApp ? (
<button style={{ backgroundColor: "rgba(73, 73, 73, 1)", border: "none", cursor: "pointer", color: "white", borderRadius: 3, display: "flex", alignItems: "center", justifyContent: "center", height: 35 }}
<Button style={{ border: "none", cursor: "pointer", borderRadius: 3, display: "flex", alignItems: "center", justifyContent: "center", height: 35 }}
onClick={(event) => {
event.preventDefault();
event.stopPropagation();
@@ -211,11 +213,13 @@ const AppCard = ({ data, index, mouseHoverIndex, setMouseHoverIndex, globalUrl,
navigate(editUrl)
}
}}
variant="contained"
color="secondary"
>
<EditIcon />
</button>
</Button>
) : (
<button style={{ backgroundColor: "rgba(73, 73, 73, 1)", border: "none", cursor: "pointer", color: "white", borderRadius: 3, display: "flex", alignItems: "center", justifyContent: "center", height: 35 }}
<Button variant="contained" color="secondary" style={{ border: "none", cursor: "pointer", color: "white", borderRadius: 3, display: "flex", alignItems: "center", justifyContent: "center", height: 35 }}
onClick={(event) => {
event.preventDefault();
event.stopPropagation();
@@ -224,26 +228,23 @@ const AppCard = ({ data, index, mouseHoverIndex, setMouseHoverIndex, globalUrl,
}}
>
<ForkRightIcon />
</button>
</Button>
)
}
<Button
disabled={data?.reference_org === userdata?.active_org?.id}
disabled={data?.reference_org === userdata?.active_org?.id}
variant="contained"
color="secondary"
className="deactivate-button"
sx={{
width: 110,
height: 35,
borderRadius: 0.75,
bgcolor: "rgba(73, 73, 73, 1)",
color: "rgba(241, 241, 241, 1)",
textTransform: "none",
fontSize: 16,
fontFamily: theme?.typography?.fontFamily,
transition: "background-color 0.3s ease",
"&:hover": {
bgcolor: "rgba(93, 93, 93, 1)",
},
}}
onMouseDown={(e) => e.stopPropagation()}
onMouseUp={(e) => e.stopPropagation()}
@@ -308,6 +309,8 @@ const Hits = ({
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
const [deactivatedIndexes, setDeactivatedIndexes] = React.useState([]);
const [isLoading, setIsLoading] = useState(false)
const { themeMode } = useContext(Context);
const theme = getTheme(themeMode);
useEffect(() => {
var baseurl = globalUrl;
@@ -507,7 +510,7 @@ const Hits = ({
overflow: "hidden",
display: "flex",
width: '100%',
backgroundColor: hoverEffect === index ? "#2F2F2F" : "#212121",
backgroundColor: hoverEffect === index ? theme.palette.hoverColor : theme.palette.platformColor,
fontFamily: theme?.typography?.fontFamily
}}
onClick={() => {
@@ -550,7 +553,7 @@ const Hits = ({
maxWidth: "90%",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
color: '#F1F1F1'
color: theme.palette.text.primary
}}
>
{(allActivatedAppIds && allActivatedAppIds.includes(data.objectID)) && <Box sx={{ width: 8, height: 8, backgroundColor: "#02CB70", borderRadius: '50%' }} />}
@@ -558,7 +561,7 @@ const Hits = ({
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
color: '#F1F1F1'
color: theme.palette.text.primary,
}}>
{normalizedString(data.name)}
</div>
@@ -569,7 +572,7 @@ const Hits = ({
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
color: "rgba(158, 158, 158, 1)",
color: theme.palette.text.secondary,
marginTop: 5,
}}
>
@@ -600,12 +603,12 @@ const Hits = ({
componentsProps={{
tooltip: {
sx: {
backgroundColor: "rgba(33, 33, 33, 1)",
color: "rgba(241, 241, 241, 1)",
backgroundColor: theme.palette.tooltip.backgroundColor,
color: theme.palette.tooltip.color,
width: "auto",
height: "auto",
fontSize: 16,
border: "1px solid rgba(73, 73, 73, 1)",
border: theme.palette.tooltip.border,
}
}
}}
@@ -650,15 +653,15 @@ const Hits = ({
<div>
{allActivatedAppIds && allActivatedAppIds?.includes(data.objectID) ? (
<Button
style={{
width: 110,
height: 35,
borderRadius: 4,
backgroundColor: "rgba(73, 73, 73, 1)",
color: "rgba(241, 241, 241, 1)",
variant="contained"
color="secondary"
sx={{
width: "110px",
height: "35px",
borderRadius: "4px",
textTransform: "none",
fontFamily: theme?.typography?.fontFamily,
fontSize: 16,
fontSize: "16px",
}}
onMouseDown={(e) => e.stopPropagation()}
onMouseUp={(e) => e.stopPropagation()}
@@ -671,9 +674,9 @@ const Hits = ({
) : (
<Button
variant="contained"
color="primary"
style={{
backgroundColor: "#FF8544",
color: "black",
width: 102,
height: 35,
borderRadius: 4,
@@ -719,6 +722,8 @@ const SearchBox = ({ refine, searchQuery, setSearchQuery }) => {
const inputRef = useRef(null);
const [localQuery, setLocalQuery] = useState(searchQuery);
const location = useLocation();
const { themeMode } = useContext(Context);
const theme = getTheme(themeMode);
// Initialize search when component mounts or when switching to Discover tab
useEffect(() => {
if (searchQuery) {
@@ -779,7 +784,8 @@ const SearchBox = ({ refine, searchQuery, setSearchQuery }) => {
InputProps={{
style: {
borderRadius: 4,
height: 45
height: 45,
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
},
endAdornment: (
<InputAdornment position="end">
@@ -809,6 +815,8 @@ const CustomHits = connectHits(Hits);
// Custom Category Dropdown Component
const CategoryDropdown = ({ items, currentRefinement, refine }) => {
const { themeMode } = useContext(Context);
const theme = getTheme(themeMode);
const handleChange = (event) => {
const value = event.target.value;
refine(value);
@@ -823,7 +831,7 @@ const CategoryDropdown = ({ items, currentRefinement, refine }) => {
onChange={handleChange}
displayEmpty
multiple
style={{ borderRadius: 4, height: 45, fontFamily: theme?.typography?.fontFamily, flex: 1 }}
style={{ borderRadius: 4, height: 45, fontFamily: theme?.typography?.fontFamily, flex: 1, backgroundColor: theme.palette.textFieldStyle.backgroundColor, color: theme.palette.textFieldStyle.color }}
renderValue={(selected) => {
if (selected.length === 0) return 'All Categories';
return (
@@ -875,6 +883,9 @@ const LabelDropdown = ({ items, currentRefinement, refine }) => {
refine(value);
};
const { themeMode } = useContext(Context);
const theme = getTheme(themeMode);
return (
<div style={{ position: 'relative', width: '100%' }}>
<Select
@@ -884,7 +895,7 @@ const LabelDropdown = ({ items, currentRefinement, refine }) => {
onChange={handleChange}
displayEmpty
multiple
style={{ borderRadius: 4, height: 45, fontFamily: theme?.typography?.fontFamily, flex: 1 }}
style={{ borderRadius: 4, height: 45, fontFamily: theme?.typography?.fontFamily, flex: 1, backgroundColor: theme.palette.textFieldStyle.backgroundColor, color: theme.palette.textFieldStyle.color }}
renderValue={(selected) => {
if (selected.length === 0) return 'All Labels';
return (
@@ -930,6 +941,7 @@ const LabelDropdown = ({ items, currentRefinement, refine }) => {
</div>
);
};
const CustomLabelDropdown = connectRefinementList(LabelDropdown);
@@ -970,6 +982,8 @@ const filterApps = (apps, searchQuery, selectedCategory, selectedLabel) => {
const LoginPrompt = () => {
const navigate = useNavigate();
const { themeMode } = useContext(Context);
const theme = getTheme(themeMode);
return (
<div style={{
display: "flex",
@@ -1006,9 +1020,11 @@ const LoginPrompt = () => {
// Add this new component for the app skeleton
const AppSkeleton = () => {
const { themeMode } = useContext(Context);
const theme = getTheme(themeMode);
return (
<Paper elevation={0} style={{
backgroundColor: "#212121",
backgroundColor: theme.palette.platformColor,
width: "100%",
height: 120,
borderRadius: 4,
@@ -1025,7 +1041,7 @@ const AppSkeleton = () => {
height={90}
style={{
borderRadius: 4,
backgroundColor: "rgba(255, 255, 255, 0.1)"
backgroundColor: theme.palette.loaderColor
}}
/>
<div style={{
@@ -1039,19 +1055,19 @@ const AppSkeleton = () => {
variant="text"
width="40%"
height={24}
style={{ backgroundColor: "rgba(255, 255, 255, 0.1)" }}
style={{ backgroundColor: theme.palette.loaderColor }}
/>
<Skeleton
variant="text"
width="60%"
height={20}
style={{ backgroundColor: "rgba(255, 255, 255, 0.1)" }}
style={{ backgroundColor: theme.palette.loaderColor }}
/>
<Skeleton
variant="text"
width="30%"
height={20}
style={{ backgroundColor: "rgba(255, 255, 255, 0.1)" }}
style={{ backgroundColor: theme.palette.loaderColor }}
/>
</div>
</div>
@@ -1117,6 +1133,9 @@ const Apps2 = (props) => {
const [validation, setValidation] = useState(null);
const [createAppModalOpen, setCreateAppModalOpen] = useState(false);
const {themeMode, brandColor} = useContext(Context);
const theme = getTheme(themeMode, brandColor);
const baseRepository = "https://github.com/frikky/shuffle-apps";
const isCloud =
@@ -1373,7 +1392,6 @@ const Apps2 = (props) => {
// setSelectedAction({});
// }
}
if (privateapps.length > 0 && storageApps.length === 0) {
try {
localStorage.setItem("apps", JSON.stringify(privateapps))
@@ -1869,13 +1887,13 @@ const Apps2 = (props) => {
}
const tabActive = {
borderBottom: "5px solid #FF8544",
borderBottom: `5px solid ${theme.palette.primary.main}`,
borderRadius: "2px",
color: "#FF8544"
color: theme.palette.primary.main,
}
return (
<div style={{ paddingTop: 70, paddingLeft: leftSideBarOpenByClick ? 200 : 0, transition: "padding-left 0.3s ease", backgroundColor: "#1A1A1A", fontFamily: theme?.typography?.fontFamily, zoom: 0.7, }}>
<div style={{ paddingTop: 70, paddingLeft: leftSideBarOpenByClick ? 200 : 0, transition: "padding-left 0.3s ease", backgroundColor: theme.palette.backgroundColor, fontFamily: theme?.typography?.fontFamily, zoom: 0.7, }}>
<InstantSearch searchClient={searchClient} indexName="appsearch">
<AppModal
open={openModal}
@@ -1984,7 +2002,7 @@ const Apps2 = (props) => {
</span>
)}
</div>
<div style={{ borderBottom: '1px solid gray', marginBottom: 30 }}>
<div style={{ borderBottom: themeMode === "dark" ? "1px solid #808080" : theme.palette.defaultBorder, marginBottom: 30 }}>
<Tabs
value={currTab}
onChange={(event, newTab) => handleTabChange(event, newTab)}
@@ -2034,6 +2052,10 @@ const Apps2 = (props) => {
value={searchQuery}
id="shuffle_search_field"
onChange={handleSearchChange}
style={{
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
color: theme.palette.textFieldStyle.color,
}}
onKeyDown={(event) => {
if (event.key === "Enter") {
event.preventDefault();
@@ -2043,7 +2065,10 @@ const Apps2 = (props) => {
InputProps={{
style: {
borderRadius: 4,
height: 45
height: 45,
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
color: theme.palette.textFieldStyle.color,
fontSize: 18,
},
endAdornment: (
<InputAdornment position="end">
@@ -2087,7 +2112,9 @@ const Apps2 = (props) => {
style={{
borderRadius: 4,
height: 45,
fontFamily: theme?.typography?.fontFamily
fontFamily: theme?.typography?.fontFamily,
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
fontSize: 18,
}}
renderValue={(selected) => {
if (selected.length === 0) return 'All Categories';
@@ -2152,7 +2179,9 @@ const Apps2 = (props) => {
style={{
borderRadius: 4,
height: 45,
fontFamily: theme?.typography?.fontFamily
fontFamily: theme?.typography?.fontFamily,
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
fontSize: 18,
}}
renderValue={(selected) => {
if (selected.length === 0) return 'All Labels';
@@ -2211,13 +2240,11 @@ const Apps2 = (props) => {
width: '100%',
borderRadius: '4px',
textTransform: 'none',
backgroundColor: "#FF8544",
color: "#1A1A1A",
fontFamily: theme?.typography?.fontFamily,
fontSize: 16,
fontWeight: 500
}}
startIcon={<AddIcon style={{ color: "#1A1A1A" }} />}
startIcon={<AddIcon />}
>
Create an App
</Button>
+89 -27
View File
@@ -1,7 +1,7 @@
import React, { useEffect, useLayoutEffect, useRef, useState, useContext, memo } from "react"
import React, { useEffect, useLayoutEffect, useRef, useState, useContext, memo, } from "react"
import { toast } from 'react-toastify';
import Markdown from 'react-markdown'
import theme from '../theme.jsx';
import {getTheme} from '../theme.jsx';
import ReactJson from "react-json-view-ssr";
import { isMobile } from "react-device-detect";
import { BrowserView, MobileView } from "react-device-detect";
@@ -28,7 +28,8 @@ import {
DialogTitle,
Box,
DialogContent,
InputAdornment
InputAdornment,
CircularProgress
} from "@mui/material";
import {
Link as LinkIcon,
@@ -171,6 +172,9 @@ export const Img = (props) => {
var height = "auto"
var width = isArticlePage ? 1000 : isFormPage ? 400: 750
const { themeMode } = useContext(Context)
const theme = getTheme(themeMode)
const docsImageStyle = {
border: isFormPage ? null : "1px solid rgba(255,255,255,0.3)",
borderRadius: theme.palette?.borderRadius,
@@ -215,6 +219,8 @@ export const CodeHandler = (props) => {
const propvalue = props.value !== undefined && props.value !== null ? props.value : props.children !== undefined && props.children !== null && props.children.length > 0 ? props.children[0] : ""
const validate = validateJson(propvalue)
const {themeMode } = useContext(Context)
const theme = getTheme(themeMode)
var newprop = propvalue
if (validate.valid === false) {
@@ -293,6 +299,7 @@ const Docs = (defaultprops) => {
let navigate = useNavigate();
const location = useLocation();
const pathname = location.pathname
// Quickfix for react router 5 -> 6
const params = useParams();
//var props = JSON.parse(JSON.stringify(defaultprops))
@@ -301,6 +308,8 @@ const Docs = (defaultprops) => {
props.match.params = params
//console.log("PARAMS: ", params)
const { themeMode } = useContext(Context)
const theme = getTheme(themeMode)
const [mobile, setMobile] = useState(serverMobile === true || isMobile === true ? true : false);
const [data, setData] = useState("");
@@ -326,9 +335,37 @@ const Docs = (defaultprops) => {
const [sidebarOpen, setSidebarOpen] = useState(false);
const [activeSubItem, setActiveSubItem] = useState(false);
const headingElementsRef = useRef({})
const [hasRedirected, setHasRedirected] = useState(false)
var isArticlePage = window.location.pathname.includes("/articles/") || window.location.pathname === "/articles" ? true : false;
const searchFieldRef = useRef(null);
const handleDocRedirectForPartners = () => {
if (hasRedirected) return;
if (
userdata &&
userdata?.org_status?.includes("integration_partner") &&
userdata?.active_org?.branding?.documentation_link?.length > 0
) {
const docLink = userdata?.active_org?.branding?.documentation_link;
if (docLink && docLink !== "") {
setHasRedirected(true);
if (docLink.startsWith('http')) {
window.location.replace(docLink);
} else {
navigate(docLink);
}
}
}
};
useEffect(() => {
if (isLoggedIn && isLoaded) {
handleDocRedirectForPartners()
}
}, [isLoggedIn, isLoaded]);
useEffect(() => {
fetchDocList();
@@ -353,6 +390,8 @@ const Docs = (defaultprops) => {
navigate('/docs/apps#app-creation-introduction')
}
}
}, [location]);
useEffect(() => {
@@ -501,19 +540,25 @@ const Docs = (defaultprops) => {
}
}}
PaperProps={{
style: {
color: "white",
minWidth: 750,
sx: {
color: theme.palette.DialogStyle.color,
minWidth: "750px",
minHeight: "180px",
maxHeight: "85vh",
borderRadius: 16,
borderRadius: theme.palette.DialogStyle.borderRadius,
border: "1px solid var(--Container-Stroke, #494949)",
background: "var(--Container, #000000)",
background: theme.palette.DialogStyle.backgroundColor,
boxShadow: "0px 16px 24px 8px rgba(0, 0, 0, 0.25)",
position: "fixed",
top: "70px",
left: "50%",
transform: "translateX(-50%)",
'& .MuiDialogContent-root': {
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
},
'& .MuiDialogTitle-root': {
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
},
},
}}
sx={{
@@ -526,7 +571,7 @@ const Docs = (defaultprops) => {
<Box sx={{ display: "flex", justifyContent: "space-between", alignItems: "center", px: 2, pr: 3, pt: 2 }}>
<DialogTitle
sx={{
color: "var(--Paragraph-text, #C8C8C8)",
color: theme.palette.DialogStyle.color,
p: 0,
m: 0,
ml: 1.5,
@@ -594,7 +639,7 @@ const Docs = (defaultprops) => {
}
const SidebarPaperStyle = {
backgroundColor: isArticlePage ? "transparent" : "rgb(26,26,26)",
backgroundColor: isArticlePage ? "transparent" : theme.palette.backgroundColor,
border: isArticlePage ? "none" : undefined,
borderRadius: isArticlePage ? "none" : undefined,
boxShadow: isArticlePage ? "none" : undefined,
@@ -663,9 +708,9 @@ const Docs = (defaultprops) => {
extraInfo = (
<div
style={{
backgroundColor: theme.palette.inputColor,
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
padding: 15,
borderRadius: theme.palette?.borderRadius,
borderRadius: theme.palette?.textFieldStyle.borderRadius,
marginBottom: isArticlePage ? 25 : 30,
display: "flex",
}}
@@ -679,7 +724,7 @@ const Docs = (defaultprops) => {
href={selectedMeta.link}
style={{ textDecoration: "none", color: "#f85a3e" }}
>
<Button style={{ color: "white", }} variant="outlined" color="secondary">
<Button variant="outlined" color="secondary">
<EditIcon /> &nbsp;&nbsp;Edit
</Button>
</a>
@@ -788,7 +833,7 @@ const Docs = (defaultprops) => {
paddingLeft: "0.3em", rotate: "-30deg",
paddingTop: "0.9em", display: props.level === 1 ? "none" : "block",
}}>
<LinkIcon />
<LinkIcon style={{color: theme.palette.textColor}} />
</Link>
</div>
{isArticlePage ? (userdata?.support ? extraInfo : "") : extraInfo}
@@ -1028,13 +1073,12 @@ const Docs = (defaultprops) => {
}
const markdownStyle = {
color: "rgba(255, 255, 255, 0.90)",
color: theme.palette.textColor,
overflow: "hidden",
paddingBottom: 100,
margin: "auto",
maxWidth: "100%",
minWidth: "100%",
overflow: "hidden",
fontSize: isMobile ? "1.3rem" : "1.1rem",
};
@@ -1188,6 +1232,7 @@ const Docs = (defaultprops) => {
const activeHrefStyleToc2 = {
...hrefStyleToc2,
color: "#f86a3e",
fontFamily: theme.typography.fontFamily,
};
const activeListItemStyle = {
@@ -1228,9 +1273,10 @@ const Docs = (defaultprops) => {
</InputAdornment>
),
style: {
backgroundColor: "#212121",
backgroundColor: theme.palette.platformColor,
borderRadius: 4,
color: "white",
fontSize: 18,
color: theme.palette.textColor,
},
}}
style={{
@@ -1290,16 +1336,17 @@ const Docs = (defaultprops) => {
>
<ListItemText
style={{
color: itemMatching ? "#f86a3e" : "inherit",
color: itemMatching ? "#f86a3e" : theme.palette.textColor,
flex: 1,
}}
primary={
<Typography
variant="body1"
style={{
whiteSpace: "nowrap",
overflow: "hidden",
color : "inherit",
textOverflow: "ellipsis",
fontSize: "18px",
}}
>
{newname}
@@ -1371,7 +1418,7 @@ const Docs = (defaultprops) => {
)}
{tocLines.length > 0 ?
(
<h4 style={{ fontWeight: 600, margin: 0, fontSize: "16px", marginBottom: "8px" }}>Table Of Content</h4>
<h4 style={{ fontWeight: 600, margin: 0, fontSize: "16px", marginBottom: "8px", color: theme.palette.text.primary, }}>Table Of Content</h4>
) : null}
<div
@@ -1393,16 +1440,17 @@ const Docs = (defaultprops) => {
paddingLeft: isArticlePage ? "0" : "8px",
paddingRight: isArticlePage ? "0" : "8px",
lineHeight: "20px",
color: activeId === data.id ? "#f86a3e" : "inherit",
}}
onClick={(e) => {
handleCollapse(index)
setActiveId(data.id)
}}
>
{data.title}
<Typography style={{ fontSize: 14, fontWeight: 400, color: activeId === data.id ? "#f86a3e" : theme.palette.textColor, }}>
{data.title}
</Typography>
{data.items.length > 0 ? (
<>{isopen == index ? <ExpandMoreIcon /> : <KeyboardArrowRightIcon />}</>
<>{isopen === index ? <ExpandMoreIcon style={{color: theme.palette.text.secondary}} /> : <KeyboardArrowRightIcon style={{color: theme.palette.text.secondary}} />}</>
) : null}
</ListItemButton>
{
@@ -1414,7 +1462,7 @@ const Docs = (defaultprops) => {
return (
<ListItemButton
key={i}
style={activeSubItem === d.id ? activeHrefStyleToc2 : hrefStyleToc2}
style={activeSubItem === d.id ? activeHrefStyleToc2 : {...hrefStyleToc2, color: theme.palette.text.secondary, fontFamily: theme.typography.fontFamily,}}
href={`#${d.id}`}
onClick={(e) => {
// e.preventDefault()
@@ -1577,7 +1625,7 @@ const Docs = (defaultprops) => {
// Padding and zIndex etc set because of footer in cloud.
const loadedCheck = (
<DocsWrapper isLoggedIn={isLoggedIn} isLoaded={isLoaded}>
<DocsWrapper isLoggedIn={isLoggedIn} isLoaded={isLoaded} userdata={userdata}>
<DocsContent postDataBrowser={postDataBrowser} postDataMobile={postDataMobile}/>
</DocsWrapper>
);
@@ -1589,6 +1637,8 @@ return <div>{loadedCheck}</div>;
export default Docs;
const DocsContent = memo(({postDataBrowser, postDataMobile}) => {
const { themeMode } = useContext(Context);
const theme = getTheme(themeMode);
return(
<div style={{fontFamily: theme?.palette?.fontFamily}}>
<BrowserView>{postDataBrowser}</BrowserView>
@@ -1596,10 +1646,22 @@ const DocsContent = memo(({postDataBrowser, postDataMobile}) => {
</div>
)})
const DocsWrapper = memo(({isLoggedIn, isLoaded, children })=>{
const DocsWrapper = memo(({isLoggedIn, isLoaded, children, userdata })=>{
const { leftSideBarOpenByClick, windowWidth } = useContext(Context);
useEffect(() => {
if (isLoaded && isLoggedIn && userdata?.org_status?.includes("integration_partner") && userdata?.active_org?.branding.documentation_link?.length > 0) {
window.location.href = userdata?.active_org?.branding.documentation_link;
}
}, [isLoaded, isLoggedIn, userdata]);
if (isLoaded && isLoggedIn && userdata?.org_status?.includes("integration_partner") && userdata?.active_org?.branding.documentation_link?.length > 0) {
return <CircularProgress style={{position: "absolute", top: "50%", left: "50%", transform: "translate(-50%, -50%)"}} />;
}
return (
<div style={{
marginTop: window.location.pathname.includes("/articles/") ? 50 : undefined,
+1 -1
View File
@@ -297,7 +297,7 @@ const LoginPage = props => {
const [showPassword, setShowPassword] = useState(false)
const [ssoUrl, setSSOUrl] = useState("");
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io" || window.location.host === "migration.shuffler.io";
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io" || window.location.host === "migration.shuffler.io" || window.location.host === "sandbox.shuffler.io";
const parsedsearch = serverside === true ? "" : window.location.search
useEffect(() => {
+5 -3
View File
@@ -1,5 +1,5 @@
/* eslint-disable react/no-multi-comp */
import React, {useState, useEffect} from 'react';
import React, {useState, useEffect, useContext} from 'react';
import ReactDOM from "react-dom"
import ReactJson from "react-json-view-ssr";
@@ -48,6 +48,7 @@ import {
Edit as EditIcon,
Polyline as PolylineIcon,
} from '@mui/icons-material';
import { Context } from '../context/ContextApi.jsx';
const hrefStyle = {
color: "white",
@@ -58,6 +59,7 @@ const hrefStyle = {
const RunWorkflow = (defaultprops) => {
const { globalUrl, userdata, isLoaded, isLoggedIn, setIsLoggedIn, setCookie, register, serverside } = defaultprops;
const { supportEmail } = useContext(Context);
let navigate = useNavigate();
const [_, setUpdate] = useState(""); // Used to force rendring, don't remove
const [explorerUi, setExplorerUi] = useState(false)
@@ -471,7 +473,7 @@ const RunWorkflow = (defaultprops) => {
}
if (response.status === 401 || response.status === 403) {
toast("This Form is not available for you to run. If you this is an error, contact support@shuffler.io with a link to this form")
toast(`This Form is not available for you to run. If you this is an error, contact ${supportEmail} with a link to this form`)
}
return response.json()
@@ -627,7 +629,7 @@ const RunWorkflow = (defaultprops) => {
}
if (response.status === 401 || response.status === 403) {
toast("This Form is not available to you. If you think this is an error, please contact support@shuffler.io with the URL.")
toast(`This Form is not available to you. If you think this is an error, please contact ${supportEmail} with the URL.`)
}
return response.json()
+4 -3
View File
@@ -1,13 +1,14 @@
import React, { useState } from "react";
import React, { useContext, useState } from "react";
import { Typography, CircularProgress } from "@mui/material";
import theme from '../theme.jsx';
import { red, } from "../views/AngularWorkflow.jsx"
import { Context } from "../context/ContextApi.jsx";
const SetAuthentication = (props) => {
const { globalUrl } = props;
const { supportEmail } = useContext(Context);
var headers = {
"Content-Type": "application/json",
"Accept": "application/json",
@@ -328,7 +329,7 @@ const SetAuthentication = (props) => {
<b>{failed ? "Failed auth. Error: " : ""}</b> {response}
<br/>
<br/>
{failed ? "If the error persists, try to use fewer scopes. Contact support@shuffler.io if you need further assistance, and include the current URL and a screenshot. You may now close this window." : ""}
{failed ? `If the error persists, try to use fewer scopes. Contact ${supportEmail} if you need further assistance, and include the current URL and a screenshot. You may now close this window.` : ""}
</Typography>
</div>
);
+26 -23
View File
@@ -1,7 +1,7 @@
import React, { useState, useEffect } from "react";
import React, { useState, useEffect, useContext } from "react";
import { useNavigate } from "react-router-dom";
import theme from '../theme.jsx';
import {getTheme} from '../theme.jsx';
import {
Grid,
Typography,
@@ -15,6 +15,7 @@ import {
//import { useAlert
import { ToastContainer, toast } from "react-toastify";
import "../codeeditor-index.css";
import { Context } from "../context/ContextApi.jsx";
import { FileCopy, Visibility, VisibilityOff } from "@mui/icons-material";
import IconButton from "@mui/material/IconButton";
@@ -40,6 +41,8 @@ const Settings = (props) => {
const [MFARequired, setMFARequired] = React.useState(false);
const [image2FA, setImage2FA] = React.useState("");
const [value2FA, setValue2FA] = React.useState("");
const {themeMode, supportEmail} = useContext(Context);
const theme = getTheme(themeMode);
// const [file, setFile] = React.useState("");
// const [fileBase64, setFileBase64] = React.useState(
@@ -83,7 +86,7 @@ const Settings = (props) => {
const boxStyle = {
flex: "1",
color: "white",
color: theme.palette.text.primary,
position: "relative",
marginLeft: "10px",
marginRight: "10px",
@@ -213,8 +216,8 @@ const Settings = (props) => {
left: "50%",
transform: "translate(-50%, -50%)",
zIndex: "9999",
backgroundColor: "#1a1a1a",
color: "white",
backgroundColor: theme.palette.backgroundColor,
color: theme.palette.text.primary,
padding: 20,
borderRadius: 5,
boxShadow: "0 0 10px rgba(0, 0, 0, 0.3)",
@@ -223,7 +226,7 @@ const Settings = (props) => {
};
const closeIconButtonStyling = {
color: "white",
color: theme.palette.text.primary,
border: "none",
backgroundColor: "transparent",
marginLeft: "90%",
@@ -243,7 +246,7 @@ const Settings = (props) => {
width: "100%",
fontSize: 16,
backgroundColor: disabled ? "gray" : "red",
color: "white",
color: theme.palette.text.primary,
cursor: disabled === false && "pointer",
};
const checkboxStyle = {
@@ -381,7 +384,7 @@ const Settings = (props) => {
className="ais-RefinementList-checkbox"
onClick={handleCheckBoxEvent}
/>
<label style={{ fontSize: "16px", color: "white" }}>
<label style={{ fontSize: "16px", color: theme.palette.text.primary }}>
I have read the above information and I agree to it completely
</label>
</div>
@@ -410,7 +413,7 @@ const Settings = (props) => {
endAdornment: (
<IconButton
onClick={handlePasswordVisibility}
style={{color:"white"}}
style={{color:theme.palette.text.primary}}
>
{showPassword ? <VisibilityOff /> : <Visibility />}
</IconButton>
@@ -704,7 +707,7 @@ const Settings = (props) => {
InputProps={{
style: {
height: "50px",
color: "white",
color: theme.palette.text.primary,
},
}}
color="primary"
@@ -732,7 +735,7 @@ const Settings = (props) => {
InputProps={{
style: {
height: "50px",
color: "white",
color: theme.palette.text.primary,
},
}}
color="primary"
@@ -757,7 +760,7 @@ const Settings = (props) => {
InputProps={{
style: {
height: "50px",
color: "white",
color: theme.palette.text.primary,
},
}}
color="primary"
@@ -788,7 +791,7 @@ const Settings = (props) => {
InputProps={{
style: {
height: "50px",
color: "white",
color: theme.palette.text.primary,
},
}}
color="primary"
@@ -806,7 +809,7 @@ const Settings = (props) => {
InputProps={{
style: {
height: "50px",
color: "white",
color: theme.palette.text.primary,
},
endAdornment: (
<>
@@ -857,7 +860,7 @@ const Settings = (props) => {
InputProps={{
style:{
height: "50px",
color: "white",
color: theme.palette.text.primary,
},
}}
color="primary"
@@ -877,7 +880,7 @@ const Settings = (props) => {
InputProps={{
style:{
height: "50px",
color: "white",
color: theme.palette.text.primary,
},
}}
color="primary"
@@ -899,7 +902,7 @@ const Settings = (props) => {
InputProps={{
style:{
height: "50px",
color: "white",
color: theme.palette.text.primary,
},
}}
color="primary"
@@ -919,7 +922,7 @@ const Settings = (props) => {
InputProps={{
style:{
height: "50px",
color: "white",
color: theme.palette.text.primary,
},
}}
color="primary"
@@ -954,7 +957,7 @@ const Settings = (props) => {
InputProps={{
style: {
height: "50px",
color: "white",
color: theme.palette.text.primary,
},
}}
color="primary"
@@ -979,7 +982,7 @@ const Settings = (props) => {
InputProps={{
style: {
height: "50px",
color: "white",
color: theme.palette.text.primary,
},
}}
color="primary"
@@ -1002,7 +1005,7 @@ const Settings = (props) => {
InputProps={{
style: {
height: "50px",
color: "white",
color: theme.palette.text.primary,
},
}}
color="primary"
@@ -1046,7 +1049,7 @@ const Settings = (props) => {
{isCloud ?
<span>
<Typography variant="body1" color="textSecondary">
By <a href="/creators" target="_blank" style={{ textDecoration: "none", color: "#f86a3e"}}>joining the Creator Incentive Program</a> and connecting your Github account, 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 non-sensitive data will be turned into a <a target="_blank" style={{ textDecoration: "none", color: "#f86a3e"}} href="https://shuffler.io/creators">creator account</a>. This enables you to earn a passive income from Shuffle. This IS reversible. Support: support@shuffler.io
By <a href="/creators" target="_blank" style={{ textDecoration: "none", color: "#f86a3e"}}>joining the Creator Incentive Program</a> and connecting your Github account, 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 non-sensitive data will be turned into a <a target="_blank" style={{ textDecoration: "none", color: "#f86a3e"}} href="https://shuffler.io/creators">creator account</a>. This enables you to earn a passive income from Shuffle. This IS reversible. Support: {supportEmail}
</Typography>
<Button
style={{ height: 40, marginTop: 10 }}
@@ -1133,7 +1136,7 @@ const Settings = (props) => {
height: "60px",
marginTop: "10px",
backgroundColor: "#d52b2b",
color: "white",
color: theme.palette.text.primary,
}}
// variant="contained"
// color="primary"
+4 -3
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 {
@@ -15,10 +15,11 @@ import { ToastContainer, toast } from "react-toastify"
import AuthenticationOauth2 from "../components/Oauth2Auth.jsx";
import AuthenticationWindow from "../components/AuthenticationWindow.jsx";
import { base64_decode, appCategories } from "../views/AppCreator.jsx";
import { Context } from "../context/ContextApi.jsx";
const SetAuthentication = (props) => {
const { globalUrl, serverside } = props;
const { supportEmail } = useContext(Context);
const [app, setApp] = useState({});
const [isAppLoaded, setIsAppLoaded] = useState(false);
const [loadFail, setLoadFail] = useState("");
@@ -114,7 +115,7 @@ const SetAuthentication = (props) => {
setLoadFail(
<span>
<Typography variant="h4">
Failed to load the app. Please contact your provider or support@shuffler.io if this persists
Failed to load the app. Please contact your provider or {supportEmail} if this persists
</Typography>
<Button
variant="contained"
+17 -13
View File
@@ -9,7 +9,7 @@ import { Context } from "../context/ContextApi.jsx"
import { ToastContainer, toast } from "react-toastify"
import { makeStyles, } from "@mui/styles"
import classNames from "classnames"
import theme from '../theme.jsx'
import {getTheme} from '../theme.jsx'
import {
Autocomplete,
@@ -123,7 +123,8 @@ const ParseUsecaseDesc = (priority, appFramework) => {
const UsecaseListComponent = (props) => {
const { keys, userdata, isCloud, globalUrl, frameworkData, isLoggedIn, workflows, setWorkflows, getFramework, setFrameworkData, } = props
const { themeMode, brandName } = useContext(Context)
const theme = getTheme(themeMode)
const [expandedIndex, setExpandedIndex] = useState(-1);
const [expandedItem, setExpandedItem] = useState(-1);
@@ -199,8 +200,8 @@ const UsecaseListComponent = (props) => {
const LoadingSkeleton = () => (
<div style={{paddingTop: 75, minHeight: 1000, textAlign: "left"}}>
{/* Header skeleton */}
<Skeleton variant="text" width={200} height={40} sx={{ bgcolor: 'grey.800' }} />
<Skeleton variant="text" width="60%" height={24} sx={{ marginTop: 3, bgcolor: 'grey.800' }} />
<Skeleton variant="text" width={200} height={40} />
<Skeleton variant="text" width="60%" height={24} sx={{ marginTop: 3, }} />
{/* Apps selection skeleton */}
<Skeleton variant="text" width={150} height={24} sx={{ marginTop: 5, marginBottom: 10 }} />
@@ -220,7 +221,7 @@ const UsecaseListComponent = (props) => {
variant="circular"
width={40}
height={40}
sx={{ bgcolor: 'grey.800' }}
sx={{ bgcolor: theme.palette.loaderColor}}
/>
))}
</div>
@@ -232,7 +233,7 @@ const UsecaseListComponent = (props) => {
sx={{
marginTop: "10px",
borderRadius: 20,
bgcolor: 'grey.800'
bgcolor: theme.palette.loaderColor
}}
/>
</Paper>
@@ -275,13 +276,13 @@ const UsecaseListComponent = (props) => {
variant="circular"
width={30}
height={30}
sx={{ bgcolor: 'grey.800' }}
sx={{ bgcolor: theme.palette.loaderColor}}
/>
<Skeleton
variant="circular"
width={30}
height={30}
sx={{ bgcolor: 'grey.800' }}
sx={{ bgcolor: theme.palette.loaderColor }}
/>
</div>
{/* Usecase title */}
@@ -289,7 +290,7 @@ const UsecaseListComponent = (props) => {
variant="text"
width="70%"
height={24}
sx={{ bgcolor: 'grey.800' }}
sx={{ bgcolor: theme.palette.loaderColor}}
/>
</Paper>
</Grid>
@@ -596,10 +597,10 @@ const UsecaseListComponent = (props) => {
return (
<div style={{paddingTop: 75, minHeight: 1000, textAlign: "left",}}>
<Typography variant="h4" style={{color: "white", }}>
<b>Usecases</b>
<Typography variant="h4" color="textPrimary" style={{fontWeight: "bold"}}>
Usecases
</Typography>
<Typography variant="body1" style={{marginTop: 25, }}>
<Typography variant="body1" color="textPrimary" style={{marginTop: 25, }}>
Choose a template tailored to your automation requirements, ready for immediate use.
</Typography>
@@ -887,6 +888,9 @@ const Usecases2 = (props) => {
const [keys, setKeys] = useState([])
const [treeKeys, setTreeKeys] = useState([])
const { themeMode, brandName } = useContext(Context)
const theme = getTheme(themeMode)
const [selectedUsecaseCategory, setSelectedUsecaseCategory] = useState("");
const [selectedUsecases, setSelectedUsecases] = useState([]);
const [usecases, setUsecases] = useState([])
@@ -987,7 +991,7 @@ const Usecases2 = (props) => {
}
document.title = "Shuffle - usecases";
document.title = brandName?.length > 0 ? `${brandName} - usecases` : "Shuffle - usecases";
var dayGraphLabels = [60, 80, 65, 130, 80, 105, 90, 130, 70, 115, 60, 130];
var dayGraphData = [60, 80, 65, 130, 80, 105, 90, 130, 70, 115, 60, 130];
+110
View File
@@ -440,6 +440,10 @@ export const collapseField = (field, inputdata) => {
return true
}
if (field.name === "result") {
return false
}
if (field.type === "array") {
return true
}
@@ -454,6 +458,112 @@ export const collapseField = (field, inputdata) => {
return false
}
export const HandleJsonCopy = (base, copy, base_node_name) => {
if (typeof copy.name === "string") {
copy.name = copy.name.replaceAll(" ", "_");
}
//lol
if (typeof base === 'object' || typeof base === 'dict') {
base = JSON.stringify(base)
}
if (base_node_name === "execution_argument" || base_node_name === "Runtime Argument") {
base_node_name = "exec"
}
//console.log("COPY: ", base_node_name, copy);
//var newitem = JSON.parse(base);
var newitem = validateJson(base).result
var to_be_copied = "$" + base_node_name.toLowerCase().replaceAll(" ", "_");
for (let copykey in copy.namespace) {
if (copy.namespace[copykey].includes("Results for")) {
continue;
}
if (newitem !== undefined && newitem !== null) {
newitem = newitem[copy.namespace[copykey]];
if (!isNaN(copy.namespace[copykey])) {
to_be_copied += ".#";
} else {
to_be_copied += "." + copy.namespace[copykey];
}
}
}
if (newitem !== undefined && newitem !== null) {
newitem = newitem[copy.name];
if (!isNaN(copy.name)) {
to_be_copied += ".#";
} else {
to_be_copied += "." + copy.name;
}
}
to_be_copied = to_be_copied.replaceAll(" ", "_");
console.log("COPY: ", to_be_copied);
const elementName = "copy_element_shuffle";
var copyText = document.getElementById(elementName);
if (copyText !== null && copyText !== undefined) {
//console.log("NAVIGATOR: ", navigator);
const clipboard = navigator.clipboard;
if (clipboard === undefined) {
toast("Can only copy over HTTPS (port 3443)");
return;
}
navigator.clipboard.writeText(to_be_copied);
copyText.select();
copyText.setSelectionRange(0, 99999); /* For mobile devices */
/* Copy the text inside the text field */
document.execCommand("copy");
//console.log("COPYING!");
toast("Copied JSON path to clipboard.")
} else {
console.log("Couldn't find element ", elementName);
}
}
export const handleReactJsonClipboard = (copy) => {
const elementName = "copy_element_shuffle";
var copyText = document.getElementById(elementName);
if (copyText !== null && copyText !== undefined) {
if (
copy.namespace !== undefined &&
copy.name !== undefined &&
copy.src !== undefined
) {
copy = copy.src;
}
const clipboard = navigator.clipboard;
if (clipboard === undefined) {
toast("Can only copy over HTTPS (port 3443)");
return;
}
var stringified = JSON.stringify(copy);
if (stringified.startsWith('"') && stringified.endsWith('"')) {
stringified = stringified.substring(1, stringified.length - 1);
}
navigator.clipboard.writeText(stringified);
copyText.select();
copyText.setSelectionRange(0, 99999); /* For mobile devices */
/* Copy the text inside the text field */
document.execCommand("copy");
console.log("COPYING!");
toast("Copied value to clipboard, NOT json path.")
} else {
console.log("Failed to copy from " + elementName + ": ", copyText);
}
}
export const validateJson = (showResult) => {
if (showResult === undefined || showResult === null) {
return {
+148 -119
View File
@@ -2,7 +2,7 @@
import React, { useEffect, useContext, memo, useState, useRef } from "react";
import { useLocation, useNavigate, Link } from "react-router-dom";
import ReactDOM from "react-dom"
import { getTheme } from "../theme.jsx";
// Material UI Icons
import Add from '@mui/icons-material/Add';
import Search from '@mui/icons-material/Search';
@@ -12,7 +12,6 @@ import GridOnIcon from '@mui/icons-material/GridOn';
import ListIcon from '@mui/icons-material/List';
import PublishIcon from '@mui/icons-material/Publish';
import GetAppIcon from '@mui/icons-material/GetApp';
// Material UI & Components
import { makeStyles } from "@mui/styles";
import { Navigate } from "react-router-dom";
@@ -103,7 +102,7 @@ import Dropzone from "../components/Dropzone.jsx";
import { ToastContainer, toast } from "react-toastify"
import { MuiChipsInput } from "mui-chips-input";
import { v4 as uuidv4 } from "uuid";
import theme from "../theme.jsx";
// import theme from "./theme.jsx";
import algoliasearch from 'algoliasearch/lite';
import { InstantSearch, Configure, connectHits, connectSearchBox, connectRefinementList } from 'react-instantsearch-dom';
import { debounce } from "lodash";
@@ -117,38 +116,7 @@ const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e52
const svgSize = 24;
const imagesize = 22;
const useStyles = makeStyles(() => {
return {
datagrid: {
border: 0,
"& .MuiDataGrid-columnsContainer": {
backgroundColor:
theme?.palette?.type === "light" ? "#fafafa" : theme?.palette?.inputColor,
},
"& .MuiDataGrid-iconSeparator": {
display: "none",
},
"& .MuiDataGrid-colCell, .MuiDataGrid-cell": {
borderRight: `1px solid ${theme?.palette?.type === "light" ? "white" : "#303030"
}`,
},
"& .MuiDataGrid-columnsContainer, .MuiDataGrid-cell": {
borderBottom: `1px solid ${theme?.palette?.type === "light" ? "#f0f0f0" : "#303030"
}`,
},
"& .MuiDataGrid-cell": {
color:
theme?.palette?.type === "light" ? "white" : "rgba(255,255,255,0.65)",
},
"& .MuiPaginationItem-root, .MuiTablePagination-actions, .MuiTablePagination-caption":
{
borderRadius: 0,
color: "white",
},
},
}
})
@@ -436,19 +404,7 @@ export const GetIconInfo = (action) => {
return selectedItem;
};
const chipStyle = {
backgroundColor: "#2F2F2F",
marginRight: 5,
paddingLeft: 5,
paddingRight: 5,
height: 35,
cursor: "pointer",
borderColor: "#2F2F2F",
color: "#C8C8C8",
fontSize: "14px",
fontFamily: theme?.typography?.fontFamily,
borderRadius: "17.5px"
};
export const collapseField = (field) => {
if (field === undefined || field === null) {
@@ -626,12 +582,14 @@ export const validateJson = (showResult) => {
//Custom hook for handling styling of the dropzone
const useDropzoneStyles = () => {
const { leftSideBarOpenByClick } = useContext(Context);
const { themeMode, brandColor } = useContext(Context);
const theme = getTheme(themeMode, brandColor);
return {
paddingTop: 70,
// minHeight: 1000,
backgroundColor: "#1A1A1A",
fontFamily: theme?.typography?.fontFamily,
backgroundColor: theme.palette.backgroundColor,
fontFamily: theme.typography?.fontFamily,
// maxWidth: window.innerWidth > 1366 ? 1366 : isMobile ? "100%" : 1200,
paddingLeft: leftSideBarOpenByClick ? 200 : 0,
transition: "padding-left 0.3s ease",
@@ -662,9 +620,59 @@ const Workflows2 = (props) => {
const [isLoadingWorkflow, setIsLoadingWorkflow] = useState(false);
const [isLoadingPublicWorkflow, setIsLoadingPublicWorkflow] = useState(false);
const [view, setView] = useState(localStorage?.getItem("workflowView") || "grid");
const classes = useStyles(theme)
const imgSize = 60;
const { themeMode, brandColor, brandName } = useContext(Context);
const theme = getTheme(themeMode, brandColor);
const chipStyle = {
backgroundColor: theme.palette.chipStyle.backgroundColor,
marginRight: 5,
paddingLeft: 5,
paddingRight: 5,
height: 35,
cursor: "pointer",
borderColor: theme.palette.chipStyle.borderColor,
color: theme.palette.chipStyle.color,
fontSize: "14px",
fontFamily: theme.typography?.fontFamily,
borderRadius: "17.5px"
};
const newStyles = makeStyles(() => {
return {
datagrid: {
border: 0,
"& .MuiDataGrid-columnsContainer": {
backgroundColor:
theme.palette?.type === "light" ? "#fafafa" : theme.palette?.inputColor,
},
"& .MuiDataGrid-iconSeparator": {
display: "none",
},
"& .MuiDataGrid-colCell, .MuiDataGrid-cell": {
borderRight: `1px solid ${theme.palette?.type === "light" ? "white" : "#303030"
}`,
},
"& .MuiDataGrid-columnsContainer, .MuiDataGrid-cell": {
borderBottom: `1px solid ${theme.palette?.type === "light" ? "#f0f0f0" : "#303030"
}`,
},
"& .MuiDataGrid-cell": {
color:
theme.palette?.type === "light" ? "white" : "rgba(255,255,255,0.65)",
},
"& .MuiPaginationItem-root, .MuiTablePagination-actions, .MuiTablePagination-caption":
{
borderRadius: 0,
color: "white",
},
},
}
})
const classes = newStyles(theme);
const referenceUrl = globalUrl + "/api/v1/hooks/";
var upload = "";
@@ -726,7 +734,7 @@ const Workflows2 = (props) => {
const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
document.title = "Shuffle - Workflows";
document.title = brandName?.length > 0 ? `${brandName} - Workflows` : "Shuffle - Workflows";
useEffect(() => {
const queryParams = new URLSearchParams(location.search);
@@ -1267,7 +1275,20 @@ const Workflows2 = (props) => {
}
useEffect(() => {
const queryParams = new URLSearchParams(window.location.search);
const paramType = queryParams.get("type");
if (paramType === "sso_login") {
localStorage.removeItem("workflows");
queryParams.delete("type");
const newUrl = window.location.pathname + (queryParams.toString() ? `?${queryParams.toString()}` : '');
window.history.replaceState({}, '', newUrl);
window.location.reload();
}
}, []);
const getAvailableWorkflows = (amount) => {
var storageWorkflows = []
setIsLoadingWorkflow(true)
@@ -1568,7 +1589,7 @@ const Workflows2 = (props) => {
width: "100%",
color: "white",
display: "flex",
fontFamily: theme?.typography?.fontFamily,
fontFamily: theme.typography?.fontFamily,
boxSizing: "border-box",
position: "relative",
borderRadius: "8px",
@@ -1589,7 +1610,7 @@ const Workflows2 = (props) => {
width: 160,
height: 44,
justifyContent: "space-between",
fontFamily: theme?.typography?.fontFamily,
fontFamily: theme.typography?.fontFamily,
};
const exportAllWorkflows = (allWorkflows) => {
@@ -2124,7 +2145,7 @@ const Workflows2 = (props) => {
const WorkflowSkeleton = () => {
return (
<Paper elevation={0} style={{
backgroundColor: "#212121",
backgroundColor: theme.palette.platformColor,
width: "100%",
height: 120,
borderRadius: 8,
@@ -2141,7 +2162,7 @@ const Workflows2 = (props) => {
height={90}
style={{
borderRadius: 6,
backgroundColor: "rgba(255, 255, 255, 0.1)"
backgroundColor: theme.palette.loaderColor,
}}
/>
<div style={{
@@ -2155,19 +2176,19 @@ const Workflows2 = (props) => {
variant="text"
width="40%"
height={24}
style={{ backgroundColor: "rgba(255, 255, 255, 0.1)" }}
style={{ backgroundColor: theme.palette.loaderColor }}
/>
<Skeleton
variant="text"
width="60%"
height={20}
style={{ backgroundColor: "rgba(255, 255, 255, 0.1)" }}
style={{ backgroundColor: theme.palette.loaderColor }}
/>
<Skeleton
variant="text"
width="30%"
height={20}
style={{ backgroundColor: "rgba(255, 255, 255, 0.1)" }}
style={{ backgroundColor: theme.palette.loaderColor }}
/>
</div>
</div>
@@ -2240,6 +2261,11 @@ const Workflows2 = (props) => {
setOpen(false);
setAnchorEl(null);
}}
MenuListProps={{
sx: {
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
}
}}
>
{isDistributed ?
<MenuItem
@@ -2253,7 +2279,7 @@ const Workflows2 = (props) => {
</MenuItem>
: null}
<MenuItem
style={{ backgroundColor: theme.palette.inputColor, color: "white" }}
sx={{ backgroundColor: theme.palette.textFieldStyle.backgroundColor, color: theme.palette.text.primary, "&:hover": {backgroundColor: theme.palette.hoverColor} }}
disabled={isDistributed}
onClick={(event) => {
event.stopPropagation()
@@ -2274,7 +2300,7 @@ const Workflows2 = (props) => {
</MenuItem>
<MenuItem
style={{ backgroundColor: theme.palette.inputColor, color: "white" }}
sx={{ backgroundColor: theme.palette.textFieldStyle.backgroundColor, color: theme.palette.text.primary, "&:hover": {backgroundColor: theme.palette.hoverColor} }}
onClick={(event) => {
window.open(`/forms/${data.id}`, "_blank")
}}
@@ -2287,7 +2313,7 @@ const Workflows2 = (props) => {
<Divider />
<MenuItem
style={{ backgroundColor: theme.palette.inputColor, color: "white" }}
sx={{ backgroundColor: theme.palette.textFieldStyle.backgroundColor, color: theme.palette.text.primary, "&:hover": {backgroundColor: theme.palette.hoverColor} }}
disabled={isDistributed}
onClick={() => {
sideloadWorkflow(data.id, "publish")
@@ -2301,8 +2327,8 @@ const Workflows2 = (props) => {
</MenuItem>
<MenuItem
style={{ backgroundColor: theme.palette.inputColor, color: "white" }}
disabled={isDistributed}
sx={{ backgroundColor: theme.palette.textFieldStyle.backgroundColor, color: theme.palette.text.primary, "&:hover": {backgroundColor: theme.palette.hoverColor} }}
//disabled={isDistributed}
onClick={() => {
sideloadWorkflow(data.id, "export", setOpen)
@@ -2317,7 +2343,7 @@ const Workflows2 = (props) => {
<Divider />
<MenuItem
style={{ backgroundColor: theme.palette.inputColor, color: "white" }}
sx={{ backgroundColor: theme.palette.textFieldStyle.backgroundColor, color: theme.palette.text.primary, "&:hover": {backgroundColor: theme.palette.hoverColor} }}
disabled={isDistributed}
onClick={() => {
duplicateWorkflow(data)
@@ -2330,7 +2356,7 @@ const Workflows2 = (props) => {
</MenuItem>
<MenuItem
style={{ backgroundColor: theme.palette.inputColor, color: "white" }}
sx={{ backgroundColor: theme.palette.textFieldStyle.backgroundColor, color: theme.palette.text.primary, "&:hover": {backgroundColor: theme.palette.hoverColor} }}
onClick={() => {
setDeleteModalOpen(true);
setSelectedWorkflowId(data.id);
@@ -2447,7 +2473,7 @@ const Workflows2 = (props) => {
return (
<div style={{ width: "100%", minWidth: 320, position: "relative", border: highlightIds.includes(data.id) ? "2px solid #f85a3e" : isDistributed || hasSuborgs ? `2px solid ${theme.palette.distributionColor}` : "inherit", borderRadius: theme.palette?.borderRadius, backgroundColor: "#212121", fontFamily: theme?.typography?.fontFamily }}>
<div style={{ width: "100%", minWidth: 320, position: "relative", border: highlightIds.includes(data.id) ? "2px solid #f85a3e" : isDistributed || hasSuborgs ? `2px solid ${theme.palette.distributionColor}` : "inherit", borderRadius: theme.palette?.borderRadius, backgroundColor: "#212121", fontFamily: theme.typography?.fontFamily }}>
<Paper square style={paperAppStyle}>
{selectedCategory !== "" ?
<Tooltip title={`Usecase Category: ${selectedCategory}`} placement="bottom">
@@ -2461,7 +2487,7 @@ const Workflows2 = (props) => {
width: 3,
backgroundColor: boxColor,
borderRadius: "0 100px 0 0",
fontFamily: theme?.typography?.fontFamily,
fontFamily: theme.typography?.fontFamily,
}}
onClick={() => {
addFilter(selectedCategory)
@@ -2472,7 +2498,7 @@ const Workflows2 = (props) => {
<Grid
item
style={{ display: "flex", flexDirection: "column", width: "100%", fontFamily: theme?.typography?.fontFamily }}
style={{ display: "flex", flexDirection: "column", width: "100%", fontFamily: theme.typography?.fontFamily }}
>
<Grid item style={{ display: "flex", maxHeight: 34 }}>
{currTab === 2 ? null :
@@ -2526,7 +2552,7 @@ const Workflows2 = (props) => {
<Typography style={{
color: "rgba(255,255,255,0.9)",
fontSize: "16px",
fontFamily: theme?.typography?.fontFamily,
fontFamily: theme.typography?.fontFamily,
}}>
Edit: {data.name}
</Typography>
@@ -2553,13 +2579,13 @@ const Workflows2 = (props) => {
} placement="right">
<Typography
variant="body1"
style={{
marginBottom: 0,
paddingBottom: 0,
fontSize: 18,
maxHeight: 30,
flex: 10,
fontFamily: theme?.typography?.fontFamily,
fontFamily: theme.typography?.fontFamily,
fontWeight: 500,
}}
>
@@ -2594,7 +2620,7 @@ const Workflows2 = (props) => {
style={{
height: 24,
width: 24,
filter: "brightness(0.6)",
filter: themeMode === "dark" ? "brightness(0.6)" : "brightness(0.9)",
cursor: "pointer",
}}
onClick={() => {
@@ -2751,7 +2777,7 @@ const Workflows2 = (props) => {
overflow: "hidden",
marginTop: 8,
maxHeight: 35,
fontFamily: theme?.typography?.fontFamily,
fontFamily: theme.typography?.fontFamily,
}}
>
{data.tags !== undefined && data.tags !== null
@@ -3395,7 +3421,7 @@ const Workflows2 = (props) => {
);
}
return (
<div style={{ ...gridContainer, backgroundColor: "#212121" }}>
<div style={{ ...gridContainer, backgroundColor: theme.palette.platformColor }}>
<Tooltip title={`New Workflow`} placement="bottom">
<IconButton
style={{ position: "absolute", top: 10, right: 50, zIndex: 1000 }}
@@ -3984,8 +4010,8 @@ const Workflows2 = (props) => {
const iconButtonStyle = {
color: 'white',
backgroundColor: '#212121',
color: theme.palette.text.primary,
backgroundColor: theme.palette.platformColor,
borderRadius: '4px',
padding: "12px 16px",
cursor: 'pointer',
@@ -4085,20 +4111,20 @@ const Workflows2 = (props) => {
maxWidth: "25%",
minWidth: "25%",
height: 47,
backgroundColor: "#212121",
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
}}
InputProps={{
style: {
color: "white",
color: theme.palette.textFieldStyle.color,
height: "100%",
backgroundColor: "#212121",
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
},
placeholder: "Search Workflows",
}}
sx={{
'& .MuiOutlinedInput-root': {
borderRadius: '4px',
backgroundColor: "#212121",
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
},
}}
value={localQuery}
@@ -4140,9 +4166,9 @@ const Workflows2 = (props) => {
maxWidth: "25%",
height: 47,
borderRadius: 4,
backgroundColor: "#212121",
fontFamily: theme?.typography?.fontFamily,
color: "#FFFFFF",
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
fontFamily: theme.typography?.fontFamily,
color: theme.palette.textFieldStyle.color,
}}
MenuProps={{
anchorOrigin: {
@@ -4155,9 +4181,9 @@ const Workflows2 = (props) => {
},
PaperProps: {
style: {
backgroundColor: '#212121',
color: '#FFFFFF',
fontFamily: theme?.typography?.fontFamily,
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
color: theme.palette.textFieldStyle.color,
fontFamily: theme.typography?.fontFamily,
}
}
}}
@@ -4193,7 +4219,7 @@ const Workflows2 = (props) => {
)
}
>
<MenuItem disabled value="" style={{ fontFamily: theme?.typography?.fontFamily, fontSize: 16 }}>
<MenuItem disabled value="" style={{ fontFamily: theme.typography?.fontFamily, fontSize: 16 }}>
All Usecases
</MenuItem>
{items.map((usecase, index) => (
@@ -4206,13 +4232,13 @@ const Workflows2 = (props) => {
'&:hover': {
backgroundColor: '#3A3A3A', // Darker background on hover
},
fontFamily: theme?.typography?.fontFamily,
fontFamily: theme.typography?.fontFamily,
fontSize: 16
}}
>
<Checkbox
checked={currentRefinement.includes(usecase.label)}
style={{ marginRight: 8, color: '#FFFFFF', fontSize: 16 }}
style={{ marginRight: 8, fontSize: 16 }}
/>
{usecase.label} ({usecase.count})
</MenuItem>
@@ -4225,7 +4251,7 @@ const Workflows2 = (props) => {
const tabStyle = {
textTransform: 'none',
marginRight: 20,
fontFamily: theme?.typography?.fontFamily,
fontFamily: theme.typography?.fontFamily,
fontSize: 16,
borderBottom: "5px solid transparent",
minHeight: "48px",
@@ -4233,9 +4259,9 @@ const Workflows2 = (props) => {
}
const tabActive = {
borderBottom: "5px solid #FF8544",
borderBottom: `5px solid ${theme.palette.primary.main}`,
borderRadius: "2px",
color: "#FF8544"
color: theme.palette.primary.main
}
@@ -4258,16 +4284,16 @@ const Workflows2 = (props) => {
maxWidth: isSafari ? "100%" : "70%",
margin: "auto",
}}>
<Typography variant="h4" style={{ marginBottom: 20, paddingLeft: 15, textTransform: 'none', fontFamily: theme?.typography?.fontFamily }}>
<Typography variant="h4" color="textPrimary" style={{ marginBottom: 20, paddingLeft: 15, textTransform: 'none', fontFamily: theme.typography?.fontFamily }}>
{currTab === 0 ? "Org" : currTab === 1 ? "Your" : "Discover"} Workflows
</Typography>
<div style={{ borderBottom: '1px solid gray', marginBottom: 30 }}>
<div style={{ borderBottom: themeMode === "dark" ? "1px solid #808080" : theme.palette.defaultBorder, marginBottom: 30 }}>
<Tabs
value={currTab}
onChange={(event, newTab) => handleTabChange(event, newTab)}
style={{
fontFamily: theme?.typography?.fontFamily,
fontFamily: theme.typography?.fontFamily,
fontSize: 16,
marginBottom: "-2px"
}}
@@ -4327,16 +4353,17 @@ const Workflows2 = (props) => {
minWidth: "25%",
height: 43,
maxHeight: "fit-content",
backgroundColor: "#212121",
zIndex: 1000,
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
zIndex: 1000,
color: theme.palette.textFieldStyle.color
}}
disabled={currTab === 2}
InputProps={{
style: {
color: "white",
height: "fit-content",
maxHeight: "fit-content",
backgroundColor: "#212121",
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
color: theme.palette.textFieldStyle.color
},
placeholder: "Filter Workflows",
// endAdornment: (
@@ -4359,7 +4386,8 @@ const Workflows2 = (props) => {
'& .MuiOutlinedInput-root': {
height: "fit-content",
borderRadius: '4px',
backgroundColor: '#212121',
color: theme.palette.textFieldStyle.color,
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
'& fieldset': {
borderColor: 'rgba(255, 255, 255, 0.23)',
},
@@ -4373,10 +4401,12 @@ const Workflows2 = (props) => {
display: 'flex',
flexWrap: 'wrap',
gap: '4px',
fontSize: 18,
padding: '4px 8px',
alignItems: 'center',
height: "fit-content", // Match height
backgroundColor: "#212121",
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
color: theme.palette.textFieldStyle.color
},
// Rest of the styling remains the same...
@@ -4424,8 +4454,9 @@ const Workflows2 = (props) => {
maxWidth: "25%",
height: 47,
borderRadius: 4,
backgroundColor: "#212121",
fontFamily: theme?.typography?.fontFamily
fontSize: 18,
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
fontFamily: theme.typography?.fontFamily,
}}
sx={{
'& .MuiOutlinedInput-root': {
@@ -4460,12 +4491,12 @@ const Workflows2 = (props) => {
removeFilter(filters.indexOf(usecase?.name.toLowerCase()))
}
}}
style={{
sx={{
padding: "12px 16px",
borderBottom: index === usecases.length - 2 ? "none" : "1px solid rgba(255,255,255,0.05)",
"&:hover": {
backgroundColor: "rgba(255,255,255,0.1)"
}
},
}}
>
<div style={{
@@ -4479,7 +4510,7 @@ const Workflows2 = (props) => {
style={{
padding: 0,
marginRight: 8,
color: "rgba(255,255,255,0.7)"
color: theme.palette.textFieldStyle.color,
}}
/>
<div style={{
@@ -4491,7 +4522,7 @@ const Workflows2 = (props) => {
<Typography
variant="body1"
style={{
color: "rgba(255,255,255,0.9)",
color: theme.palette.textFieldStyle.color,
fontWeight: selectedCategory.includes(category) ? 500 : 400
}}
>
@@ -4500,8 +4531,8 @@ const Workflows2 = (props) => {
<Typography
variant="body2"
style={{
color: "rgba(255,255,255,0.5)",
backgroundColor: "rgba(255,255,255,0.1)",
color: theme.palette.textFieldStyle.color,
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
padding: "2px 8px",
borderRadius: "12px",
fontSize: "0.75rem"
@@ -4539,7 +4570,7 @@ const Workflows2 = (props) => {
onClick={() => navigate("/workflows/debug")}
disabled={currTab === 2}
>
<QueryStatsIcon style={{ color: currTab === 2 ? "rgba(241, 241, 241, 0.5)" : "#F1F1F1" }} />
<QueryStatsIcon style={{ color: theme.palette.text.primary, opacity: currTab === 2 ? 0.5 : 1 }} />
</IconButton>
</Tooltip>
@@ -4554,8 +4585,8 @@ const Workflows2 = (props) => {
disabled={currTab === 2}
>
{view === "grid" ?
<ListIcon style={{ color: currTab === 2 ? "rgba(255, 255, 255, 0.5)" : "white" }} /> :
<GridOnIcon style={{ color: currTab === 2 ? "rgba(255, 255, 255, 0.5)" : "white" }} />
<ListIcon style={{ color: theme.palette.text.primary, opacity: currTab === 2 ? 0.5 : 1 }} /> :
<GridOnIcon style={{ color: theme.palette.text.primary, opacity: currTab === 2 ? 0.5 : 1 }} />
}
</IconButton>
</Tooltip>
@@ -4568,7 +4599,7 @@ const Workflows2 = (props) => {
>
{submitLoading ?
<CircularProgress color="secondary" /> :
<PublishIcon style={{ color: currTab === 2 ? "rgba(255, 255, 255, 0.5)" : "white" }} />
<PublishIcon style={{ color: theme.palette.text.primary, opacity: currTab === 2 ? 0.5 : 1}} />
}
</IconButton>
</Tooltip>
@@ -4587,7 +4618,7 @@ const Workflows2 = (props) => {
disabled={isCloud || currTab === 2}
onClick={() => exportAllWorkflows(workflows)}
>
<GetAppIcon style={{ color: (isCloud || currTab === 2) ? "rgba(255, 255, 255, 0.5)" : "white" }} />
<GetAppIcon style={{ color: theme.palette.text.primary, opacity: currTab === 2 ? 0.5 : 1}} />
</IconButton>
</Tooltip>
</div>
@@ -4600,13 +4631,11 @@ const Workflows2 = (props) => {
borderRadius: 4,
flex: 0.8,
textTransform: 'none',
backgroundColor: "#FF8544",
color: "#1A1A1A",
fontFamily: theme?.typography?.fontFamily,
fontFamily: theme.typography?.fontFamily,
fontSize: 16,
fontWeight: 500
}}
startIcon={<Add style={{ color: "#1A1A1A" }} />}
startIcon={<Add/>}
>
Create Workflow
</Button>