diff --git a/backend/go-app/main.go b/backend/go-app/main.go index acfde3c4..59a4ce31 100755 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -1131,9 +1131,9 @@ func checkAdminLogin(resp http.ResponseWriter, request *http.Request) { } // No childorg setup, only parent org - if len(org.ManagerOrgs) > 0 || len(org.CreatorOrg) > 0 { - continue - } + // if len(org.ManagerOrgs) > 0 || len(org.CreatorOrg) > 0 { + // continue + // } // Should run calculations if len(org.SSOConfig.OpenIdAuthorization) > 0 { diff --git a/frontend/src/components/Billing.jsx b/frontend/src/components/Billing.jsx index d5b39411..ef6f350e 100644 --- a/frontend/src/components/Billing.jsx +++ b/frontend/src/components/Billing.jsx @@ -42,6 +42,7 @@ import { Delete, RestaurantRounded, Cloud, + CheckCircle } from "@mui/icons-material"; //import { useAlert @@ -71,6 +72,7 @@ const Billing = (props) => { const [currentAppRunsInNumber, setCurrentAppRunsInNumber] = useState(0); const [alertThresholds, setAlertThresholds] = useState(selectedOrganization.Billing !== undefined && selectedOrganization.Billing.AlertThreshold !== undefined && selectedOrganization.Billing.AlertThreshold !== null ? selectedOrganization.Billing.AlertThreshold : [{ percentage: '', count: '', Email_send: false }]); const [currentIndex, setCurrentIndex] = useState(0); + const [deleteAlertVerification, setDeleteAlertVerification] = useState(false); useEffect(() => { if (userdata.app_execution_limit !== undefined && userdata.app_execution_usage !== undefined) { @@ -353,13 +355,13 @@ const Billing = (props) => { if (subscription.name === "Enterprise" && subscription.active === true) { top_text = "Current Plan" - newPaperstyle.border = "1px solid #f85a3e" + // newPaperstyle.border = "1px solid #f85a3e" } var showSupport = false if (subscription.name.includes("default")) { top_text = "Custom Contract" - newPaperstyle.border = "1px solid #f85a3e" + newPaperstyle.border = "1px solid rgba(255,255,255,0.3)" showSupport = true } @@ -379,7 +381,7 @@ const Billing = (props) => { if (highlight === true) { // Add an "Upgrade now" button - newPaperstyle.border = "1px solid #f85a3e" + newPaperstyle.border = "1px solid rgba(255,255,255,0.3)" } if (hovered) { @@ -820,7 +822,8 @@ const Billing = (props) => { height: 40, fontSize: 16, color: "white", - backgroundImage: userdata.has_card_available ? null : "linear-gradient(to right, #f86a3e, #f34079)", + backgroundColor: userdata.has_card_available ? null : "#f86743", + // backgroundImage: userdata.has_card_available ? null : "linear-gradient(to right, #f86a3e, #f34079)", textTransform: "none", }} @@ -852,7 +855,7 @@ const Billing = (props) => { height: 40, fontSize: 16, color: "white", - backgroundImage: "linear-gradient(to right, #f86a3e, #f34079)", + backgroundColor: "#f86743", textTransform: 'none' }} onClick={() => { @@ -1036,11 +1039,11 @@ const Billing = (props) => { { height: 40, fontSize: 16, color: "white", - backgroundImage: "linear-gradient(to right, #f86a3e, #f34079)", - textTransform: 'none' + backgroundColor: "#f86743", + textTransform: 'none', }} onClick={() => { if (Cloud) { @@ -1188,7 +1191,7 @@ const Billing = (props) => { height: 40, fontSize: 16, color: "white", - backgroundImage: "linear-gradient(to right, #f86a3e, #f34079)", + backgroundColor: "#f86743", textTransform: 'none', cursor: getProfessionalServices ? 'pointer' : 'not-allowed', opacity: getProfessionalServices ? 1 : 0.6, @@ -1315,7 +1318,7 @@ const Billing = (props) => { width: 340, backgroundColor: hovered ? "#232427" : theme.palette.platformColor, borderRadius: theme.palette.borderRadius * 2, - border: "1px solid #f85a3e", + border: "1px solid rgba(255,255,255,0.3)", marginRight: 10, marginTop: 15, }} @@ -1373,7 +1376,7 @@ const Billing = (props) => { height: 40, fontSize: 16, color: "white", - backgroundImage: "linear-gradient(to right, #f86a3e, #f34079)", + backgroundColor: "#f86743", textTransform: 'none' }} onClick={() => { @@ -1381,6 +1384,8 @@ const Billing = (props) => { ReactGA.event({ category: "Billing", action: "click_public_training_button", + label: "Public Training", + userId: userdata?.id }); } navigate("/training") @@ -1399,21 +1404,24 @@ const Billing = (props) => { height: 40, fontSize: 16, color: "white", - backgroundImage: "linear-gradient(to right, #f86a3e, #f34079)", + backgroundColor: "#f86743", textTransform: 'none' }} onClick={() => { if (Cloud) { ReactGA.event({ category: "Billing", - action: "click_public_training_button", + action: "click_private_training_button", + label: "Private Training", + userId: userdata?.id, }); } - setOpenPrivateTraining(true) + setOpenPrivateTraining(true); }} > Private Training + setOpenPrivateTraining(false)} fullWidth @@ -1818,6 +1826,7 @@ const Billing = (props) => { // Update currentIndex based on remaining elements const findCurrentIndex = newAlertThresholds.some(threshold => threshold.Email_send === false); setCurrentIndex(findCurrentIndex ? newAlertThresholds.findIndex(threshold => threshold.Email_send === false) : - 1); + toast.info("Alert Threshold deleted successfully. Don't forget to save your changes."); }; const HandleEditOrgForAlertThreshold = (orgId) => { @@ -1997,15 +2006,6 @@ const Billing = (props) => { /> : null} - {isCloud && billingInfo.subscription !== undefined && billingInfo.subscription !== null ? isChildOrg ? null : - : null} - - - {isCloud && selectedOrganization.subscriptions !== undefined && @@ -2261,7 +2261,29 @@ const Billing = (props) => { ) : null*/} -
+ {!isChildOrg && isCloud && ( +
+ + Professional Services + + + 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. + +
+ {billingInfo.subscription !== undefined && billingInfo.subscription !== null ? ( + isChildOrg ? null : ( + + ) + ) : null} + +
+
+ )} +
{ : " " + 0 + " "} app runs. - + + Please note: Once your app runs reach the set alert threshold, all admins in the organization will receive an email notification. +
{alertThresholds.map((threshold, index) => (
@@ -2354,6 +2378,7 @@ const Billing = (props) => { margin="normal" variant="outlined" /> + {alertThresholds[index].Email_send === true && } { alertThresholds.length > 1 && ( @@ -2362,18 +2387,24 @@ const Billing = (props) => { disableElevation sx={{ padding: 0, - color: 'red', '&:hover': { backgroundColor: 'transparent', }, }} - onClick={() => { handleDeleteAlertThreshold(index) }} + onClick={() => { setDeleteAlertVerification(true) }} > - + ) } + setDeleteAlertVerification(false)} sx={{ '& .MuiBackdrop-root': { backgroundColor: 'rgba(0, 0, 0, 0.3)', }, }}> + Are you sure you want to delete this threshold? + + + + +
))}
@@ -2415,4 +2446,4 @@ const Billing = (props) => { ) } -export default Billing; +export default Billing; \ No newline at end of file diff --git a/frontend/src/components/NewHeader.jsx b/frontend/src/components/NewHeader.jsx index 3dc07460..e5ce563b 100644 --- a/frontend/src/components/NewHeader.jsx +++ b/frontend/src/components/NewHeader.jsx @@ -296,38 +296,50 @@ const Header = (props) => { if (response.status !== 200) { console.log("Error in response"); } else { - localStorage.removeItem("apps") - localStorage.removeItem("workflows") - localStorage.removeItem("userinfo") + localStorage.removeItem("apps"); + localStorage.removeItem("workflows"); + localStorage.removeItem("userinfo"); } return response.json(); }) .then(function (responseJson) { - console.log("In here?") + console.log("In here?"); if (responseJson.success === true) { - if (responseJson.region_url !== undefined && responseJson.region_url !== null && responseJson.region_url.length > 0) { + if ( + responseJson.region_url !== undefined && + responseJson.region_url !== null && + responseJson.region_url.length > 0 + ) { console.log("Region Change: ", responseJson.region_url); localStorage.setItem("globalUrl", responseJson.region_url); //globalUrl = responseJson.region_url } if (responseJson["reason"] === "SSO_REDIRECT") { setTimeout(() => { - toast.info("Redirecting to SSO login page as SSO is required for this organization.") - window.location.href = responseJson["url"] - return - }, 2000) + toast.info( + "Redirecting to SSO login page as SSO is required for this organization." + ); + window.location.href = responseJson["url"]; + return; + }, 2000); } else { + toast("Successfully changed active organization - refreshing!"); setTimeout(() => { - window.location.reload() + window.location.reload(); }, 2000); } - toast("Successfully changed active organization - refreshing!"); } else { - if (responseJson.reason !== undefined && responseJson.reason !== null && responseJson.reason.length > 0) { + if ( + responseJson.reason !== undefined && + responseJson.reason !== null && + responseJson.reason.length > 0 + ) { toast(responseJson.reason); } else { - toast("Failed changing org. Try again or contact support@shuffler.io if this persists."); + toast( + "Failed changing org. Try again or contact support@shuffler.io if this persists." + ); } } }) diff --git a/frontend/src/components/OrgHeaderexpanded.jsx b/frontend/src/components/OrgHeaderexpanded.jsx index 58d1a244..009f0d20 100644 --- a/frontend/src/components/OrgHeaderexpanded.jsx +++ b/frontend/src/components/OrgHeaderexpanded.jsx @@ -319,9 +319,71 @@ const OrgHeaderexpanded = (props) => { ); - const toggleBetweenRequiredOrOptional = (event) => { - setSSORequired(event.target.checked); - }; + const toggleBetweenRequiredOrOptional = (event) => { + if ( + ssoEntrypoint === "" && + openidAuthorization === "" && + openidToken === "" + ) { + if (!SSORequired) { + toast.error( + "Please fill in fields for either OpenID connect or SSO before continuing. " + ); + return; + } + } else { + toast.info("Toggled SSO. Remember to save."); + } + + setSSORequired(event.target.checked); + }; + + const HandleTestSSO = () => { + const url = `${globalUrl}/api/v1/orgs/${selectedOrganization?.id}/change`; + const data = { + org_id: selectedOrganization?.id, + sso_test: true, + }; + + fetch(url, { + mode: "cors", + credentials: "include", + crossDomain: true, + method: "POST", + body: JSON.stringify(data), + withCredentials: true, + headers: { + "Content-Type": "application/json; charset=utf-8", + }, + }) + .then((response) => { + if (response.status !== 200) { + toast.error( + "Failed to test sso. Please try again later or contact support@shuffler.io if issue persist." + ); + return; + } + return response.json(); + }) + .then((responjson) => { + if (responjson["reason"] === "SSO_REDIRECT") { + setTimeout(() => { + toast.info( + "Redirecting to SSO login page as SSO is required for this organization." + ); + window.location.href = responjson["url"]; + return; + }, 2000); + } else { + toast.error( + "No SSO found for this org. Please set up sso for this org." + ); + } + }) + .catch((err) => { + console.log("error for sso test is: ", err); + }); + }; return (
@@ -664,398 +726,476 @@ const OrgHeaderexpanded = (props) => { SSO Configuration -
- Make SAML SSO or OpenID Authentication Required or Optional for Your Organization. -
- - {SSORequired ? 'Required' : 'Optional'} -
-
- - OpenID connect - - - - Client ID - 0 - } - id="outlined-with-placeholder" - margin="normal" - variant="outlined" - placeholder="The OpenID client ID from the identity provider" - value={openidClientId} - onChange={(e) => { - setOpenidClientId(e.target.value); - }} - InputProps={{ - classes: { - notchedOutline: classes.notchedOutline, - }, - style: { - color: "white", - }, - }} - /> - - - - - Client Secret (optional) - 0 - } - id="outlined-with-placeholder" - margin="normal" - variant="outlined" - placeholder="The OpenID client secret - DONT use this if dealing with implicit auth / PKCE" - value={openidClientSecret} - onChange={(e) => { - setOpenidClientSecret(e.target.value); - }} - InputProps={{ - classes: { - notchedOutline: classes.notchedOutline, - }, - style: { - color: "white", - }, - }} - /> - - - - - - - Authorization URL - { - setOpenidAuthorization(e.target.value) - }} - InputProps={{ - classes: { - notchedOutline: classes.notchedOutline, - }, - style: { - color: "white", - }, - }} - /> - - - - - Token URL - { - setOpenidToken(e.target.value) - }} - InputProps={{ - classes: { - notchedOutline: classes.notchedOutline, - }, - style: { - color: "white", - }, - }} - /> - - - - - {/* } */} - {/*isCloud ? null : */} - - SAML SSO (v1.1) - - - - SSO Entrypoint (IdP) - 0 - } - id="outlined-with-placeholder" - margin="normal" - variant="outlined" - placeholder="The entrypoint URL from your provider" - value={ssoEntrypoint} - onChange={(e) => { - setSsoEntrypoint(e.target.value); - }} - InputProps={{ - classes: { - notchedOutline: classes.notchedOutline, - }, - style: { - color: "white", - }, - }} - /> - - - - - SSO Certificate (X509) - { - setSsoCertificate(e.target.value); - }} - InputProps={{ - classes: { - notchedOutline: classes.notchedOutline, - }, - style: { - color: "white", - }, - }} - /> - - - - {isCloud ? - - IdP URL for Shuffle: https://shuffler.io/api/v1/login_sso - - : null} - - {isCloud ? null : ( - - - App Download URL - { - setAppDownloadUrl(e.target.value); - }} - InputProps={{ - classes: { - notchedOutline: classes.notchedOutline, - }, - style: { - color: "white", - }, - }} - /> - - - )} - {isCloud ? null : ( - - - App Download Branch - { - setAppDownloadBranch(e.target.value); - }} - InputProps={{ - classes: { - notchedOutline: classes.notchedOutline, - }, - style: { - color: "white", - }, - }} - /> - - - )} - {isCloud ? null : ( - - - Workflow Download URL - { - setWorkflowDownloadUrl(e.target.value); - }} - InputProps={{ - classes: { - notchedOutline: classes.notchedOutline, - }, - style: { - color: "white", - }, - }} - /> - - - )} - {isCloud ? null : ( - - - Workflow Download Branch - { - setWorkflowDownloadBranch(e.target.value); - }} - InputProps={{ - classes: { - notchedOutline: classes.notchedOutline, - }, - style: { - color: "white", - }, - }} - /> - - - )} +
+ + Make SAML SSO or OpenID Authentication Required or Optional for Your + Organization. + +
+ + {SSORequired ? "Required" : "Optional"} +
+
+
+ + You can test your SSO configuration by clicking the button below. + Before testing, ensure you have set Open ID Connect or SAML SSO + credentials. + + 0 || + ssoCertificate.length > 0 || + openidAuthorization.length > 0 || + openidClientId.length > 0 + ) + ? "Please ensure all SSO credentials are set before testing." + : "" + } + > + + + + +
+
+ + + OpenID connect + + + + + Client ID + 0 + } + id="outlined-with-placeholder" + margin="normal" + variant="outlined" + placeholder="The OpenID client ID from the identity provider" + value={openidClientId} + onChange={(e) => { + setOpenidClientId(e.target.value); + }} + InputProps={{ + classes: { + notchedOutline: classes.notchedOutline, + }, + style: { + color: "white", + }, + }} + /> + + + + + Client Secret (optional) + 0 + } + id="outlined-with-placeholder" + margin="normal" + variant="outlined" + placeholder="The OpenID client secret - DONT use this if dealing with implicit auth / PKCE" + value={openidClientSecret} + onChange={(e) => { + setOpenidClientSecret(e.target.value); + }} + InputProps={{ + classes: { + notchedOutline: classes.notchedOutline, + }, + style: { + color: "white", + }, + }} + /> + + + + + + + Authorization URL + { + setOpenidAuthorization(e.target.value); + }} + InputProps={{ + classes: { + notchedOutline: classes.notchedOutline, + }, + style: { + color: "white", + }, + }} + /> + + + + + Token URL + { + setOpenidToken(e.target.value); + }} + InputProps={{ + classes: { + notchedOutline: classes.notchedOutline, + }, + style: { + color: "white", + }, + }} + /> + + + + + {/* } */} + {/*isCloud ? null : */} + + + SAML SSO (v1.1) + + + + + SSO Entrypoint (IdP) + 0 + } + id="outlined-with-placeholder" + margin="normal" + variant="outlined" + placeholder="The entrypoint URL from your provider" + value={ssoEntrypoint} + onChange={(e) => { + setSsoEntrypoint(e.target.value); + }} + InputProps={{ + classes: { + notchedOutline: classes.notchedOutline, + }, + style: { + color: "white", + }, + }} + /> + + + + + SSO Certificate (X509) + { + setSsoCertificate(e.target.value); + }} + InputProps={{ + classes: { + notchedOutline: classes.notchedOutline, + }, + style: { + color: "white", + }, + }} + /> + + + + {isCloud ? ( + + IdP URL for Shuffle: https://shuffler.io/api/v1/login_sso + + ) : null} + + {isCloud ? null : ( + + + App Download URL + { + setAppDownloadUrl(e.target.value); + }} + InputProps={{ + classes: { + notchedOutline: classes.notchedOutline, + }, + style: { + color: "white", + }, + }} + /> + + + )} + {isCloud ? null : ( + + + App Download Branch + { + setAppDownloadBranch(e.target.value); + }} + InputProps={{ + classes: { + notchedOutline: classes.notchedOutline, + }, + style: { + color: "white", + }, + }} + /> + + + )} + {isCloud ? null : ( + + + Workflow Download URL + { + setWorkflowDownloadUrl(e.target.value); + }} + InputProps={{ + classes: { + notchedOutline: classes.notchedOutline, + }, + style: { + color: "white", + }, + }} + /> + + + )} + {isCloud ? null : ( + + + Workflow Download Branch + { + setWorkflowDownloadBranch(e.target.value); + }} + InputProps={{ + classes: { + notchedOutline: classes.notchedOutline, + }, + style: { + color: "white", + }, + }} + /> + + + )} -
- {orgSaveButton} -
- {/* +
+ {orgSaveButton} +
+ {/* {expanded ? @@ -1064,9 +1204,9 @@ const OrgHeaderexpanded = (props) => { } */} - -
- ) -} + +
+ ); +}; export default OrgHeaderexpanded;