diff --git a/portal-ui/src/screens/Console/License/License.tsx b/portal-ui/src/screens/Console/License/License.tsx index 2436326b8..6bd797739 100644 --- a/portal-ui/src/screens/Console/License/License.tsx +++ b/portal-ui/src/screens/Console/License/License.tsx @@ -19,16 +19,13 @@ import { connect } from "react-redux"; import { Theme } from "@mui/material/styles"; import createStyles from "@mui/styles/createStyles"; import withStyles from "@mui/styles/withStyles"; -import { CircularProgress, LinearProgress } from "@mui/material"; +import {Box, LinearProgress} from "@mui/material"; import clsx from "clsx"; import Grid from "@mui/material/Grid"; import Button from "@mui/material/Button"; -import Moment from "react-moment"; import Typography from "@mui/material/Typography"; import { SubnetInfo } from "./types"; import { AppState } from "../../../store"; -import { niceBytes } from "../../../common/utils"; -import { ErrorResponseHandler } from "../../../common/types"; import { containerForHeader } from "../Common/FormComponents/common/styleLibrary"; import PageHeader from "../Common/PageHeader/PageHeader"; import LicenseModal from "./LicenseModal"; @@ -43,6 +40,7 @@ import { import RegisterStatus from "../Support/RegisterStatus"; import LicensePlans from "./LicensePlans"; import { Link } from "react-router-dom"; +import PageLayout from "../Common/Layout/PageLayout"; const mapState = (state: AppState) => ({ operatorMode: state.system.operatorMode, @@ -84,29 +82,6 @@ const styles = (theme: Theme) => color: "#000000", fontSize: 14, }, - licenseContainer: { - display: "flex", - flexWrap: "wrap", - flexDirection: "row", - padding: "30px 30px 0px 30px", - border: "1px solid #EAEDEE", - "& h2": { - // color: "#FFF", - flexDirection: "row", - }, - "& a": { - textDecoration: "none", - flexDirection: "row", - }, - "& h3": { - // color: "#FFFFFF", - marginBottom: "30px", - fontWeight: "bold", - }, - "& h6": { - // color: "#FFFFFF !important", - }, - }, link: { textDecoration: "underline !important", color: theme.palette.info.main, @@ -121,26 +96,11 @@ const styles = (theme: Theme) => margin: 0, }, - button: { - textTransform: "none", - fontSize: 15, - fontWeight: 700, - }, openSourcePolicy: { fontSize: 14, color: "#1C5A8D", fontWeight: "bold", }, - subnetRefreshLicenseLink: { - color: "#1C5A8D", - fontWeight: "bold", - clear: "both", - background: "none", - border: "none", - textDecoration: "underline", - cursor: "pointer", - fontSize: 13, - }, licenseInfo: { position: "relative", }, @@ -201,7 +161,6 @@ const License = ({ classes, operatorMode }: ILicenseProps) => { const [loadingLicenseInfo, setLoadingLicenseInfo] = useState(false); const [initialLicenseLoading, setInitialLicenseLoading] = useState(true); - const [loadingRefreshLicense, setLoadingRefreshLicense] = useState(false); const [clusterRegistered, setClusterRegistered] = useState(false); @@ -247,27 +206,6 @@ const License = ({ classes, operatorMode }: ILicenseProps) => { } }, [loadingLicenseInfo, getSubnetInfo]); - const refreshLicense = () => { - setLoadingRefreshLicense(true); - api - .invoke("POST", `/api/v1/subscription/refresh`, {}) - .then((res: SubnetInfo) => { - if (res) { - if (res.plan === "STANDARD") { - setCurrentPlanID(1); - } else if (res.plan === "ENTERPRISE") { - setCurrentPlanID(2); - } else { - setCurrentPlanID(1); - } - setLicenseInfo(res); - } - setLoadingRefreshLicense(false); - }) - .catch((err: ErrorResponseHandler) => { - setLoadingRefreshLicense(false); - }); - }; useEffect(() => { if (initialLicenseLoading) { @@ -284,329 +222,193 @@ const License = ({ classes, operatorMode }: ILicenseProps) => { ); } + const isRegistered = licenseInfo && clusterRegistered + return ( - - - + + + + -
- - {licenseInfo && } - {!clusterRegistered && ( - - - - GNU Affero General Public License - - - - - {licenseInfo ? ( - - - - - License - - - Commercial License - - - Organization - - - {licenseInfo.organization} - - - Registered Capacity - - - {niceBytes( - (licenseInfo.storage_capacity * 1099511627776) // 1 Terabyte = 1099511627776 Bytes - .toString(10), - false - )} - - - - - Subscription Plan - - - {licenseInfo.plan} - - - Requestor - - - {licenseInfo.email} - - - Expiry Date - - - - {licenseInfo.expires_at - .split(" ") - .slice(0, 1) - .join(" ")} - - - - verified - - - ) : ( - - setLicenseModal(false)} - /> - - - agpl{" "} - - - Version 3. 19 November 2007{" "} - - + {isRegistered && } + {!isRegistered && ( - - - The GNU Affero General Public License is a free, - copyleft license for software and other kinds of - works, specifically designed to ensure cooperation - with the Community in the case of network server - software. - -
- - The licenses for most software and other practical - works are designed to take away your freedom to - share and change the works. By contrast, our - General Public Licenses are intended to guarantee - your freedom to share and change all versions of a - program--to make sure it remains free software for - all its users. - -
- -
-
-
- )} -
- - {licenseInfo ? ( -
- - Login to MinIO SUBNET ! - - + + Are you already a customer of MinIO? + - It combines a commercial license with a support - experience unlike any other. - -
- - {operatorMode && ( - - {" "} + Register this cluster → + + + +
+ + + Choosing between GNU AGPL v3 and Commercial License +
-
- - {loadingRefreshLicense && ( - - )} - - )} -
- ) : ( -
- - Choosing between GNU AGPL v3 and Commercial License - -
- - If you are building proprietary applications, you - may want to choose the commercial license included - as part of the Standard and Enterprise subscription - plans. Applications must otherwise comply with all - the GNU AGPLv3 License & Trademark obligations. - Follow the links below to learn more about the - compliance policy. - - -
-
- )} - + + If you are building proprietary applications, you + may want to choose the commercial license included + as part of the Standard and Enterprise subscription + plans. Applications must otherwise comply with all + the GNU AGPLv3 License & Trademark obligations. + Follow the links below to learn more about the + compliance policy. + + +
+
+ + + MinIO License and Support plans + + + )} + + + + + + + + + + + setLicenseModal(false)} + /> + + + GNU Affero General Public License + + + + agpl{" "} + + + Version 3. 19 November 2007{" "} + + + + + + + The GNU Affero General Public License is a free, + copyleft license for software and other kinds of + works, specifically designed to ensure cooperation + with the Community in the case of network server + software. + +
+ + The licenses for most software and other practical + works are designed to take away your freedom to + share and change the works. By contrast, our + General Public Licenses are intended to guarantee + your freedom to share and change all versions of a + program--to make sure it remains free software for + all its users. + +
+ +
+
+
+
+
-
- -
-
- - - Are you already a customer? Register Here → - - - -
-
- - )} + + + + + + - - -
-
- ); }; diff --git a/portal-ui/src/screens/Console/License/LicensePlans.tsx b/portal-ui/src/screens/Console/License/LicensePlans.tsx index d95cec38a..5d0f18d0a 100644 --- a/portal-ui/src/screens/Console/License/LicensePlans.tsx +++ b/portal-ui/src/screens/Console/License/LicensePlans.tsx @@ -14,362 +14,674 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -import React, { Fragment } from "react"; -import Grid from "@mui/material/Grid"; +import React, {Fragment, useEffect, useState} from "react"; import clsx from "clsx"; -import ActivationModal from "./ActivationModal"; -import { planButtons, planDetails, planItems } from "./utils"; +import { planDetails, planItems} from "./utils"; import CheckCircleIcon from "@mui/icons-material/CheckCircle"; import Button from "@mui/material/Button"; -import { Theme } from "@mui/material/styles"; +import {Theme, useTheme} from "@mui/material/styles"; import createStyles from "@mui/styles/createStyles"; -import { SubnetInfo } from "./types"; +import {SubnetInfo} from "./types"; import withStyles from "@mui/styles/withStyles"; +import {Box, Tooltip} from "@mui/material"; +import useMediaQuery from "@mui/material/useMediaQuery"; +import HelpIcon from "../../../icons/HelpIcon"; const styles = (theme: Theme) => - createStyles({ - planItemsPadding: { - border: "1px solid #EAEDEE", - borderTop: 0, - maxWidth: 1180, - }, - planItemsBorder: { - height: 7, - backgroundColor: "#07193E", - }, + createStyles({ + planItemsPadding: { + border: "1px solid #EAEDEE", + borderTop: 0, + maxWidth: 1180, + }, + planItemsBorder: { + height: 7, + backgroundColor: "#07193E", + }, - link: { - textDecoration: "underline !important", - color: theme.palette.info.main, - }, - linkButton: { - fontFamily: '"Lato", sans-serif', - fontWeight: "normal", - textTransform: "none", - fontSize: "inherit", - height: 0, - padding: 0, - margin: 0, - }, - tableContainer: { - marginLeft: 28, - }, - detailsContainerBorder: { - borderLeft: "1px solid #e2e2e2", - }, - detailsTitle: { - fontSize: 19, - fontWeight: 700, - marginBottom: 26, - paddingTop: 18, - lineHeight: 1, - }, - currPlan: { - color: "white", - backgroundColor: "#2781B0", - }, - planHeader: { - padding: 8, - }, - detailsPrice: { - fontSize: 13, - fontWeight: 700, - }, - detailsCapacityMax: { - minHeight: 28, - fontSize: 10, - }, - itemContainer: { - height: 36, - "& .item:last-child": { - borderRight: "1px solid #e5e5e5", - }, - }, - itemContainerDetail: { - height: 48, - }, - item: { - height: "100%", - borderLeft: "1px solid #e5e5e5", - textAlign: "center", - fontSize: 10, - fontWeight: 700, - display: "flex", - alignItems: "center", - alignContent: "center", - borderTop: "1px solid #e5e5e5", - }, + link: { + textDecoration: "underline !important", + color: theme.palette.info.main, + }, + linkButton: { + fontFamily: '"Lato", sans-serif', + fontWeight: "normal", + textTransform: "none", + fontSize: "inherit", + height: 0, + padding: 0, + margin: 0, + }, + tableContainer: { + marginLeft: 28, + }, + detailsContainerBorder: { + borderLeft: "1px solid #e2e2e2", + }, + detailsTitle: { + fontSize: 19, + fontWeight: 700, + marginBottom: 26, + paddingTop: 18, + lineHeight: 1, + }, + currPlan: { + color: "white", + backgroundColor: "#2781B0", + }, + planHeader: { + padding: 8, + }, + detailsPrice: { + fontSize: 13, + fontWeight: 700, + }, + detailsCapacityMax: { + minHeight: 28, + fontSize: 10, + }, + itemContainer: { + height: 36, + "& .item:last-child": { + borderRight: "1px solid #e5e5e5", + }, + }, + itemContainerDetail: { + height: 48, + }, + item: { + height: "100%", + borderLeft: "1px solid #e5e5e5", + textAlign: "center", + fontSize: 10, + fontWeight: 700, + display: "flex", + alignItems: "center", + alignContent: "center", + borderTop: "1px solid #e5e5e5", + }, - itemFirst: { - borderLeft: 0, - borderRight: 0, - }, - field: { - textAlign: "left", - fontWeight: 400, - fontSize: 12, - }, - checkIcon: { - fontSize: 15, - color: "#385973", - }, - buttonContainer: { - paddingTop: 8, - paddingBottom: 24, - height: "100%", - display: "flex", - justifyContent: "center", - borderLeft: "1px solid #e2e2e2", - }, - buttonContainerBlank: { - border: 0, - }, - buttonContainerHighlighted: { - borderTop: 0, - }, - button: { - textTransform: "none", - fontSize: 15, - fontWeight: 700, - }, - activateLink: { - color: "#1C5A8D", - fontWeight: "bold", - clear: "both", - background: "none", - border: "none", - textDecoration: "underline", - cursor: "pointer", - }, - currentPlanBG: { - background: "#022A4A 0% 0% no-repeat padding-box", - color: "#FFFFFF", - borderTop: "1px solid #52687d", - }, - }); + itemFirst: { + borderLeft: 0, + borderRight: 0, + }, + field: { + textAlign: "left", + fontWeight: 400, + fontSize: 12, + }, + checkIcon: { + fontSize: 15, + color: "#385973", + }, + buttonContainer: { + paddingTop: 8, + paddingBottom: 24, + height: "100%", + display: "flex", + justifyContent: "center", + borderLeft: "1px solid #e2e2e2", + }, + buttonContainerBlank: { + border: 0, + }, + buttonContainerHighlighted: { + borderTop: 0, + }, + button: { + textTransform: "none", + fontSize: 15, + fontWeight: 700, + }, + activateLink: { + color: "#1C5A8D", + fontWeight: "bold", + clear: "both", + background: "none", + border: "none", + textDecoration: "underline", + cursor: "pointer", + + }, + currentPlanBG: { + background: "#022A4A 0% 0% no-repeat padding-box", + color: "#FFFFFF", + borderTop: "1px solid #52687d", + }, + }); interface IRegisterStatus { - classes: any; - activateProductModal: any; - closeModalAndFetchLicenseInfo: any; - licenseInfo: SubnetInfo | undefined; - setLicenseModal: React.Dispatch>; - operatorMode: boolean; - currentPlanID: number; - setActivateProductModal: any; + classes: any; + activateProductModal: any; + closeModalAndFetchLicenseInfo: any; + licenseInfo: SubnetInfo | undefined; + setLicenseModal: React.Dispatch>; + operatorMode: boolean; + currentPlanID: number; + setActivateProductModal: any; } -const LicensePlans = ({ - classes, - activateProductModal, - closeModalAndFetchLicenseInfo, - licenseInfo, - setLicenseModal, - operatorMode, - currentPlanID, - setActivateProductModal, -}: IRegisterStatus) => { - const planDetailsFiltered = planDetails.filter((item) => { - if (licenseInfo) { - if (item.title === "Community") { - return false; - } - } - return true; - }); - const planButtonsFiltered = planButtons.filter((item) => { - if (licenseInfo) { - if (item.plan === "Community") { - return false; - } - } - return true; - }); +const formatLabel = (label = "", isLink = false, isSmallScreen = false, setLicenseModal:any) => { - const gridColWidth = licenseInfo ? 4 : 3; - - return ( - - -
- - - - closeModalAndFetchLicenseInfo()} - /> - - - - {planDetailsFiltered.map((details: any) => { - let currentPlan = - (!licenseInfo && details.title === "Community") || - (licenseInfo && - licenseInfo.plan.toLowerCase() === - details.title.toLowerCase()); - return ( - - - - {details.title} - {currentPlan && ( - -
CURRENT PLAN
-
- )} -
- - {details.price} - - - {details.capacityMax || ""} - -
- ); - })} -
- {planItems.map((item: any) => { - return ( - + ) : ( + + {isLink ? ( + - ) : ( - item.plans[pd.title].label - )} - - )} - - {item.plans[pd.title].detail !== undefined && ( - - {item.plans[pd.title].detail} - - )} -
- + {label} + + ) : ( + label + )} + + ) + + return lbl; +} + +const PlanHeader = ({ + isActive, + isXsViewActive, + title, + tooltipText = "", + onClick + }: { + isActive: boolean, + isXsViewActive: boolean, + title: string, + price?: string, + capacity: string, + tooltipText?: string, + onClick: any +}) => { + + const plan = title.toLowerCase() + return ( + { + onClick && onClick(plan) + }} + + sx={ + { + display: "flex", + alignItems: "center", + justifyContent: "center", + flexFlow: "column", + borderLeft: "1px solid #eaeaea", + "& .plan-header": { + display: "flex", + alignItems: "center", + justifyContent: "center", + flexFlow: "column" + }, + + "& .title-block": { + paddingTop: "20px", + paddingBottom: "20px", + + "& .title": { + fontSize: "19px", + fontWeight: 600 + }, + + }, + "& .cur-plan-text": { + padding: "10px", + fontSize: "12px", + }, + + "@media (max-width: 600px)": { + cursor: "pointer", + "& .title-block": { + "& .title": { + fontSize: "14px", + fontWeight: 600 + } + } + }, + + "&.active, &.active.xs-active": { + borderTop: "3px solid #2781B0", + color: "#000000" + }, + "&.active": { + background: "#2781B0", + color: "#ffffff" + }, + "&.xs-active": { + background: "#eaeaea" + } + + + } + } + > + +
{title}
+ +
+ +
+
+ +
+
{isActive ? "Current Plan" : ""}
+ +
+ ) +} + +const LICENSE_PLANS={ + COMMUNITY:"community", + STANDARD:"standard", + ENTERPRISE:"enterprise" +} + +const PAID_PLANS = [LICENSE_PLANS.STANDARD, LICENSE_PLANS.ENTERPRISE] +const LicensePlans = ({ + licenseInfo, + setLicenseModal, + operatorMode, + }: IRegisterStatus) => { + + + const theme = useTheme(); + const isSmallScreen = useMediaQuery(theme.breakpoints.down("sm")); + + let currentPlan = !licenseInfo ? "community" : licenseInfo?.plan?.toLowerCase() + + const isCommunityPlan = currentPlan === LICENSE_PLANS.COMMUNITY + const isStandardPlan = currentPlan === LICENSE_PLANS.STANDARD + const isEnterprisePlan = currentPlan === LICENSE_PLANS.ENTERPRISE + + + /*In smaller screen use tabbed view to show features*/ + const [xsPlanView, setXsPlanView] = useState("") + let isXsViewCommunity = xsPlanView === LICENSE_PLANS.COMMUNITY + let isXsViewStandard = xsPlanView === LICENSE_PLANS.STANDARD + let isXsViewEnterprise = xsPlanView === LICENSE_PLANS.ENTERPRISE + + + const [communityHeader, standardHeader, enterpriseHeader] = planDetails + + const getButton = (link:string, btnText:string, variant:any, plan:string) =>{ + let linkToNav = currentPlan!=="community" ? "https://subnet.min.io": link + return( + + ) + } + + const onPlanClick = (plan: string) => { + setXsPlanView(plan) + } + + useEffect(() => { + + if (isSmallScreen) { + setXsPlanView(currentPlan || "community") + } else { + setXsPlanView("") + } + }, [isSmallScreen,currentPlan]) + return ( + + + + + + + + + + + + {/*Spacer*/} + + + + + + + + { - e.preventDefault(); - window.open( - `${button.link}?ref=${operatorMode ? "op" : "con"}`, - "_blank" - ); - }} - > - {currentPlanID !== index && index > 0 - ? button.text2 - : button.text} - -
-
- ); - })} -
- - - - - ); + + }}> +
Features
+ + + + {planItems.map((item: any, index:number) => { + + const clsName = item.className || "" + const community = item.plans["Community"] + const standard = item.plans["Standard"] + const enterprise = item.plans["Enterprise"] + + const linkTracker = `?ref=${operatorMode ? "op" : "con"}` + return ( + + + {item.field} + + +
{item.field}
+
+ {formatLabel(community.label, community.link, isSmallScreen, setLicenseModal)} + {community.detail} +
+
+ +
{item.field}
+
+ {formatLabel(standard.label, standard.link, isSmallScreen,null)} + {standard.detail} +
+
+ +
{item.field}
+
+ {formatLabel(enterprise.label, enterprise.link, isSmallScreen,null)} + {enterprise.detail} +
+
+ + { + index+1 === planItems.length?( + + + {/*Space Col*/} + + + + { + getButton(`https://slack.min.io${linkTracker}`, "Join Slack", "outlined", LICENSE_PLANS.COMMUNITY) + } + + + + { + getButton(`https://min.io/signup${linkTracker}`, !PAID_PLANS.includes(currentPlan) ?"Subscribe":"Login to SUBNET", "contained",LICENSE_PLANS.STANDARD) + } + + + { + getButton(`https://min.io/signup${linkTracker}`, !PAID_PLANS.includes(currentPlan) ?"Subscribe":"Login to SUBNET","contained", LICENSE_PLANS.ENTERPRISE) + } + + + ):null + } + +
+ ) + })} +
+ + + + ); }; export default withStyles(styles)(LicensePlans); diff --git a/portal-ui/src/screens/Console/License/utils.ts b/portal-ui/src/screens/Console/License/utils.ts index d7d594479..2e7f86961 100644 --- a/portal-ui/src/screens/Console/License/utils.ts +++ b/portal-ui/src/screens/Console/License/utils.ts @@ -32,14 +32,14 @@ export const planDetails: IPlanDetails[] = [ { id: 1, title: "Standard", - price: "$10 per TB", + price: "$10 per TiB per Month", capacityMax: "(Minimum of 100TB)", capacityMin: "", }, { id: 2, title: "Enterprise", - price: "$20 per TB", + price: "$20 per TiB per Month", capacityMax: "(Minimum of 100TB)", capacityMin: "", }, @@ -58,28 +58,49 @@ export interface IPlanItem { id: number; field: string; plans: IPlanItemValues; + className?:string } export const planItems: IPlanItem[] = [ { id: 0, - field: "License", + field: "Unit Price", + className:"unit-price", plans: { Community: { - label: "GNU AGPL v3", + label: "", detail: "", - link: true, }, Standard: { - label: "Commercial License", + label: "$10 per TiB per Month", + detail: "(Minimum of 100TB)", }, Enterprise: { - label: "Commercial License", + label: "$20 per TiB per Month", + detail: "(Minimum of 100TB)", }, }, }, { id: 1, + field: "License", + className:"license-col", + plans: { + Community: { + label: "GNU AGPL v3", + detail: "Open source", + link: true, + }, + Standard: { + label: "Commercial", + }, + Enterprise: { + label: "Commercial", + }, + }, + }, + { + id: 2, field: "Software Release", plans: { Community: { @@ -94,14 +115,15 @@ export const planItems: IPlanItem[] = [ }, }, { - id: 2, + id: 3, field: "SLA", plans: { Community: { label: "No SLA", }, Standard: { - label: "<48 Hours (Local Business Hours)", + label: "<48 Hours", + detail: "(Local Business Hours)" }, Enterprise: { label: "<1 hour", @@ -109,7 +131,7 @@ export const planItems: IPlanItem[] = [ }, }, { - id: 3, + id: 4, field: "Support", plans: { Community: { @@ -127,11 +149,11 @@ export const planItems: IPlanItem[] = [ }, }, { - id: 4, - field: "Security Updates & Critical Bugs", + id: 5, + field: "Critical Security and Bug Detection", plans: { Community: { - label: "Self Update", + label: "Self", }, Standard: { label: "Continuous Scan and Alert", @@ -142,7 +164,7 @@ export const planItems: IPlanItem[] = [ }, }, { - id: 5, + id: 6, field: "Panic Button", plans: { Community: { @@ -157,7 +179,7 @@ export const planItems: IPlanItem[] = [ }, }, { - id: 6, + id:7, field: "Health Diagnostics", plans: { Community: { @@ -172,7 +194,7 @@ export const planItems: IPlanItem[] = [ }, }, { - id: 6, + id: 8, field: "Annual Architecture Review", plans: { Community: { @@ -187,7 +209,7 @@ export const planItems: IPlanItem[] = [ }, }, { - id: 7, + id: 9, field: "Annual Performance Review", plans: { Community: { @@ -202,7 +224,7 @@ export const planItems: IPlanItem[] = [ }, }, { - id: 8, + id: 10, field: "Indemnification", plans: { Community: { @@ -217,8 +239,8 @@ export const planItems: IPlanItem[] = [ }, }, { - id: 9, - field: "Security + Policy Review", + id: 11, + field: "Security and Policy Review", plans: { Community: { label: "N/A", diff --git a/portal-ui/src/screens/Console/Support/Register.tsx b/portal-ui/src/screens/Console/Support/Register.tsx index 10157101e..4db469827 100644 --- a/portal-ui/src/screens/Console/Support/Register.tsx +++ b/portal-ui/src/screens/Console/Support/Register.tsx @@ -14,20 +14,20 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -import { Theme } from "@mui/material/styles"; +import {Theme} from "@mui/material/styles"; import createStyles from "@mui/styles/createStyles"; import { - actionsTray, - containerForHeader, - searchField, - spacingUtils, + actionsTray, + containerForHeader, + searchField, + spacingUtils, } from "../Common/FormComponents/common/styleLibrary"; import withStyles from "@mui/styles/withStyles"; -import { Box, Button, Link } from "@mui/material"; +import {Box, Button, Grid, Link} from "@mui/material"; import PageHeader from "../Common/PageHeader/PageHeader"; import PageLayout from "../Common/Layout/PageLayout"; -import React, { Fragment, useCallback, useEffect, useState } from "react"; -import { CopyIcon, DiagnosticsFeatureIcon, UsersIcon } from "../../../icons"; +import React, {Fragment, useCallback, useEffect, useState} from "react"; +import {CopyIcon, UsersIcon} from "../../../icons"; import RemoveRedEyeIcon from "@mui/icons-material/RemoveRedEye"; import VisibilityOffIcon from "@mui/icons-material/VisibilityOff"; import OnlineRegistrationIcon from "../../../icons/OnlineRegistrationIcon"; @@ -38,1060 +38,991 @@ import OfflineRegistrationBackIcon from "../../../icons/OfflineRegistrationBackI import api from "../../../common/api"; import { - SubnetInfo, - SubnetLoginRequest, - SubnetLoginResponse, - SubnetLoginWithMFARequest, - SubnetOrganization, - SubnetRegisterRequest, - SubnetRegTokenResponse, + SubnetInfo, + SubnetLoginRequest, + SubnetLoginResponse, + SubnetLoginWithMFARequest, + SubnetOrganization, + SubnetRegisterRequest, + SubnetRegTokenResponse, } from "../License/types"; -import { ErrorResponseHandler } from "../../../common/types"; +import {ErrorResponseHandler} from "../../../common/types"; import LockOutlinedIcon from "@mui/icons-material/LockOutlined"; import SelectWrapper from "../Common/FormComponents/SelectWrapper/SelectWrapper"; import { hasPermission } from "../../../common/SecureComponent"; import { - CONSOLE_UI_RESOURCE, - IAM_PAGES, - IAM_PAGES_PERMISSIONS, + CONSOLE_UI_RESOURCE, + IAM_PAGES, + IAM_PAGES_PERMISSIONS, } from "../../../common/SecureComponent/permissions"; -import { connect } from "react-redux"; -import { setErrorSnackMessage } from "../../../actions"; +import {connect} from "react-redux"; +import {setErrorSnackMessage} from "../../../actions"; import SettingsIcon from "../../../icons/SettingsIcon"; import RegisterStatus from "./RegisterStatus"; import FormSwitchWrapper from "../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper"; -import { AppState } from "../../../store"; -import { - HelpIconFilled, - CallHomeFeatureIcon, - PerformanceFeatureIcon, -} from "../../../icons"; +import {AppState} from "../../../store"; + +import RegisterHelpBox from "./RegisterHelpBox"; interface IRegister { - classes: any; - displayErrorMessage: typeof setErrorSnackMessage; - operatorMode: boolean; + classes: any; + displayErrorMessage: typeof setErrorSnackMessage; + operatorMode: boolean; } const styles = (theme: Theme) => - createStyles({ - loading: { - paddingTop: 8, - paddingLeft: 40, - }, - buttons: { - justifyContent: "flex-start", - gap: 20, - }, - localMessage: { - fontSize: 24, - color: "#07193E", - fontWeight: "bold", - textAlign: "center", - marginBottom: 10, - }, - headerStyle: { - color: theme.palette.primary.main, - fontSize: 16, - fontWeight: "bold", - }, - registerActivationIcon: { - color: theme.palette.primary.main, - fontSize: 16, - fontWeight: "bold", - marginBottom: 20, - "& .min-icon": { - width: 32.12, - height: 25, - marginRight: 10, - verticalAlign: "middle", - }, - }, - registerActivationMode: { - textAlign: "right", - "& a": { - cursor: "pointer", - }, - }, - subnetDescription: { - textAlign: "left", - Font: "normal normal normal 14px/17px Lato", - letterSpacing: 0, - color: "#000000", - "& span": { - fontWeight: "bold", - }, - }, - registeredStatus: { - border: "1px solid #E2E2E2", - padding: "24px 24px 24px 24px", - borderRadius: 2, - marginBottom: 25, - backgroundColor: "#FBFAFA", - "& .min-icon": { - width: 20, - height: 20, - marginLeft: 48, - marginRight: 13, - verticalAlign: "middle", - marginTop: -3, - }, - "& span": { - fontWeight: "bold", - }, - }, - offlineRegisterButton: { - textAlign: "right", - paddingRight: 20, - }, - copyInputBox: { - "& button": { - border: "1px solid #5E5E5E", - borderRadius: 2, - }, - }, - link: { - color: "#2781B0", - cursor: "pointer", - }, - smallBoxy: { - border: "#E5E5E5 1px solid", - borderRadius: 2, - padding: 20, - backgroundColor: "#fff", - }, - sizedLabel: { - minWidth: "75px", - }, - ...actionsTray, - ...searchField, - ...spacingUtils, - ...containerForHeader(theme.spacing(4)), - }); - -const FormTitle = ({ icon = null, title }: { icon?: any; title: any }) => { - return ( - - {icon} -
{title}
-
- ); -}; - -const RegisterHelpBox = () => { - return ( - - - -
Why should I register?
-
- - Registering this cluster with the MinIO Subscription Network (SUBNET) - provides the following benefits in addition to the commercial license - and SLA backed support. - - - - } - description={`Call Home Monitoring`} - /> - } - description={`Health Diagnostics`} - /> - } - description={`Performance Analysis`} - /> - -
- ); -}; -const FeatureItem = ({ - icon, - description, -}: { - icon: any; - description: string; -}) => { - return ( - - {icon}{" "} -
- {description} -
-
- ); -}; -const Register = ({ - classes, - displayErrorMessage, - operatorMode, -}: IRegister) => { - const [license, setLicense] = useState(""); - const [subnetPassword, setSubnetPassword] = useState(""); - const [subnetEmail, setSubnetEmail] = useState(""); - const [subnetMFAToken, setSubnetMFAToken] = useState(""); - const [subnetOTP, setSubnetOTP] = useState(""); - const [subnetAccessToken, setSubnetAccessToken] = useState(""); - const [selectedSubnetOrganization, setSelectedSubnetOrganization] = - useState(""); - const [subnetRegToken, setSubnetRegToken] = useState(""); - const [subnetOrganizations, setSubnetOrganizations] = useState< - SubnetOrganization[] - >([]); - const [showPassword, setShowPassword] = useState(false); - const [onlineActivation, setOnlineActivation] = useState(true); - const [loading, setLoading] = useState(false); - const [loadingLicenseInfo, setLoadingLicenseInfo] = useState(false); - const [clusterRegistered, setClusterRegistered] = useState(false); - const [initialLicenseLoading, setInitialLicenseLoading] = - useState(true); - const [displaySubnetProxy, setDisplaySubnetProxy] = useState(false); - - const clearForm = () => { - setSubnetAccessToken(""); - setSelectedSubnetOrganization(""); - setSubnetRegToken(""); - setShowPassword(false); - setOnlineActivation(true); - setSubnetOrganizations([]); - setLicense(""); - setSubnetPassword(""); - setSubnetEmail(""); - setSubnetMFAToken(""); - setSubnetOTP(""); - }; - - const getSubnetInfo = hasPermission( - CONSOLE_UI_RESOURCE, - IAM_PAGES_PERMISSIONS[IAM_PAGES.LICENSE], - true - ); - - const fetchLicenseInfo = useCallback(() => { - if (loadingLicenseInfo) { - return; - } - if (getSubnetInfo) { - setLoadingLicenseInfo(true); - api - .invoke("GET", `/api/v1/subnet/info`) - .then((res: SubnetInfo) => { - setClusterRegistered(true); - setLoadingLicenseInfo(false); - }) - .catch((err: ErrorResponseHandler) => { - if (err.errorMessage !== "License not found") { - displayErrorMessage(err); - } - setClusterRegistered(false); - setLoadingLicenseInfo(false); - }); - } else { - setLoadingLicenseInfo(false); - } - }, [loadingLicenseInfo, getSubnetInfo, displayErrorMessage]); - - const fetchSubnetRegToken = () => { - if (loading || subnetRegToken) { - return; - } - setLoading(true); - api - .invoke("GET", "/api/v1/subnet/registration-token") - .then((resp: SubnetRegTokenResponse) => { - setLoading(false); - if (resp && resp.regToken) { - setSubnetRegToken(resp.regToken); - } - }) - .catch((err: ErrorResponseHandler) => { - console.log(err); - displayErrorMessage(err); - setLoading(false); - }); - }; - - const callRegister = (token: string, account_id: string) => { - const request: SubnetRegisterRequest = { - token: token, - account_id: account_id, - }; - api - .invoke("POST", "/api/v1/subnet/register", request) - .then(() => { - setLoading(false); - clearForm(); - fetchLicenseInfo(); - }) - .catch((err: ErrorResponseHandler) => { - displayErrorMessage(err); - setLoading(false); - }); - }; - const subnetRegister = () => { - if (loading) { - return; - } - setLoading(true); - if (subnetAccessToken && selectedSubnetOrganization) { - callRegister(subnetAccessToken, selectedSubnetOrganization); - } - }; - - const subnetLoginWithMFA = () => { - if (loading) { - return; - } - setLoading(true); - const request: SubnetLoginWithMFARequest = { - username: subnetEmail, - otp: subnetOTP, - mfa_token: subnetMFAToken, - }; - api - .invoke("POST", "/api/v1/subnet/login/mfa", request) - .then((resp: SubnetLoginResponse) => { - setLoading(false); - if (resp && resp.access_token && resp.organizations.length > 0) { - if (resp.organizations.length === 1) { - callRegister( - resp.access_token, - resp.organizations[0].accountId.toString() - ); - } else { - setSubnetAccessToken(resp.access_token); - setSubnetOrganizations(resp.organizations); - setSelectedSubnetOrganization( - resp.organizations[0].accountId.toString() - ); - } - } - }) - .catch((err: ErrorResponseHandler) => { - displayErrorMessage(err); - setLoading(false); - setSubnetOTP(""); - }); - }; - - const subnetLogin = () => { - if (loading) { - return; - } - setLoading(true); - let request: SubnetLoginRequest = { - username: subnetEmail, - password: subnetPassword, - apiKey: license, - }; - api - .invoke("POST", "/api/v1/subnet/login", request) - .then((resp: SubnetLoginResponse) => { - setLoading(false); - if (resp && resp.registered) { - clearForm(); - fetchLicenseInfo(); - } else if (resp && resp.mfa_token) { - setSubnetMFAToken(resp.mfa_token); - } else if (resp && resp.access_token && resp.organizations.length > 0) { - setSubnetAccessToken(resp.access_token); - setSubnetOrganizations(resp.organizations); - setSelectedSubnetOrganization( - resp.organizations[0].accountId.toString() - ); - } - }) - .catch((err: ErrorResponseHandler) => { - displayErrorMessage(err); - setLoading(false); - clearForm(); - }); - }; - - useEffect(() => { - if (initialLicenseLoading) { - fetchLicenseInfo(); - setInitialLicenseLoading(false); - } - }, [fetchLicenseInfo, initialLicenseLoading, setInitialLicenseLoading]); - - const formTitle = onlineActivation ? ( - - } - title={`Register with MinIO Subscription Network`} - /> - - ) : ( - - } - title={` Offline Activation of SUBNET License`} - /> - - ); - - let clusterRegistrationForm: JSX.Element; - - if (onlineActivation) { - if (subnetAccessToken && subnetOrganizations.length > 0) { - clusterRegistrationForm = ( - - - - - - - - setSelectedSubnetOrganization(e.target.value as string) - } - label="Select an organization" - value={selectedSubnetOrganization} - options={subnetOrganizations.map((organization) => ({ - label: organization.company, - value: organization.accountId.toString(), - }))} - /> - - - - - - - - ); - } else if (subnetMFAToken) { - clusterRegistrationForm = ( - - - - Two-Factor Authentication - - - - Please enter the 6-digit verification code that was sent to your - email address. This code will be valid for 5 minutes. - - - - } - id="subnet-otp" - name="subnet-otp" - onChange={(event: React.ChangeEvent) => - setSubnetOTP(event.target.value) - } - placeholder="" - label="" - value={subnetOTP} - /> - - - - - - - - - ); - } else { - clusterRegistrationForm = ( - - - - Use your MinIO Subscription Network login credentials to register - this cluster. - - - ) => - setSubnetEmail(event.target.value) - } - label="Email" - value={subnetEmail} - overlayIcon={} - /> - ) => - setSubnetPassword(event.target.value) - } - label="Password" - type={showPassword ? "text" : "password"} - value={subnetPassword} - overlayIcon={ - showPassword ? : - } - overlayAction={() => setShowPassword(!showPassword)} - /> - - - - - - - - - - ); - } - } else { - clusterRegistrationForm = ( - - - - -
1
{" "} -
- Copy the following registration token -
-
+ }, + registeredStatus: { + border: "1px solid #E2E2E2", + padding: "24px 24px 24px 24px", + borderRadius: 2, + marginBottom: 25, + backgroundColor: "#FBFAFA", + "& .min-icon": { + width: 20, + height: 20, + marginLeft: 48, + marginRight: 13, + verticalAlign: "middle", + marginTop: -3, + }, + "& span": { + fontWeight: "bold", + }, + }, + offlineRegisterButton: { + textAlign: "right", + paddingRight: 20, + }, + copyInputBox: { + "& button": { + border: "1px solid #5E5E5E", + borderRadius: 2, + }, + }, + link: { + color: "#2781B0", + cursor: "pointer", + }, + smallBoxy: { + border: "#E5E5E5 1px solid", + borderRadius: 2, + padding: 20, + backgroundColor: "#fff", + }, + sizedLabel: { + minWidth: "75px", + }, + ...actionsTray, + ...searchField, + ...spacingUtils, + ...containerForHeader(theme.spacing(4)), + }); - - {}} - value={subnetRegToken} - overlayIcon={} - extraInputProps={{ - readOnly: true, - }} - overlayAction={() => - navigator.clipboard.writeText(subnetRegToken) - } - /> - -
- - - -
2
-
- Navigate to SUBNET and register your cluster -
-
- - - - https://subnet.min.io/cluster/register - - -
- - - -
3
{" "} -
- Enter the API key generated by SUBNET -
-
- - - ) => - setLicense(event.target.value) - } - id="api-key" - name="api-key" - placeholder="" - label="" - type="text" - /> - -
- - - - -
- - -
- ); - } - - const proxyConfigurationCommand = - "mc admin config set {alias} subnet proxy={proxy}"; - - return ( - - - +const FormTitle = ({icon = null, title}: { icon?: any; title: any }) => { + return ( - {clusterRegistered && } - - {!onlineActivation ? ( - + {icon} +
{title}
+
+ ); +}; + + +const Register = ({ + classes, + displayErrorMessage, + operatorMode, + }: IRegister) => { + const [license, setLicense] = useState(""); + const [subnetPassword, setSubnetPassword] = useState(""); + const [subnetEmail, setSubnetEmail] = useState(""); + const [subnetMFAToken, setSubnetMFAToken] = useState(""); + const [subnetOTP, setSubnetOTP] = useState(""); + const [subnetAccessToken, setSubnetAccessToken] = useState(""); + const [selectedSubnetOrganization, setSelectedSubnetOrganization] = + useState(""); + const [subnetRegToken, setSubnetRegToken] = useState(""); + const [subnetOrganizations, setSubnetOrganizations] = useState([]); + const [showPassword, setShowPassword] = useState(false); + const [onlineActivation, setOnlineActivation] = useState(true); + const [loading, setLoading] = useState(false); + const [loadingLicenseInfo, setLoadingLicenseInfo] = useState(false); + const [clusterRegistered, setClusterRegistered] = useState(false); + const [initialLicenseLoading, setInitialLicenseLoading] = + useState(true); + const [displaySubnetProxy, setDisplaySubnetProxy] = useState(false); + + const clearForm = () => { + setSubnetAccessToken(""); + setSelectedSubnetOrganization(""); + setSubnetRegToken(""); + setShowPassword(false); + setOnlineActivation(true); + setSubnetOrganizations([]); + setLicense(""); + setSubnetPassword(""); + setSubnetEmail(""); + setSubnetMFAToken(""); + setSubnetOTP(""); + }; + + const getSubnetInfo = hasPermission( + CONSOLE_UI_RESOURCE, + IAM_PAGES_PERMISSIONS[IAM_PAGES.LICENSE], + true + ); + + const fetchLicenseInfo = useCallback(() => { + if (loadingLicenseInfo) { + return; + } + if (getSubnetInfo) { + setLoadingLicenseInfo(true); + api + .invoke("GET", `/api/v1/subnet/info`) + .then((res: SubnetInfo) => { + setClusterRegistered(true); + setLoadingLicenseInfo(false); + }) + .catch((err: ErrorResponseHandler) => { + if (err.errorMessage !== "License not found") { + displayErrorMessage(err); + } + setClusterRegistered(false); + setLoadingLicenseInfo(false); + }); + } else { + setLoadingLicenseInfo(false); + } + }, [loadingLicenseInfo, getSubnetInfo, displayErrorMessage]); + + const fetchSubnetRegToken = () => { + if (loading || subnetRegToken) { + return; + } + setLoading(true); + api + .invoke("GET", "/api/v1/subnet/registration-token") + .then((resp: SubnetRegTokenResponse) => { + setLoading(false); + if (resp && resp.regToken) { + setSubnetRegToken(resp.regToken); + } + }) + .catch((err: ErrorResponseHandler) => { + console.log(err); + displayErrorMessage(err); + setLoading(false); + }); + }; + + const callRegister = (token: string, account_id: string) => { + const request: SubnetRegisterRequest = { + token: token, + account_id: account_id, + }; + api + .invoke("POST", "/api/v1/subnet/register", request) + .then(() => { + setLoading(false); + clearForm(); + fetchLicenseInfo(); + }) + .catch((err: ErrorResponseHandler) => { + displayErrorMessage(err); + setLoading(false); + }); + }; + const subnetRegister = () => { + if (loading) { + return; + } + setLoading(true); + if (subnetAccessToken && selectedSubnetOrganization) { + callRegister(subnetAccessToken, selectedSubnetOrganization); + } + }; + + const subnetLoginWithMFA = () => { + if (loading) { + return; + } + setLoading(true); + const request: SubnetLoginWithMFARequest = { + username: subnetEmail, + otp: subnetOTP, + mfa_token: subnetMFAToken, + }; + api + .invoke("POST", "/api/v1/subnet/login/mfa", request) + .then((resp: SubnetLoginResponse) => { + setLoading(false); + if (resp && resp.access_token && resp.organizations.length > 0) { + if (resp.organizations.length === 1) { + callRegister( + resp.access_token, + resp.organizations[0].accountId.toString() + ); + } else { + setSubnetAccessToken(resp.access_token); + setSubnetOrganizations(resp.organizations); + setSelectedSubnetOrganization( + resp.organizations[0].accountId.toString() + ); + } + } + }) + .catch((err: ErrorResponseHandler) => { + displayErrorMessage(err); + setLoading(false); + setSubnetOTP(""); + }); + }; + + const subnetLogin = () => { + if (loading) { + return; + } + setLoading(true); + let request: SubnetLoginRequest = { + username: subnetEmail, + password: subnetPassword, + apiKey: license, + }; + api + .invoke("POST", "/api/v1/subnet/login", request) + .then((resp: SubnetLoginResponse) => { + setLoading(false); + if (resp && resp.registered) { + clearForm(); + fetchLicenseInfo(); + } else if (resp && resp.mfa_token) { + setSubnetMFAToken(resp.mfa_token); + } else if (resp && resp.access_token && resp.organizations.length > 0) { + setSubnetAccessToken(resp.access_token); + setSubnetOrganizations(resp.organizations); + setSelectedSubnetOrganization( + resp.organizations[0].accountId.toString() + ); + } + }) + .catch((err: ErrorResponseHandler) => { + displayErrorMessage(err); + setLoading(false); + clearForm(); + }); + }; + + useEffect(() => { + if (initialLicenseLoading) { + fetchLicenseInfo(); + setInitialLicenseLoading(false); + } + }, [fetchLicenseInfo, initialLicenseLoading, setInitialLicenseLoading]); + + const formTitle = onlineActivation ? ( + + } + title={`Register with MinIO Subscription Network`} + /> + + ) : ( + + } + title={` Offline Activation of SUBNET License`} + /> + + ); + + let clusterRegistrationForm: JSX.Element; + + if (onlineActivation) { + if (subnetAccessToken && subnetOrganizations.length > 0) { + clusterRegistrationForm = ( + + + + + + + + setSelectedSubnetOrganization(e.target.value as string) + } + label="Select an organization" + value={selectedSubnetOrganization} + options={subnetOrganizations.map((organization) => ({ + label: organization.company, + value: organization.accountId.toString(), + }))} + /> + + + + + + + + ); + } else if (subnetMFAToken) { + clusterRegistrationForm = ( + + + + Two-Factor Authentication + + + + Please enter the 6-digit verification code that was sent to your + email address. This code will be valid for 5 minutes. + + + + } + id="subnet-otp" + name="subnet-otp" + onChange={(event: React.ChangeEvent) => + setSubnetOTP(event.target.value) + } + placeholder="" + label="" + value={subnetOTP} + /> + + + + + + + + + ); + } else { + clusterRegistrationForm = ( + + + + Use your MinIO Subscription Network login credentials to register + this cluster. + + + ) => + setSubnetEmail(event.target.value) + } + label="Email" + value={subnetEmail} + overlayIcon={} + /> + ) => + setSubnetPassword(event.target.value) + } + label="Password" + type={showPassword ? "text" : "password"} + value={subnetPassword} + overlayIcon={ + showPassword ? : + } + overlayAction={() => setShowPassword(!showPassword)} + /> + + + + + + + + + + ); + } + } else { + clusterRegistrationForm = ( + - - setOnlineActivation(!onlineActivation)} - > - Back to Online Activation - + + + +
1
+ {" "} +
+ Copy the following registration token +
+
+ + + { + }} + value={subnetRegToken} + overlayIcon={} + extraInputProps={{ + readOnly: true, + }} + overlayAction={() => + navigator.clipboard.writeText(subnetRegToken) + } + /> + +
+ + + +
2
+
+ Navigate to SUBNET and register your cluster +
+
+ + + + https://subnet.min.io/cluster/register + + +
+ + + +
3
+ {" "} +
+ Enter the API key generated by SUBNET +
+
+ + + ) => + setLicense(event.target.value) + } + id="api-key" + name="api-key" + placeholder="" + label="" + type="text" + /> + +
+ + + + +
+ +
- ) : null} + ); + } - {formTitle} + const proxyConfigurationCommand = + "mc admin config set {alias} subnet proxy={proxy}"; - {clusterRegistrationForm} + + const regUi = + + {clusterRegistered && } + {clusterRegistered ? ( + + + Login to SUBNET to avail support for + this MinIO cluster + + ):null} + + + {!onlineActivation ? ( + + + setOnlineActivation(!onlineActivation)} + > + Back to Online Activation + + + ) : null} + + {clusterRegistered ? null : formTitle} + + {clusterRegistered ? null : clusterRegistrationForm} - {onlineActivation && ( - - - + {!clusterRegistered && onlineActivation && ( + - -
- Proxy Configuration -
+ + + +
+ Proxy Configuration +
+
+ + For airgap/firewalled environments it is possible to{" "} + + configure a proxy + {" "} + to connect to SUBNET . + + + {displaySubnetProxy && ( + { + }} + label="" + value={proxyConfigurationCommand} + overlayIcon={} + extraInputProps={{ + readOnly: true, + }} + overlayAction={() => + navigator.clipboard.writeText(proxyConfigurationCommand) + } + /> + )} + +
+ + ) => { + setDisplaySubnetProxy(event.target.checked); + }} + /> +
- - For airgap/firewalled environments it is possible to{" "} - - configure a proxy - {" "} - to connect to SUBNET . - - - {displaySubnetProxy && ( - {}} - label="" - value={proxyConfigurationCommand} - overlayIcon={} - extraInputProps={{ - readOnly: true, - }} - overlayAction={() => - navigator.clipboard.writeText(proxyConfigurationCommand) - } - /> - )} - -
- - ) => { - setDisplaySubnetProxy(event.target.checked); - }} - /> - -
- - - Cluster does not have internet - access? Use{" "} - - - { - fetchSubnetRegToken(); - setOnlineActivation(!onlineActivation); - }} + - Offline Activation. - - - -
+ + Cluster does not have internet + access? Use{" "} + + + { + fetchSubnetRegToken(); + setOnlineActivation(!onlineActivation); + }} + > + Offline Activation. + + +
+ )} -
- ); + + const loadingUi =
Loading..
+ const uiToShow = loadingLicenseInfo ? loadingUi : regUi + + return ( + + + + {uiToShow} + + + ); }; const mapState = (state: AppState) => ({ - operatorMode: state.system.operatorMode, + operatorMode: state.system.operatorMode, }); const connector = connect(mapState, { - displayErrorMessage: setErrorSnackMessage, + displayErrorMessage: setErrorSnackMessage, }); export default withStyles(styles)(connector(Register)); diff --git a/portal-ui/src/screens/Console/Support/RegisterHelpBox.tsx b/portal-ui/src/screens/Console/Support/RegisterHelpBox.tsx new file mode 100644 index 000000000..774057d61 --- /dev/null +++ b/portal-ui/src/screens/Console/Support/RegisterHelpBox.tsx @@ -0,0 +1,99 @@ +import {Box} from "@mui/material"; +import {CallHomeFeatureIcon, DiagnosticsFeatureIcon, HelpIconFilled, PerformanceFeatureIcon} from "../../../icons"; +import React from "react"; + +const FeatureItem = ({ + icon, + description, + }: { + icon: any; + description: string; +}) => { + return ( + + {icon}{" "} +
+ {description} +
+
+ ); +}; +const RegisterHelpBox = ({hasMargin=true}:{hasMargin?:boolean}) => { + return ( + + + +
Why should I register?
+
+ + Registering this cluster with the MinIO Subscription Network (SUBNET) + provides the following benefits in addition to the commercial license + and SLA backed support. + + + + } + description={`Call Home Monitoring`} + /> + } + description={`Health Diagnostics`} + /> + } + description={`Performance Analysis`} + /> + +
+ ); +}; + +export default RegisterHelpBox diff --git a/portal-ui/src/screens/Console/Support/RegisterStatus.tsx b/portal-ui/src/screens/Console/Support/RegisterStatus.tsx index bc4728fc6..8936efd8b 100644 --- a/portal-ui/src/screens/Console/Support/RegisterStatus.tsx +++ b/portal-ui/src/screens/Console/Support/RegisterStatus.tsx @@ -14,49 +14,72 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -import { Grid } from "@mui/material"; +import {Box, Grid} from "@mui/material"; import VerifiedIcon from "../../../icons/VerifiedIcon"; import React from "react"; -import { Theme } from "@mui/material/styles"; +import {Theme} from "@mui/material/styles"; import createStyles from "@mui/styles/createStyles"; import withStyles from "@mui/styles/withStyles"; +import RegisterHelpBox from "./RegisterHelpBox"; +import Link from "@mui/material/Link"; const styles = (theme: Theme) => - createStyles({ - registeredStatus: { - border: "1px solid #E2E2E2", - padding: "24px 24px 24px 24px", - borderRadius: 2, - marginBottom: 25, - backgroundColor: "#FBFAFA", - "& .min-icon": { - width: 20, - height: 20, - marginLeft: 48, - marginRight: 13, - verticalAlign: "middle", - marginTop: -3, - }, - "& span": { - fontWeight: "bold", - }, - }, - }); + createStyles({ + registeredStatus: { + border: "1px solid #E2E2E2", + display: "flex", + alignItems: "center", + padding: "24px", + borderRadius: 2, + backgroundColor: "#FBFAFA", + "& .min-icon": { + width: 20, + height: 20, + marginRight: 13, + verticalAlign: "middle", + }, + "& span": { + fontWeight: "bold", + }, + }, + }); interface IRegisterStatus { - classes: any; + classes: any; + showHelp?: boolean } -function RegisterStatus({ classes }: IRegisterStatus) { - return ( - - - Registration Status: - - Registered - - - ); +function RegisterStatus({classes, showHelp}: IRegisterStatus) { + return ( + + + + Registered with MinIO SUBNET + + {showHelp ? + ( + + + Login to SUBNET to avail technical product support for + this + MinIO cluster + + + + + + ) : null} + + + ); } export default withStyles(styles)(RegisterStatus);