Sync from cloud for 2.1.0-rc1

This commit is contained in:
Frikky
2025-06-04 18:37:36 +02:00
parent 90d909e5da
commit 6ad756f8d8
13 changed files with 357 additions and 86 deletions
+1 -1
View File
@@ -1463,7 +1463,7 @@ const ActionsList = memo(({
<Tooltip title="Go to app" placement="right"> <Tooltip title="Go to app" placement="right">
<div style={{ display: "flex", alignItems: "center" }}> <div style={{ display: "flex", alignItems: "center" }}>
<img <img
src={openapi?.info["x-logo"]} src={openapi?.info["x-logo"]?.length > 0 ? openapi?.info["x-logo"] : theme?.palette?.defaultImage}
width={48} width={48}
height={48} height={48}
alt="app logo" alt="app logo"
+153 -8
View File
@@ -83,18 +83,17 @@ const AppAuthTab = memo((props) => {
const [appAuthenticationGroupId, setAppAuthenticationGroupId] = React.useState(""); const [appAuthenticationGroupId, setAppAuthenticationGroupId] = React.useState("");
const [appAuthenticationGroups, setAppAuthenticationGroups] = React.useState([]); const [appAuthenticationGroups, setAppAuthenticationGroups] = React.useState([]);
const [appAuthenticationGroupName, setAppAuthenticationGroupName] = React.useState(""); const [appAuthenticationGroupName, setAppAuthenticationGroupName] = React.useState("");
const [selectedSubOrg, setSelectedSubOrg] = useState([]);
const [appAuthenticationGroupDescription, setAppAuthenticationGroupDescription] = React.useState(""); const [appAuthenticationGroupDescription, setAppAuthenticationGroupDescription] = React.useState("");
const [appsForAppAuthGroup, setAppsForAppAuthGroup] = React.useState([]); const [appsForAppAuthGroup, setAppsForAppAuthGroup] = React.useState([]);
const [searchQuery, setSearchQuery] = React.useState(""); const [searchQuery, setSearchQuery] = React.useState("");
const [showAppModal, setShowAppModal] = useState(false) const [showAppModal, setShowAppModal] = useState(false)
const [selectedAuthId, setSelectedAuthId] = useState("");
const [showDistributionPopup, setShowDistributionPopup] = useState(false);
const [showAuthenticationLoader, setShowAuthenticationLoader] = useState(true) const [showAuthenticationLoader, setShowAuthenticationLoader] = useState(true)
const [showAppAuthGroupLoader, setShowAppAuthGroupLoader] = useState(true) const [showAppAuthGroupLoader, setShowAppAuthGroupLoader] = useState(true)
const { themeMode, supportEmail, brandColor } = useContext(Context) const { themeMode, supportEmail, brandColor } = useContext(Context)
const theme = getTheme(themeMode, brandColor) const theme = getTheme(themeMode, brandColor)
const changeDistribution = (data) => {
//changeDistributed(data, !isDistributed)
editAuthenticationConfig(data.id, "suborg_distribute")
}
useEffect(() => { useEffect(() => {
getAppAuthentication(); getAppAuthentication();
@@ -214,11 +213,39 @@ const AppAuthTab = memo((props) => {
}); });
}; };
const editAuthenticationConfig = (id, parentAction) => { const handleSelectSubOrg = (id, action) => {
if (action === "all") {
const childOrgs = userdata.orgs.filter(
(data) => data.creator_org === userdata.active_org.id
);
setSelectedSubOrg((prev) => {
if (prev.length === childOrgs.length) {
// If all child orgs are already selected, clear the selection
return [];
} else {
// Otherwise, select all child org IDs
return childOrgs.map((data) => data.id);
}
});
} else if (action === "none") {
setSelectedSubOrg([]);
} else {
setSelectedSubOrg((prev) => {
if (prev.includes(id)) {
return prev.filter((data) => data !== id);
} else {
return [...prev, id];
}
});
}
};
const editAuthenticationConfig = (id, parentAction, selectedSuborgs) => {
const data = { const data = {
id: id, id: id,
action: parentAction !== undefined && parentAction !== null ? parentAction : "assign_everywhere", action: parentAction !== undefined && parentAction !== null ? parentAction : "assign_everywhere",
} selected_suborgs: selectedSuborgs !== undefined && selectedSuborgs !== null ? selectedSuborgs : [],
}
const url = globalUrl + "/api/v1/apps/authentication/" + id + "/config"; const url = globalUrl + "/api/v1/apps/authentication/" + id + "/config";
@@ -240,6 +267,7 @@ const AppAuthTab = memo((props) => {
} else { } else {
toast("Successfully updated auth!"); toast("Successfully updated auth!");
setSelectedUserModalOpen(false); setSelectedUserModalOpen(false);
setShowDistributionPopup(false);
setTimeout(() => { setTimeout(() => {
getAppAuthentication(); getAppAuthentication();
}, 1000); }, 1000);
@@ -251,6 +279,106 @@ const AppAuthTab = memo((props) => {
}); });
}; };
const changeDistribution = (id, selectedSubOrg) => {
editAuthenticationConfig(id, "suborg_distribute", [...new Set(selectedSubOrg)])
}
const cacheDistributionModal = showDistributionPopup ? (
<Dialog
open={showDistributionPopup}
onClose={() => {setShowDistributionPopup(false);setSelectedAuthId("")}}
PaperProps={{
sx: {
borderRadius: theme?.palette?.DialogStyle?.borderRadius,
border: theme?.palette?.DialogStyle?.border,
fontFamily: theme?.typography?.fontFamily,
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
zIndex: 1000,
minWidth: "600px",
minHeight: "320px",
overflow: "auto",
'& .MuiDialogContent-root': {
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
},
'& .MuiDialogTitle-root': {
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
},
'& .MuiDialogActions-root': {
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
},
},
}}
>
<DialogTitle>
<Typography variant="h5" color="textPrimary">
Select sub-org to distribute Datastore key
</Typography>
</DialogTitle>
<DialogContent style={{ color: "rgba(255,255,255,0.65)" }}>
<MenuItem value="none" onClick={()=> {handleSelectSubOrg(null, "none")}}>None</MenuItem>
<MenuItem value="all" onClick={()=> {handleSelectSubOrg(null, "all")}}>All</MenuItem>
{userdata.orgs.map((data, index) => {
if (data.creator_org !== userdata.active_org.id) {
return null;
}
const imagesize = 22;
const imageStyle = {
width: imagesize,
height: imagesize,
pointerEvents: "none",
marginRight: 10,
marginLeft: data.id === userdata.active_org.id ? 0 : 20,
};
const image = data.image === "" ? (
<img alt={data.name} src={theme.palette.defaultImage} style={imageStyle} />
) : (
<img alt={data.name} src={data.image} style={imageStyle} />
);
return (
<MenuItem
key={index}
value={data.id}
onClick={() => handleSelectSubOrg(data.id)}
style={{ display: "flex", alignItems: "center" }}
>
<Checkbox
checked={selectedSubOrg.includes(data.id)}
/>
{image}
<span style={{ marginLeft: 8 }}>{data.name}</span>
</MenuItem>
);
})}
<div style={{ display: "flex", marginTop: 20 }}>
<Button
style={{ borderRadius: "2px", textTransform: 'none', fontSize:16, color: theme.palette.primary.main }}
onClick={() => {setShowDistributionPopup(false); setSelectedAuthId("")}}
color="primary"
>
Cancel
</Button>
<Button
variant="contained"
style={{ borderRadius: "2px", textTransform: 'none', fontSize:16, marginLeft: 10 }}
onClick={() => {
changeDistribution(selectedAuthId, selectedSubOrg);
}}
color="primary"
>
Submit
</Button>
</div>
</DialogContent>
</Dialog>
) : null;
const editAuthenticationModal = selectedAuthenticationModalOpen ? ( const editAuthenticationModal = selectedAuthenticationModalOpen ? (
<Dialog <Dialog
open={selectedAuthenticationModalOpen} open={selectedAuthenticationModalOpen}
@@ -857,6 +985,7 @@ const AppAuthTab = memo((props) => {
return ( return (
<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, }}> <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} {appModal}
{cacheDistributionModal}
<div style={{ height: "100%", width: "calc(100% - 20px)", scrollbarColor: theme.palette.scrollbarColorTransparent, scrollbarWidth: 'thin'}}> <div style={{ height: "100%", width: "calc(100% - 20px)", scrollbarColor: theme.palette.scrollbarColorTransparent, scrollbarWidth: 'thin'}}>
<div style={{ width: 'auto', display:'flex',}}> <div style={{ width: 'auto', display:'flex',}}>
<div style={{display: 'flex', flexDirection: 'column'}}> <div style={{display: 'flex', flexDirection: 'column'}}>
@@ -1004,7 +1133,7 @@ const AppAuthTab = memo((props) => {
]; ];
} }
const isDistributed = data.suborg_distributed === true ? true : false; const isDistributed = data?.suborg_distribution?.length > 0 || data?.suborg_distributed ? true : false;
var validIcon = <CheckCircleIcon style={{ color: "green" }} /> var validIcon = <CheckCircleIcon style={{ color: "green" }} />
if (data.validation !== null && data.validation !== undefined && data.validation.valid === false) { if (data.validation !== null && data.validation !== undefined && data.validation.valid === false) {
@@ -1260,7 +1389,23 @@ const AppAuthTab = memo((props) => {
style={{ }} style={{ }}
color="secondary" color="secondary"
onClick={() => { onClick={() => {
changeDistribution(data, !isDistributed) setShowDistributionPopup(true)
if(data?.suborg_distribution?.length > 0){
setSelectedSubOrg(data.suborg_distribution)
}else{
setSelectedSubOrg([])
}
setSelectedAuthId(data.id)
if (data?.suborg_distributed) {
const allSuborg = userdata?.orgs?.map((data, index) => {
if (data.creator_org !== userdata.active_org.id) {
return null;
}
return data.id;
})
setSelectedSubOrg(allSuborg.filter((data) => data !== null))
}
}} }}
/> />
</Tooltip> </Tooltip>
+8 -5
View File
@@ -33,7 +33,7 @@ const Branding = (props) => {
//const alert = useAlert(); //const alert = useAlert();
const [publishingInfo, setPublishingInfo] = useState(""); const [publishingInfo, setPublishingInfo] = useState("");
const [publishRequirements, setPublishRequirements] = useState([]) const [publishRequirements, setPublishRequirements] = useState([])
const [currentSelectedTheme, setCurrentSelectedTheme] = useState("dark"); const [currentSelectedTheme, setCurrentSelectedTheme] = useState(themeMode);
const [integrationPartner, setIntegrationPartner] = useState(false); const [integrationPartner, setIntegrationPartner] = useState(false);
const [changingTheme, setChangingTheme] = useState(false); const [changingTheme, setChangingTheme] = useState(false);
const theme = getTheme(themeMode, brandColor) const theme = getTheme(themeMode, brandColor)
@@ -190,6 +190,13 @@ const Branding = (props) => {
}) })
}; };
useEffect(() => {
if (userdata && userdata?.active_org && userdata?.active_org?.branding?.theme?.length > 0) {
console.log("Setting current selected theme from userdata", userdata?.active_org?.branding?.theme);
setCurrentSelectedTheme(userdata?.active_org?.branding?.theme);
}
},[userdata]);
// Should enable / disable org branding // Should enable / disable org branding
const handleChangePublishing = () => { const handleChangePublishing = () => {
console.log("Handle change publishing"); console.log("Handle change publishing");
@@ -247,10 +254,6 @@ const Branding = (props) => {
setCurrentSelectedTheme(userdata?.active_org?.branding.theme); setCurrentSelectedTheme(userdata?.active_org?.branding.theme);
} }
if (userdata.support) {
setCurrentSelectedTheme(localStorage.getItem("theme") || "dark");
}
if ( if (
selectedOrganization && selectedOrganization &&
selectedOrganization?.branding?.brand_color && selectedOrganization?.branding?.brand_color &&
+8 -1
View File
@@ -216,6 +216,13 @@ const CacheView = memo((props) => {
category: selectedCategory, category: selectedCategory,
} }
if (listCache.length > 0) {
const selectedCache = listCache.find((data) => data.key === dataValue.key);
if (selectedCache?.suborg_distribution?.length > 0) {
cache.suborg_distribution = selectedCache.suborg_distribution;
}
}
setCacheInput([cache]); setCacheInput([cache]);
fetch(globalUrl + `/api/v1/orgs/${orgId}/set_cache`, { fetch(globalUrl + `/api/v1/orgs/${orgId}/set_cache`, {
@@ -805,7 +812,7 @@ const CacheView = memo((props) => {
}} }}
> >
<AddIcon/> <AddIcon/>
Category (beta) Category
</Button> </Button>
</Tooltip> </Tooltip>
} }
+3 -9
View File
@@ -212,13 +212,7 @@ const EditWorkflow = (props) => {
//minWidth: isMobile ? "90%" : newWorkflow === true ? 1000 : 550, //minWidth: isMobile ? "90%" : newWorkflow === true ? 1000 : 550,
//maxWidth: isMobile ? "90%" : newWorkflow === true ? 1000 : 550, //maxWidth: isMobile ? "90%" : newWorkflow === true ? 1000 : 550,
borderRadius: theme.palette.DialogStyle.borderRadius, borderRadius: theme.palette.DialogStyle.borderRadius,
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor, backgroundColor: themeMode === "dark" ? "black" : theme?.palette?.DialogStyle?.backgroundColor,
'& .MuiDialogContent-root': {
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
},
'& .MuiDialogTitle-root': {
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
},
}, },
}} }}
> >
@@ -299,10 +293,10 @@ const EditWorkflow = (props) => {
right: 20, right: 20,
bottom: 0, bottom: 0,
zIndex: 1002, zIndex: 1002,
backgroundColor: theme.palette.DialogStyle.backgroundColor,
height: 75, height: 75,
paddingTop: 20, paddingTop: 20,
paddingLeft: 30, paddingLeft: 30,
backgroundColor: themeMode === "dark" ? "#262626" : theme.palette.DialogStyle.backgroundColor,
}}> }}>
<Button <Button
variant="contained" variant="contained"
@@ -1292,7 +1286,7 @@ const EditWorkflow = (props) => {
{newWorkflow === true ? {newWorkflow === true ?
<span style={{ paddingTop: 30, backgroundColor: theme.palette.DialogStyle.backgroundColor }}> <span style={{ paddingTop: 30 }}>
<Typography variant="h6" style={{ marginLeft: 30, paddingBottom: 0, }}> <Typography variant="h6" style={{ marginLeft: 30, paddingBottom: 0, }}>
Relevant Workflows Relevant Workflows
</Typography> </Typography>
+11 -5
View File
@@ -118,6 +118,14 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
}; };
}, [setSearchBarModalOpen]); }, [setSearchBarModalOpen]);
useEffect(() => {
if (userdata && userdata?.active_org?.branding?.theme?.length > 0) {
setCurrentSelectedTheme(userdata?.active_org?.branding?.theme);
}else if (userdata && userdata?.theme?.length > 0) {
setCurrentSelectedTheme(userdata?.theme);
}
}, [userdata]);
const CustomPopper = (props) => { const CustomPopper = (props) => {
return ( return (
<Portal> <Portal>
@@ -436,11 +444,7 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
response.json().then((responseJson) => { response.json().then((responseJson) => {
if (responseJson["success"] === false) { if (responseJson["success"] === false) {
toast("Failed updating org: ", responseJson.reason); toast("Failed updating org: ", responseJson.reason);
} else { }
handleThemeChange(newTheme)
setCurrentSelectedTheme(newTheme);
}
}) })
) )
.catch((error) => { .catch((error) => {
@@ -586,6 +590,8 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
if (newTheme === currentSelectedTheme) { if (newTheme === currentSelectedTheme) {
return; return;
} }
handleThemeChange(newTheme)
setCurrentSelectedTheme(newTheme);
if (userdata?.org_status?.includes("integration_partner")){ if (userdata?.org_status?.includes("integration_partner")){
handleChangeTheme(newTheme); handleChangeTheme(newTheme);
} else { } else {
+60 -23
View File
@@ -3750,41 +3750,46 @@ const ParsedAction = (props) => {
boxShadow={2} boxShadow={2}
backgroundColor={theme.palette.textFieldStyle} backgroundColor={theme.palette.textFieldStyle}
display="flex" display="flex"
height={"100%"}
flexDirection="column" flexDirection="column"
> >
<Box display="flex" alignItems="center" justifyContent="space-between"> <Box display="flex" alignItems="center" justifyContent="space-between">
<Typography variant="body1" style={{ flexGrow: 1 }}> <Typography sx={{fontSize: "16px"}} style={{ flexGrow: 1 }}>
{tmpitem.charAt(0).toUpperCase() + tmpitem.slice(1)} {tmpitem.charAt(0).toUpperCase() + tmpitem.slice(1)}
</Typography> </Typography>
<IconButton size="small" {/* <IconButton size="small"
onClick={() => { onClick={() => {
setUiBox("closed") setUiBox("closed")
}} }}
> >
<CloseIcon fontSize="small" /> <CloseIcon fontSize="small" />
</IconButton> </IconButton> */}
</Box> </Box>
<Divider sx={{ backgroundColor: theme.palette.surfaceColor, marginTop: "5px", marginBottom: "10px", height: "3px" }} /> <Divider sx={{ backgroundColor: theme.palette.surfaceColor, marginTop: "5px", marginBottom: "10px", height: "1px" }} />
<Box display="flex" flexDirection="column"> <Box display="flex" flexDirection="column">
<Typography variant="body2" mb={0.5}> <Typography sx={{fontSize: "14px"}} mb={0.5}>
<strong>Required:</strong> {data.required === true || data.configuration === true ? "True" : "False"} <strong>Required:</strong> {data.required === true || data.configuration === true ? "True" : "False"}
</Typography> </Typography>
<Typography variant="body2" mb={0.5}> {
<strong>Description:</strong> {description} data.description !== undefined && data.description !== null && data.description.length > 0 ?
</Typography> <Typography sx={{fontSize: "14px"}} mb={0.5}>
<Typography variant="body2"> <strong>Description:</strong> {description}
</Typography>
: null
}
<Typography sx={{fontSize: "14px"}}>
<strong>Ex. :</strong> {data?.example?.length > 0 ? data.example : "No example available"} <strong>Ex. :</strong> {data?.example?.length > 0 ? data.example : "No example available"}
</Typography> </Typography>
{ {
data?.configuration === true ? data?.configuration === true ?
( (
<Typography variant="body2" mt={0.5}> <Typography sx={{fontSize: "14px"}} mt={0.5}>
<strong>Auth: </strong>Use "\$" instead of "$" <strong>Auth: </strong>Use "\$" instead of "$"
</Typography> </Typography>
) : null ) : null
} }
<div onClick={(e) => { {/* <div onClick={(e) => {
e.preventDefault() e.preventDefault()
e.stopPropagation() e.stopPropagation()
@@ -3794,7 +3799,7 @@ const ParsedAction = (props) => {
<Typography style={{ marginTop: 10, color: "#FF8544", cursor: "pointer", }} variant="body2"> <Typography style={{ marginTop: 10, color: "#FF8544", cursor: "pointer", }} variant="body2">
Don't show again Don't show again
</Typography> </Typography>
</div> </div> */}
</Box> </Box>
</Box> </Box>
); );
@@ -4734,8 +4739,9 @@ const ParsedAction = (props) => {
{isFirstOptional ? <Divider style={{ backgroundColor: "rgba(255,255,255,0.1)", marginBottom: 20, }} /> : null} {isFirstOptional ? <Divider style={{ backgroundColor: "rgba(255,255,255,0.1)", marginBottom: 20, }} /> : null}
{showButtonField === true ? hideBodyButtonValue : null} {showButtonField === true ? hideBodyButtonValue : null}
<div <div
style={{ marginTop: 18, marginBottom: 0, display: "flex" }} style={{ marginTop: 18, marginBottom: "4px", display: "flex", width: "100%", justifyContent: "space-between", alignItems: "center"}}
> >
<div style={{display: "flex", alignItems: "center", justifyContent: "flex-start"}}>
{data.configuration === true ? ( {data.configuration === true ? (
<Tooltip <Tooltip
title={buttonTitle} title={buttonTitle}
@@ -4794,28 +4800,59 @@ const ParsedAction = (props) => {
<StorageIcon style={{ <StorageIcon style={{
color: "#FF8544", color: "#FF8544",
marginRight: 10, marginRight: 10,
marginBottom: "-5px",
}} /> }} />
</a> </a>
</Tooltip> </Tooltip>
: null} : null}
<div <Tooltip
style={{ title={tooltipDescription}
flex: "10", placement="left"
marginTop: "auto", sx={{
marginBottom: "auto", zIndex: 0,
color: theme.palette.textPrimary, }}
PopperProps={{
sx: {
'& .MuiTooltip-tooltip': {
backgroundColor: 'transparent',
boxShadow: 'none',
},
'& .MuiTooltip-arrow': {
color: 'transparent',
},
},
modifiers: [
{
name: 'offset',
options: {
offset: (data?.configuration || showCacheConfig || hasAutocomplete) ? [0, 31] : [0,0]
},
},
],
}} }}
> >
{tmpitem} <span style={{ color: theme.palette.main }}>{selectedActionParameters[count].required || selectedActionParameters[count].configuration ? "*" : ""}</span> <Typography
</div> sx={{
marginTop: "auto",
width: "fit-content",
cursor: "pointer",
color: theme.palette.textPrimary,
fontFamily: theme.typography.fontFamily,
"&:hover": {
color: theme.palette.main,
},
}}
>
{tmpitem} <span style={{ color: theme.palette.main }}>{selectedActionParameters[count].required || selectedActionParameters[count].configuration ? "*" : ""}</span>
</Typography>
</Tooltip>
{parentParamValue !== undefined && parentParamValue !== null && parentParamValue !== "" && parentParamValue !== data.value ? {parentParamValue !== undefined && parentParamValue !== null && parentParamValue !== "" && parentParamValue !== data.value ?
<Tooltip title={`Parent value is different. Click to reset.`} placement="top" arrow> <Tooltip title={`Parent value is different. Click to reset.`} placement="top" arrow>
<RestoreIcon <RestoreIcon
style={{ style={{
marginRight: 10, marginRight: 10,
marginBottom: 5,
cursor: "pointer", cursor: "pointer",
color: theme.palette.distributionColor, color: theme.palette.distributionColor,
}} }}
@@ -4828,7 +4865,7 @@ const ParsedAction = (props) => {
/> />
</Tooltip> </Tooltip>
: null} : null}
</div>
{((data.options !== undefined && data.options !== null && data.options.length > 0) || (selectedActionParameters[count].options !== undefined && selectedActionParameters[count].options !== null && selectedActionParameters[count].options.length > 0)) ? null : {((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"> <Tooltip title="Expand editor window" placement="top">
+6 -6
View File
@@ -76,7 +76,6 @@ const UserManagmentTab = memo((props) => {
const [ipSelected, setIpSelected] = React.useState(""); const [ipSelected, setIpSelected] = React.useState("");
const [userLogViewing, setUserLogViewing] = React.useState({}); const [userLogViewing, setUserLogViewing] = React.useState({});
const { themeMode, supportEmail, brandColor } = useContext(Context); const { themeMode, supportEmail, brandColor } = useContext(Context);
const theme = getTheme(themeMode, brandColor); const theme = getTheme(themeMode, brandColor);
@@ -1359,7 +1358,7 @@ const UserManagmentTab = memo((props) => {
}} }}
> >
<ListItem style={{ width: "100%", padding: "10px 10px 10px 0px", verticalAlign: 'middle', borderBottom: theme.palette.defaultBorder, display: "table-row" }}> <ListItem style={{ width: "100%", padding: "10px 10px 10px 0px", verticalAlign: 'middle', borderBottom: theme.palette.defaultBorder, display: "table-row" }}>
{["Region", "Username", /*"API Key",*/ "Role", /*"Active",*/ "Type", "MFA", ...(selectedOrganization?.child_orgs?.length > 0 ? ["Suborgs"]: []), "Actions", "Last Login"].map((header, index) => ( {[...(isCloud ? ["Region"] : []), "Username", /*"API Key",*/ "Role", /*"Active",*/ "Type", "MFA", ...(selectedOrganization?.child_orgs?.length > 0 ? ["Suborgs"]: []), "Actions", "Last Login"].map((header, index) => (
<ListItemText <ListItemText
key={index} key={index}
primary={header} primary={header}
@@ -1384,7 +1383,7 @@ const UserManagmentTab = memo((props) => {
backgroundColor: theme.palette.platformColor, backgroundColor: theme.palette.platformColor,
}} }}
> >
{Array(9) {Array(isCloud ? 7 : 6)
.fill() .fill()
.map((_, colIndex) => ( .map((_, colIndex) => (
<ListItemText <ListItemText
@@ -1485,14 +1484,15 @@ const UserManagmentTab = memo((props) => {
return regionCode; return regionCode;
}; };
const userRegion = data?.user_geo_info?.country?.iso_code?.length > 0 ? data?.user_geo_info?.country?.iso_code : data?.active_org?.region_url?.length > 0 ? getRegionFlag(data?.active_org?.region_url) : "eu"; const userRegion = data?.user_geo_info?.country?.iso_code?.length > 0 ? data?.user_geo_info?.country?.iso_code : selectedOrganization?.region_url?.length > 0 ? getRegionFlag(selectedOrganization?.region_url) : "eu";
return ( return (
<ListItem key={index} style={{ backgroundColor: bgColor, display: 'table-row', borderBottomLeftRadius: users?.length - 1 === index ? 8 : 0, borderBottomRightRadius: users?.length - 1 === index ? 8 : 0 }}> <ListItem key={index} style={{ backgroundColor: bgColor, display: 'table-row', borderBottomLeftRadius: users?.length - 1 === index ? 8 : 0, borderBottomRightRadius: users?.length - 1 === index ? 8 : 0 }}>
<ListItemText {isCloud ? (
<ListItemText
primary={(<img src={`https://flagcdn.com/48x36/${userRegion.toLowerCase()}.png`} alt={data?.user_geo_info?.country?.iso_code} style={{ marginRight: 30, width: 25, height: 23, }} />)} primary={(<img src={`https://flagcdn.com/48x36/${userRegion.toLowerCase()}.png`} alt={data?.user_geo_info?.country?.iso_code} style={{ marginRight: 30, width: 25, height: 23, }} />)}
style={{ display: 'table-cell', verticalAlign: 'middle', textAlign: 'center' }} style={{ display: 'table-cell', verticalAlign: 'middle', textAlign: 'center' }}
/> />) : null}
<ListItemText <ListItemText
primary={( primary={(
<Tooltip title={data.username || 'No username available'}> <Tooltip title={data.username || 'No username available'}>
+55 -7
View File
@@ -31,6 +31,13 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle
const [roleRequired, setRoleRequired] = React.useState(selectedOrganization?.sso_config?.role_required || false); const [roleRequired, setRoleRequired] = React.useState(selectedOrganization?.sso_config?.role_required || false);
const [showOpenIdCred, setShowOpenIdCred] = React.useState(false); const [showOpenIdCred, setShowOpenIdCred] = React.useState(false);
const [showSamlCred, setShowSamlCred] = React.useState(false); const [showSamlCred, setShowSamlCred] = React.useState(false);
const [skipSSOForAdmin, setSkipSSOForAdmin] = React.useState(
selectedOrganization?.sso_config === undefined
? false
: selectedOrganization?.sso_config?.skip_sso_for_admins === undefined
? false
: selectedOrganization?.sso_config?.skip_sso_for_admins
);
const [ssoEntrypoint, setSsoEntrypoint] = React.useState( const [ssoEntrypoint, setSsoEntrypoint] = React.useState(
selectedOrganization.sso_config === undefined selectedOrganization.sso_config === undefined
? "" ? ""
@@ -127,6 +134,10 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle
setRoleRequired(selectedOrganization?.sso_config?.role_required) setRoleRequired(selectedOrganization?.sso_config?.role_required)
} }
if (skipSSOForAdmin !== selectedOrganization?.sso_config?.skip_sso_for_admins) {
setSkipSSOForAdmin(selectedOrganization?.sso_config?.skip_sso_for_admins)
}
},[selectedOrganization]) },[selectedOrganization])
const orgSaveButton = ( const orgSaveButton = (
@@ -170,9 +181,14 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle
SSORequired: SSORequired, SSORequired: SSORequired,
auto_provision: autoPrivision, auto_provision: autoPrivision,
role_required: roleRequired, role_required: roleRequired,
} skip_sso_for_admins: skipSSOForAdmin,
},
undefined,
{},
"sso_config"
) )
} }
> >
Save Changes Save Changes
{/* <SaveIcon /> */} {/* <SaveIcon /> */}
@@ -206,12 +222,9 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle
openidAuthorization === "" && openidAuthorization === "" &&
openidToken === "" openidToken === ""
) { ) {
if (!autoPrivision) { toast.error(
toast.error( "Please fill in fields for either OpenID connect or SSO before continuing. "
"Please fill in fields for either OpenID connect or SSO before continuing. " );
);
return;
}
} else { } else {
setAutoProvision((prev)=> !prev); setAutoProvision((prev)=> !prev);
toast.info("Toggled Auto Provisioning. Remember to save."); toast.info("Toggled Auto Provisioning. Remember to save.");
@@ -233,6 +246,12 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle
} }
} }
const handlechangeSkipSSOForAdmin = (event) => {
setSkipSSOForAdmin((prev) => !prev);
toast.info("Toggled Skip SSO for Admins. Remember to save.");
};
const HandleTestSSO = () => { const HandleTestSSO = () => {
const url = `${globalUrl}/api/v1/orgs/${selectedOrganization?.id}/change`; const url = `${globalUrl}/api/v1/orgs/${selectedOrganization?.id}/change`;
const data = { const data = {
@@ -385,6 +404,35 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle
</div> </div>
</div> </div>
{userdata && userdata?.active_org?.creator_org?.length > 0 ? null :
<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 }}
>
Skip SSO for Admins. When enabled, parent org admins will be able switch to the sub-organization without SSO.
</Typography>
<div>
<Switch
checked={skipSSOForAdmin}
onChange={handlechangeSkipSSOForAdmin}
sx={{marginBottom: 0.6, marginTop: 0.6}}
name="onOffSwitch"
color="primary"
title="Disable auto-provisioning of users in SSO"
/>
</div>
</div>
}
<div <div
style={{ style={{
+3 -1
View File
@@ -237,7 +237,8 @@ const Admin2 = (props) => {
defaults, defaults,
sso_config, sso_config,
lead_info, lead_info,
{ mfa_required } = {} { mfa_required } = {},
editing,
) => { ) => {
const data = { const data = {
name: name, name: name,
@@ -248,6 +249,7 @@ const Admin2 = (props) => {
sso_config: sso_config, sso_config: sso_config,
lead_info: lead_info, lead_info: lead_info,
mfa_required: mfa_required !== undefined ? mfa_required : selectedOrganization?.mfa_required, mfa_required: mfa_required !== undefined ? mfa_required : selectedOrganization?.mfa_required,
editing: editing?.length > 0 ? editing : "",
}; };
const url = globalUrl + `/api/v1/orgs/${selectedOrganization.id}`; const url = globalUrl + `/api/v1/orgs/${selectedOrganization.id}`;
+34 -7
View File
@@ -1973,7 +1973,7 @@ const AppCreator = (defaultprops) => {
// Saving the app that's been configured. // Saving the app that's been configured.
// Save SAVE app // Save SAVE app
const submitApp = () => { const submitApp = () => {
toast("Uploading and building app " + name); toast.info("Uploading and building app " + name + ". This may take a minute or two.");
setAppBuilding(true); setAppBuilding(true);
setErrorCode(""); setErrorCode("");
@@ -2600,15 +2600,23 @@ const AppCreator = (defaultprops) => {
} }
if (setExtraAuth.length > 0) { if (setExtraAuth.length > 0) {
const invalidfieldnames = ["apikey", "username", "password", "username_basic", "password_basic", "url", "access_token"]
for (let authkey in extraAuth) { for (let authkey in extraAuth) {
const curauth = extraAuth[authkey]; const curauth = extraAuth[authkey];
if (curauth.name.length === 0 || curauth.name.toLowerCase() == "url") { if (curauth.name.length === 0 || curauth.name.toLowerCase() == "url") {
toast("Can't add extra auth with empty name or Name URL"); toast("Can't add extra auth with empty name or Name URL");
setAppBuilding(false); setAppBuilding(false);
return; return;
} }
// Additional comparisonchecks
if (authenticationOption !== "No authentication" && authenticationOption !== "") {
if (invalidfieldnames.includes(curauth.name.toLowerCase())) {
toast.warn("Skipping extra auth field: " + curauth.name + ". This is not valid.")
continue
}
}
data.components.securitySchemes[curauth.name] = { data.components.securitySchemes[curauth.name] = {
type: "apiKey", type: "apiKey",
in: curauth.type, in: curauth.type,
@@ -2662,9 +2670,15 @@ const AppCreator = (defaultprops) => {
if (responseJson.reason !== undefined) { if (responseJson.reason !== undefined) {
setErrorCode(responseJson.reason); setErrorCode(responseJson.reason);
toast.error("Failed to build: " + responseJson.reason, { if (responseJson.details !== undefined && responseJson.details !== null) {
autoClose: 10000 toast.error("Failed to build - contact support@shuffler.io: " + responseJson.details, {
}) autoClose: 60000
})
} else {
toast.error("Failed to build: " + responseJson.reason, {
autoClose: 10000
})
}
} }
} else { } else {
toast.success("Successfully built openapi app! Added job to rebuild it in your hybrid runtime locations (Orborus)."); toast.success("Successfully built openapi app! Added job to rebuild it in your hybrid runtime locations (Orborus).");
@@ -2678,7 +2692,7 @@ const AppCreator = (defaultprops) => {
.catch((error) => { .catch((error) => {
setAppBuilding(false); setAppBuilding(false);
setErrorCode(error.toString()); setErrorCode(error.toString());
toast(error.toString()); toast.error(error.toString());
}); });
}; };
@@ -2810,10 +2824,21 @@ const AppCreator = (defaultprops) => {
margin="normal" margin="normal"
variant="outlined" variant="outlined"
defaultValue={extraAuth[index].name} defaultValue={extraAuth[index].name}
helperText={
<Typography variant="caption" style={{ color: theme.palette.error.main, marginTop: 10, marginBottom: 10, }}>
{extraAuth[index]?.name?.toLowerCase() === "url"
|| extraAuth[index]?.name?.toLowerCase() === "apikey"
? `ERROR: Invalid key: ${extraAuth[index].name}. ` : ""}
</Typography>
}
onChange={(e) => { onChange={(e) => {
extraAuth[index].name = e.target.value; extraAuth[index].name = e.target.value;
setExtraAuth(extraAuth); setExtraAuth(extraAuth);
}} }}
onBlur={(e) => {
// Forcerender
setUpdate(Math.random());
}}
InputProps={{ InputProps={{
classes: { classes: {
notchedOutline: classes.notchedOutline, notchedOutline: classes.notchedOutline,
@@ -5865,7 +5890,9 @@ const AppCreator = (defaultprops) => {
</h2> </h2>
</Link> </Link>
<h2> <h2>
{name}{" "} <Link to={props?.match?.params?.appid !== undefined && props?.match?.params?.appid !== null && props?.match?.params?.appid.length > 0 ? `/apps/${props?.match?.params?.appid}` : "/apps" } style={{ textDecoration: "none", color: theme.palette.text.primary }}>
{name}{" "}
</Link>
{actions === null || {actions === null ||
actions === undefined || actions === undefined ||
actions.length === 0 ? null : ( actions.length === 0 ? null : (
+12 -12
View File
@@ -3121,12 +3121,12 @@ const buttonBackground = "linear-gradient(to right, #f86a3e, #f34079)";
style={{ marginBottom: 0, marginLeft: 0, marginRight: 0, minWidth: 800, maxWidth: 800, margin: "auto", }} style={{ marginBottom: 0, marginLeft: 0, marginRight: 0, minWidth: 800, maxWidth: 800, margin: "auto", }}
aria-label="disabled tabs example" aria-label="disabled tabs example"
> >
<Tab style={{marginLeft: 0, color: theme.palette.text.primary }} icon={<DescriptionIcon />} label="Docs" /> <Tab style={{marginLeft: 0, color: theme.palette.text.primary, textTransform: "none",}} icon={<DescriptionIcon />} label="Docs" />
<Tab icon={appType === 0 || appType === 2 ? <OpenInNewIcon /> : <AppsIcon />} label={appType === 0 || appType === 2 ? "Explore the API" : "Try it out"} /> <Tab style={{color: theme.palette.text.primary, textTransform: "none", }} icon={appType === 0 || appType === 2 ? <OpenInNewIcon /> : <AppsIcon />} label={appType === 0 || appType === 2 ? "Try the API" : "Try it out"} />
<Tab icon={<ShowChartIcon />} style={{color: theme.palette.text.primary}} label="Stats" /> <Tab icon={<ShowChartIcon />} style={{color: theme.palette.text.primary, textTransform: "none", }} label="Stats & Downloads" />
<Tab icon={<PolylineIcon />} disabled style={{color: theme.palette.text.secondary}} label="Integrations" /> <Tab icon={<PolylineIcon />} style={{color: theme.palette.text.primary, textTransform: "none", }} 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} /> <Tab icon={<PersonIcon />} disabled={userdata.support !== true} style={{color: userdata.support !== true ? theme.palette.text.secondary: theme.palette.text.primary, textTransform: "none", }} label="Creator" value={4} />
</Tabs> </Tabs>
<div style={{ marginTop: 25 }}> <div style={{ marginTop: 25 }}>
{selectedTab === 1 && app.skipped_build == false ? ( {selectedTab === 1 && app.skipped_build == false ? (
@@ -3135,8 +3135,8 @@ const buttonBackground = "linear-gradient(to right, #f86a3e, #f34079)";
<SelectedActionView action={currentAction} /> <SelectedActionView action={currentAction} />
</div> </div>
<div style={{ marginLeft: 25, maxWidth: 350 }}> <div style={{ marginLeft: 25, maxWidth: 350 }}>
{currentAction.description !== undefined && currentAction.description !== null && currentAction.description !== "" ? {currentAction.description !== undefined && currentAction.description !== null && currentAction.description !== "" ?
<div <div
style={{ style={{
maxHeight: 175, maxHeight: 175,
overflowX: "hidden", overflowX: "hidden",
@@ -3253,11 +3253,11 @@ const buttonBackground = "linear-gradient(to right, #f86a3e, #f34079)";
Use the App onprem (hybrid) Use the App onprem (hybrid)
</Typography> </Typography>
<Typography variant="body2" color="textSecondary" style={{marginTop: 5, }}> <Typography variant="body2" color="textSecondary" style={{marginTop: 5, }}>
Due to using docker containers with privately uploaded containers, we had to use a custom registry. Use the command below to download the image to the server if it fails to run. Custom uses a custom Docker registry for private containers. Use the command below to download the app image to a server if it fails to do so automatically.
It will authenticate and authorize you, before redirecting to a Signed URL on https://storage.googleapis.com The downloadable image may not be ready until 5 minutes after the app was built.
<b>&nbsp;Now also works for ARM containers!</b> <b>&nbsp;Now also works for ARM containers (?arch=arm)!</b>
</Typography> </Typography>
<div <div
@@ -4027,7 +4027,7 @@ const buttonBackground = "linear-gradient(to right, #f86a3e, #f34079)";
</IconButton> </IconButton>
) : null} ) : null}
{selectedOrganization !== undefined && selectedOrganization !== null && selectedOrganization.id !== undefined && userdata.support === true ? {selectedOrganization !== undefined && selectedOrganization !== null && selectedOrganization.id !== undefined ?
// Iconbutton for authentication with just an icon. Link to /apps/authentication?app_id=app.id // Iconbutton for authentication with just an icon. Link to /apps/authentication?app_id=app.id
<IconButton <IconButton
color="secondary" color="secondary"
@@ -4165,7 +4165,7 @@ const buttonBackground = "linear-gradient(to right, #f86a3e, #f34079)";
style={{ height: 40, marginTop: 5, marginLeft: 5, }} style={{ height: 40, marginTop: 5, marginLeft: 5, }}
> >
<OpenInNewIcon style={{marginRight: 5, }}/> <OpenInNewIcon style={{marginRight: 5, }}/>
Explore API Try the API
</Button> </Button>
</a> </a>
<Select <Select
@@ -191,6 +191,7 @@ const SetAuthentication = (props) => {
appAuthentication={appAuthentication} />} appAuthentication={appAuthentication} />}
</Typography> </Typography>
{/*
<Typography variant="h6" style={{ marginTop: 50, marginBottom: 20, }}> <Typography variant="h6" style={{ marginTop: 50, marginBottom: 20, }}>
What can they do with this? What can they do with this?
</Typography> </Typography>
@@ -217,6 +218,7 @@ const SetAuthentication = (props) => {
</div> </div>
</div> </div>
</Typography> </Typography>
*/}
</div> </div>
<> <>
<div style={{ height: 100, }}></div> <div style={{ height: 100, }}></div>