Updated License page with new design (#3405)

This commit is contained in:
Alex
2024-07-12 10:18:17 -06:00
committed by GitHub
parent e3e3599095
commit 8f0eb11ced
4 changed files with 346 additions and 1111 deletions

View File

@@ -0,0 +1,32 @@
// This file is part of MinIO Console Server
// Copyright (c) 2024 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import * as React from "react";
import { SVGProps } from "react";
const CheckIcon = (props: SVGProps<SVGSVGElement>) => (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
className={`min-icon`}
fill={"currentcolor"}
{...props}
>
<polygon points="8.5 16.5 21.5 3.6 23.4 5.5 8.5 20.4 .6 12.5 2.5 10.5 8.5 16.5" />
</svg>
);
export default CheckIcon;

View File

@@ -1,41 +0,0 @@
// This file is part of MinIO Console Server
// Copyright (c) 2023 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React from "react";
import ModalWrapper from "../Common/ModalWrapper/ModalWrapper";
import LicenseFAQ from "./LicenseFAQ";
import { useSelector } from "react-redux";
import { AppState, useAppDispatch } from "../../../store";
import { closeFAQModal } from "./licenseSlice";
const FAQModal = () => {
const dispatch = useAppDispatch();
const isOpen = useSelector((state: AppState) => state.license.faqModalOpen);
return (
<ModalWrapper
modalOpen={isOpen}
title="License FAQ"
onClose={() => {
dispatch(closeFAQModal());
}}
>
<LicenseFAQ />
</ModalWrapper>
);
};
export default FAQModal;

View File

@@ -14,30 +14,13 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React, { Fragment, useEffect, useState } from "react";
import clsx from "clsx";
import {
AGPLV3Logo,
Box,
breakPoints,
Button,
CheckCircleIcon,
ConsoleEnterprise,
ConsoleStandard,
LicenseDocIcon,
} from "mds";
import React, { Fragment } from "react";
import { Box, Button } from "mds";
import { SubnetInfo } from "./types";
import {
COMMUNITY_PLAN_FEATURES,
ENTERPRISE_PLAN_FEATURES,
FEATURE_ITEMS,
getRenderValue,
LICENSE_PLANS,
PAID_PLANS,
STANDARD_PLAN_FEATURES,
} from "./utils";
import { FEATURE_ITEMS, FeatureItem, LICENSE_PLANS_INFORMATION } from "./utils";
import styled from "styled-components";
import get from "lodash/get";
import CheckIcon from "./CheckIcon";
interface IRegisterStatus {
activateProductModal: any;
@@ -47,441 +30,105 @@ interface IRegisterStatus {
setActivateProductModal: any;
}
const PlanListContainer = styled.div(({ theme }) => ({
const LicensesInformation = styled.div(({ theme }) => ({
display: "grid",
margin: "0 1.5rem 0 1.5rem",
gridTemplateColumns: "1fr 1fr 1fr 1fr",
[`@media (max-width: ${breakPoints.sm}px)`]: {
gridTemplateColumns: "1fr 1fr 1fr",
},
"&.paid-plans-only": {
display: "grid",
gridTemplateColumns: "1fr 1fr 1fr",
},
"& .features-col": {
flex: 1,
minWidth: "260px",
"@media (max-width: 600px)": {
display: "none",
},
},
"& .xs-only": {
display: "none",
},
"& .button-box": {
display: "flex",
alignItems: "center",
gridTemplateColumns: "repeat(4, minmax(350px, 400px));",
justifyContent: "flex-start",
marginTop: 30,
marginLeft: 30,
"& > div": {
borderBottom: `${get(theme, "borderColor", "#EAEAEA")} 1px solid`,
padding: "25px 40px",
justifyContent: "center",
padding: "5px 0px 25px 0px",
borderLeft: `1px solid ${get(theme, "borderColor", "#EAEAEA")}`,
},
"& .plan-header": {
height: "99px",
borderBottom: `1px solid ${get(theme, "borderColor", "#EAEAEA")}`,
},
"& .feature-title": {
height: "25px",
paddingLeft: "26px",
fontSize: "14px",
background: get(theme, "signalColors.disabled", "#E5E5E5"),
color: get(theme, "signalColors.main", "#07193E"),
"@media (max-width: 600px)": {
"& .feature-title-info .xs-only": {
"&.openSource": {
borderRight: `#002562 2px solid`,
borderLeft: `#002562 2px solid`,
position: "relative",
"&.first:before": {
content: "' '",
width: "calc(100% + 4px)",
height: 16,
display: "block",
backgroundColor: "#001F55",
position: "absolute",
top: -14,
left: -2,
border: `#002562 2px solid`,
borderBottom: 0,
borderTopLeftRadius: 12,
borderTopRightRadius: 12,
},
"&.last": {
paddingBottom: 30,
"&:after": {
content: "' '",
width: "calc(100% + 4px)",
height: 16,
display: "block",
position: "absolute",
bottom: -14,
left: -2,
border: `#002562 2px solid`,
borderTop: 0,
borderBottomLeftRadius: 12,
borderBottomRightRadius: 12,
},
},
},
},
"& .feature-name": {
minHeight: "60px",
padding: "5px",
borderBottom: `1px solid ${get(theme, "borderColor", "#EAEAEA")}`,
display: "flex",
alignItems: "center",
paddingLeft: "26px",
fontSize: "14px",
},
"& .feature-item": {
display: "flex",
flexFlow: "column",
alignItems: "center",
justifyContent: "center",
minHeight: "60px",
padding: "0 15px 0 15px",
borderBottom: `1px solid ${get(theme, "borderColor", "#EAEAEA")}`,
borderLeft: `1px solid ${get(theme, "borderColor", "#EAEAEA")}`,
fontSize: "14px",
"& .link-text": {
color: "#2781B0",
cursor: "pointer",
textDecoration: "underline",
"&.feature-information": {
textAlign: "center",
},
"&.icon-yes": {
width: "15px",
height: "15px",
"&.feature-label": {
paddingLeft: 5,
},
"&.noBorderBottom": {
borderBottom: 0,
},
},
"& .feature-item-info": {
flex: 1,
display: "flex",
flexFlow: "column",
alignItems: "center",
justifyContent: "space-around",
"& .planName": {
fontWeight: 600,
fontSize: 35,
marginBottom: 20,
textAlign: "center",
"@media (max-width: 600px)": {
justifyContent: "space-evenly",
width: "100%",
"& .xs-only": {
display: "block",
},
"& .plan-feature": {
textAlign: "center",
paddingRight: "10px",
},
},
marginTop: 10,
},
"& .plan-col": {
minWidth: "260px",
flex: 1,
},
"& .active-plan-col": {
background: `${get(
theme,
"boxBackground",
"#FDFDFD",
)} 0% 0% no-repeat padding-box`,
boxShadow: " 0px 3px 20px #00000038",
"& .plan-header": {
backgroundColor: get(theme, "signalColors.info", "#2781B0"),
},
"& .feature-title": {
background: get(theme, "signalColors.disabled", "#E5E5E5"),
color: get(theme, "fontColor", "#000"),
},
},
}));
const PlanHeaderContainer = styled.div(({ theme }) => ({
display: "flex",
alignItems: "flex-start",
justifyContent: "center",
flexFlow: "column",
borderLeft: `1px solid ${get(theme, "borderColor", "#EAEAEA")}`,
borderBottom: "0px !important",
"& .plan-header": {
"& .planIcon": {
height: 45,
display: "flex",
alignItems: "center",
alignItems: "flex-start",
justifyContent: "center",
flexFlow: "column",
"& svg": {
height: 35,
},
"&.commercial": {
"& svg": {
height: 20,
},
},
},
"& .title-block": {
"& .planDescription": {
display: "flex",
alignItems: "center",
flexFlow: "column",
width: "100%",
"& .title-main": {
display: "flex",
alignItems: "center",
justifyContent: "center",
flex: 1,
},
"& .iconContainer": {
"& .min-icon": {
minWidth: 140,
width: "100%",
maxHeight: 55,
height: "100%",
},
},
},
"& .open-source": {
fontSize: "14px",
display: "flex",
marginBottom: "5px",
alignItems: "center",
"& .min-icon": {
marginRight: "8px",
height: "12px",
width: "12px",
},
},
"& .cur-plan-text": {
fontSize: "12px",
textTransform: "uppercase",
},
"@media (max-width: 600px)": {
cursor: "pointer",
"& .title-block": {
"& .title": {
fontSize: "14px",
fontWeight: 600,
},
},
},
"&.active, &.active.xs-active": {
color: "#ffffff",
position: "relative",
"& .min-icon": {
fill: "#ffffff",
},
"&:before": {
content: "' '",
position: "absolute",
width: "100%",
height: "18px",
backgroundColor: get(theme, "signalColors.info", "#2781B0"),
display: "block",
top: -16,
},
"& .iconContainer": {
"& .min-icon": {
marginTop: "-12px",
},
},
},
"&.active": {
backgroundColor: get(theme, "signalColors.info", "#2781B0"),
color: "#ffffff",
},
"&.xs-active": {
background: "#eaeaea",
justifyContent: "center",
},
}));
const ListContainer = styled.div(({ theme }) => ({
border: `1px solid ${get(theme, "borderColor", "#EAEAEA")}`,
borderTop: "0px",
marginBottom: "45px",
"&::-webkit-scrollbar": {
width: "5px",
height: "5px",
},
"&::-webkit-scrollbar-track": {
background: "#F0F0F0",
borderRadius: 0,
boxShadow: "inset 0px 0px 0px 0px #F0F0F0",
},
"&::-webkit-scrollbar-thumb": {
background: "#777474",
borderRadius: 0,
},
"&::-webkit-scrollbar-thumb:hover": {
background: "#5A6375",
},
}));
const PlanHeader = ({
isActive,
isXsViewActive,
title,
onClick,
children,
}: {
isActive: boolean;
isXsViewActive: boolean;
title: string;
price?: string;
onClick: any;
children: any;
}) => {
const plan = title.toLowerCase();
return (
<PlanHeaderContainer
className={clsx({
"plan-header": true,
active: isActive,
[`xs-active`]: isXsViewActive,
})}
onClick={() => {
onClick && onClick(plan);
}}
>
{children}
</PlanHeaderContainer>
);
};
const FeatureTitleRowCmp = (props: { featureLabel: any }) => {
return (
<Box className="feature-title">
<Box className="feature-title-info">
<div className="xs-only">{props.featureLabel} </div>
</Box>
</Box>
);
};
const PricingFeatureItem = (props: {
featureLabel: any;
label?: any;
detail?: any;
xsLabel?: string;
style?: any;
}) => {
return (
<Box className="feature-item" style={props.style}>
<Box className="feature-item-info">
<div className="xs-only">
{getRenderValue(props.featureLabel || "")}
</div>
<Box className="plan-feature">
<div>{getRenderValue(props.label || "")}</div>
{getRenderValue(props.detail)}
<div className="xs-only">{props.xsLabel} </div>
</Box>
</Box>
</Box>
);
};
const LicensePlans = ({ licenseInfo }: IRegisterStatus) => {
const [isSmallScreen, setIsSmallScreen] = useState<boolean>(
window.innerWidth >= breakPoints.sm,
);
useEffect(() => {
const handleWindowResize = () => {
let extMD = false;
if (window.innerWidth >= breakPoints.sm) {
extMD = true;
}
setIsSmallScreen(extMD);
};
window.addEventListener("resize", handleWindowResize);
return () => {
window.removeEventListener("resize", handleWindowResize);
};
}, []);
let currentPlan = !licenseInfo
? "community"
: licenseInfo?.plan?.toLowerCase();
const isCommunityPlan = currentPlan === LICENSE_PLANS.COMMUNITY;
const isStandardPlan = currentPlan === LICENSE_PLANS.STANDARD;
const isEnterprisePlan = [
LICENSE_PLANS.ENTERPRISE,
LICENSE_PLANS.ENTERPRISE_LITE,
LICENSE_PLANS.ENTERPRISE_PLUS,
].includes(currentPlan);
const isPaidPlan = PAID_PLANS.includes(currentPlan);
/*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 = [
LICENSE_PLANS.ENTERPRISE,
LICENSE_PLANS.ENTERPRISE_LITE,
LICENSE_PLANS.ENTERPRISE_PLUS,
].includes(xsPlanView);
const getCommunityPlanHeader = () => {
return (
<PlanHeader
key={"community-header"}
isActive={isCommunityPlan}
isXsViewActive={isXsViewCommunity}
title={"community"}
onClick={isSmallScreen ? onPlanClick : null}
>
<Box className="title-block">
<Box className="title-main">
<div className="iconContainer">
<AGPLV3Logo style={{ width: 117 }} />
</div>
</Box>
</Box>
</PlanHeader>
);
};
const getStandardPlanHeader = () => {
return (
<PlanHeader
key={"standard-header"}
isActive={isStandardPlan}
isXsViewActive={isXsViewStandard}
title={"Standard"}
onClick={isSmallScreen ? onPlanClick : null}
>
<Box className="title-block">
<Box className="title-main">
<div className="iconContainer">
<ConsoleStandard />
</div>
</Box>
</Box>
</PlanHeader>
);
};
const getEnterpriseHeader = () => {
return (
<PlanHeader
key={"enterprise-header"}
isActive={isEnterprisePlan}
isXsViewActive={isXsViewEnterprise}
title={"Enterprise"}
onClick={isSmallScreen ? onPlanClick : null}
>
<Box className="title-block">
<Box className="title-main">
<div className="iconContainer">
<ConsoleEnterprise />
</div>
</Box>
</Box>
</PlanHeader>
);
};
const getButton = (
link: string,
btnText: string,
variant: any,
plan: string,
) => {
const getButton = (link: string, btnText: string, variant: any) => {
let linkToNav =
currentPlan !== "community" ? "https://subnet.min.io" : link;
return (
<Button
id={`license-action-${link}`}
variant={variant}
style={{
sx={{
marginTop: "12px",
width: "80%",
height: "55px",
}}
disabled={
currentPlan !== LICENSE_PLANS.COMMUNITY && currentPlan !== plan
}
onClick={(e) => {
e.preventDefault();
@@ -492,264 +139,88 @@ const LicensePlans = ({ licenseInfo }: IRegisterStatus) => {
);
};
const onPlanClick = (plan: string) => {
setXsPlanView(plan);
const renderFeatureInformation = (content: FeatureItem | null) => {
if (content) {
return (
<Fragment>
{content.content}
{content.isCheck && <CheckIcon style={{ width: 16, height: 16 }} />}
</Fragment>
);
}
return <Fragment />;
};
useEffect(() => {
if (isSmallScreen) {
setXsPlanView(currentPlan || "community");
} else {
setXsPlanView("");
}
}, [isSmallScreen, currentPlan]);
const featureList = FEATURE_ITEMS;
return (
<Fragment>
<ListContainer>
<Box
className={"title-blue-bar"}
sx={{
height: "8px",
borderBottom: "8px solid rgb(6 48 83)",
}}
/>
<PlanListContainer className={isPaidPlan ? "paid-plans-only" : ""}>
<Box className="features-col">
{featureList.map((fi) => {
const featureTitleRow = fi.featureTitleRow;
const isHeader = fi.isHeader;
if (isHeader) {
if (isPaidPlan) {
return (
<Box
key={`plan-header-${fi.desc}`}
className="plan-header"
sx={{
fontSize: "14px",
paddingLeft: "26px",
display: "flex",
alignItems: "center",
justifyContent: "flex-start",
borderBottom: "0px !important",
"& .link-text": {
color: "#2781B0",
cursor: "pointer",
textDecoration: "underline",
},
"& .min-icon": {
marginRight: "10px",
color: "#2781B0",
fill: "#2781B0",
},
}}
>
<LicenseDocIcon />
<a
href={`https://subnet.min.io/terms-and-conditions/${currentPlan}`}
rel="noopener"
className={"link-text"}
>
View License agreement <br />
for the registered plan.
</a>
</Box>
);
}
return (
<LicensesInformation>
{[null, ...LICENSE_PLANS_INFORMATION].map((element, index) => {
return (
<Box className={`${index === 1 ? "openSource first" : ""}`}>
{element !== null && (
<Box>
<Box className={"planName"}>{element.planName}</Box>
<Box
key={`plan-header-label-${fi.desc}`}
className={`plan-header`}
sx={{
fontSize: "14px",
paddingLeft: "26px",
display: "flex",
alignItems: "center",
justifyContent: "flex-start",
borderBottom: "0px !important",
}}
className={`planIcon ${
element.planType === "commercial" ? "commercial" : ""
}`}
>
{fi.label}
{element?.planIcon}
</Box>
);
}
if (featureTitleRow) {
return (
<Box
key={`plan-descript-${fi.desc}`}
className="feature-title"
sx={{
fontSize: "14px",
fontWeight: 600,
textTransform: "uppercase",
}}
>
<div>{getRenderValue(fi.desc)} </div>
<Box className={"planDescription"}>
{element?.planDescription}
</Box>
);
}
return (
<Box
key={`plan-feature-name-${fi.desc}`}
className="feature-name"
style={fi.style}
>
<div>{getRenderValue(fi.desc)} </div>
</Box>
);
})}
</Box>
{!isPaidPlan ? (
<Box
className={`plan-col ${
isCommunityPlan ? "active-plan-col" : "non-active-plan-col"
}`}
>
{COMMUNITY_PLAN_FEATURES.map((fi, idx) => {
const featureLabel = featureList[idx].desc;
const { featureTitleRow, isHeader } = fi;
)}
</Box>
);
})}
{FEATURE_ITEMS.map((feature, index) => {
const lastItem =
index === FEATURE_ITEMS.length - 1 ? "noBorderBottom" : "";
if (isHeader) {
return getCommunityPlanHeader();
}
if (featureTitleRow) {
return (
<FeatureTitleRowCmp
key={`title-row-${fi.id}`}
featureLabel={featureLabel}
/>
);
}
return (
<PricingFeatureItem
key={`pricing-feature-${fi.id}`}
featureLabel={featureLabel}
label={fi.label}
detail={fi.detail}
xsLabel={fi.xsLabel}
style={fi.style}
/>
);
})}
<Box className="button-box">
{getButton(
`https://slack.min.io`,
"Join Slack",
"regular",
LICENSE_PLANS.COMMUNITY,
return (
<Fragment>
<Box className={`feature-label ${lastItem}`}>
{feature.featureLabel}
</Box>
<Box className={`feature-information openSource ${lastItem}`}>
{renderFeatureInformation(
feature.featurePlans.openSource || null,
)}
</Box>
<Box className={`feature-information ${lastItem}`}>
{renderFeatureInformation(feature.featurePlans.eosLite || null)}
</Box>
<Box className={`feature-information ${lastItem}`}>
{renderFeatureInformation(feature.featurePlans.eosPlus || null)}
</Box>
</Fragment>
);
})}
{[null, ...LICENSE_PLANS_INFORMATION].map((element, index) => {
return (
<Box
className={`${
index === 1 ? "openSource last" : ""
} noBorderBottom`}
sx={{
display: "flex",
justifyContent: "center",
}}
>
{element &&
getButton(
`https://min.io/signup`,
element.planType === "commercial"
? "Subscribe"
: "Join Slack",
element.planType === "commercial" ? "callAction" : "regular",
)}
</Box>
) : null}
<Box
className={`plan-col ${
isStandardPlan ? "active-plan-col" : "non-active-plan-col"
}`}
>
{STANDARD_PLAN_FEATURES.map((fi, idx) => {
const featureLabel = featureList[idx].desc;
const featureTitleRow = fi.featureTitleRow;
const isHeader = fi.isHeader;
if (isHeader) {
return getStandardPlanHeader();
}
if (featureTitleRow) {
return (
<FeatureTitleRowCmp
key={`feature-title-row-${fi.id}`}
featureLabel={featureLabel}
/>
);
}
return (
<PricingFeatureItem
key={`feature-item-${fi.id}`}
featureLabel={featureLabel}
label={fi.label}
detail={fi.detail}
xsLabel={fi.xsLabel}
style={fi.style}
/>
);
})}
<Box className="button-box">
{getButton(
`https://min.io/signup`,
!PAID_PLANS.includes(currentPlan)
? "Subscribe"
: "Login to SUBNET",
"callAction",
LICENSE_PLANS.STANDARD,
)}
</Box>
</Box>
<Box
className={`plan-col ${
isEnterprisePlan ? "active-plan-col" : "non-active-plan-col"
}`}
>
{ENTERPRISE_PLAN_FEATURES.map((fi, idx) => {
const featureLabel = featureList[idx].desc;
const { featureTitleRow, isHeader, yesIcon } = fi;
if (isHeader) {
return getEnterpriseHeader();
}
if (featureTitleRow) {
return (
<FeatureTitleRowCmp
key={`feature-title-row2-${fi.id}`}
featureLabel={featureLabel}
/>
);
}
if (yesIcon) {
return (
<Box className="feature-item" key={`ent-feature-yes${fi.id}`}>
<Box className="feature-item-info">
<div className="xs-only"></div>
<Box className="plan-feature">
<CheckCircleIcon />
</Box>
</Box>
</Box>
);
}
return (
<PricingFeatureItem
key={`pricing-feature-item-${fi.id}`}
featureLabel={featureLabel}
label={fi.label}
detail={fi.detail}
style={fi.style}
/>
);
})}
<Box className="button-box">
{getButton(
`https://min.io/signup`,
!PAID_PLANS.includes(currentPlan)
? "Subscribe"
: "Login to SUBNET",
"callAction",
LICENSE_PLANS.ENTERPRISE,
)}
</Box>
</Box>
</PlanListContainer>
</ListContainer>
);
})}
</LicensesInformation>
</Fragment>
);
};

View File

@@ -14,444 +14,217 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import LicenseLink from "./LicenseLink";
import { openFAQModal } from "./licenseSlice";
import store from "../../../store";
import FAQModal from "./FAQModal";
import { Box } from "mds";
import React from "react";
import { ApplicationLogo } from "mds";
export const LICENSE_PLANS = {
COMMUNITY: "community",
STANDARD: "standard",
ENTERPRISE: "enterprise",
ENTERPRISE_LITE: "enterprise-lite",
ENTERPRISE_PLUS: "enterprise-plus",
};
export interface LicensePlanOption {
planId: string;
planName: string;
planType: "commercial" | "open-source";
planIcon: React.ReactNode;
planDescription: React.ReactNode;
}
type FeatureItem = {
label?: any;
isHeader?: boolean;
style?: any;
desc?: any;
featureTitleRow?: boolean;
};
export interface FeatureElementObject {
[name: string]: FeatureItem;
}
const FeatureLink = ({ text, anchor }: { text: string; anchor: string }) => {
return (
<a
href={`https://min.io/product/subnet?ref=con#${anchor}`}
className={"link-text"}
target="_blank"
rel="noopener "
style={{
color: "#2781B0",
}}
>
{text}
</a>
);
};
export interface FeatureItem {
content: React.ReactNode;
isCheck?: boolean;
}
export const FEATURE_ITEMS: FeatureItem[] = [
export interface PlansFeatures {
featureLabel: string;
featurePlans: FeatureElementObject;
}
export const FEATURE_ITEMS: PlansFeatures[] = [
{
label: "License ",
isHeader: true,
},
{
label: "",
isHeader: false,
style: {
height: "400px",
verticalAlign: "top",
alignItems: "start",
featureLabel: "License",
featurePlans: {
openSource: {
content: "Requires AGPLv3 License Compliance",
},
eosLite: {
content: "Commercial License",
},
eosPlus: {
content: "Commercial License",
},
},
},
{
desc: "Features",
featureTitleRow: true,
},
{
desc: "Unit Price",
},
{
desc: () => {
return (
<FeatureLink
anchor={"sa-long-term-support"}
text={"Software Release"}
/>
);
featureLabel: "Release",
featurePlans: {
openSource: {
content: "Upstream Community Release",
},
eosLite: {
content: "Enterprise Stable Release",
},
eosPlus: {
content: "Enterprise Stable Release",
},
},
},
{
desc: "SLA",
},
{
desc: "Support",
},
{
desc: "Critical Security and Bug Detection",
},
{
desc: () => {
return <FeatureLink anchor={"sa-panic-button"} text={"Panic Button"} />;
featureLabel: "Additional Features",
featurePlans: {
openSource: {
content: "None",
},
eosLite: {
content:
"Global Console, Observability, Cache, Data Firewall, Key Management Server Catalog",
},
eosPlus: {
content:
"Global Console, Observability, Cache, Data Firewall, Key Management Server Catalog",
},
},
},
{
desc: () => {
return (
<FeatureLink anchor={"sa-healthcheck"} text={"Health Diagnostics"} />
);
featureLabel: "Long Term Release Support",
featurePlans: {
openSource: {
content: "None",
},
eosLite: {
content: "1 year LTS",
},
eosPlus: {
content: "5 years LTS",
},
},
},
{
desc: "Annual Architecture Review",
featureLabel: "Support SLA",
featurePlans: {
openSource: {
content: "No SLA",
},
eosLite: {
content: "Next Business Day SLA",
},
eosPlus: {
content: "Less than 4 Hour SLA",
},
},
},
{
desc: "Annual Performance Review",
featureLabel: "Panic button",
featurePlans: {
openSource: {
content: "None",
},
eosLite: {
content: "1 Panic Button Per Year",
},
eosPlus: {
content: "Unlimited Panic Buttons Per Year",
},
},
},
{
desc: "Indemnification",
featureLabel:
"Call Home Diagnostics, Health Check, Performance Benchmark, Security and Critical Vulnerabilities Notifications",
featurePlans: {
openSource: {
content: "",
},
eosLite: {
content: "",
isCheck: true,
},
eosPlus: {
content: "",
isCheck: true,
},
},
},
{
desc: "Security and Policy Review",
featureLabel: "Indemnification",
featurePlans: {
openSource: {
content: "",
},
eosLite: {
content: "",
},
eosPlus: {
content: "",
isCheck: true,
},
},
},
{
featureLabel: "Annual Review of Architecture, Performance and Security",
featurePlans: {
openSource: {
content: "",
},
eosLite: {
content: "",
},
eosPlus: {
content: "",
isCheck: true,
},
},
},
];
export const COMMUNITY_PLAN_FEATURES = [
export const LICENSE_PLANS_INFORMATION: LicensePlanOption[] = [
{
label: "Community",
isHeader: true,
style: {
borderBottom: 0,
},
},
{
label: () => {
return (
<Box
sx={{
textAlign: "left",
}}
>
<span>
Designed for developers who are building open source applications in
compliance with the <LicenseLink /> license, MinIO Trademarks and
are able to self support themselves. It is fully featured. If you
distribute, host or create derivative works of the MinIO software
over the network, the <LicenseLink /> license requires that you also
distribute the complete, corresponding source code of the combined
work under the same <LicenseLink /> license. This requirement
applies whether or not you modified MinIO.
<br />
<br />
<span
className="link-text"
onClick={() => {
store.dispatch(openFAQModal());
}}
>
Compliance FAQ
</span>
<FAQModal />
</span>
</Box>
);
},
isHeader: false,
style: {
height: "400px",
borderBottom: 0,
},
},
{
id: "com_feat_title",
featureTitleRow: true,
},
{
id: "com_license_cost",
},
{
id: "com_release",
label: "Upstream",
},
{
id: "com_sla",
label: "No SLA",
},
{
id: "com_support",
label: "Community:",
detail: "Slack + GitHub",
},
{
id: "com_security",
label: "Self",
},
{
id: "com_panic",
xsLabel: "N/A",
},
{
id: "com_diag",
xsLabel: "N/A",
},
{
id: "com_arch",
xsLabel: "N/A",
},
{
id: "com_perf",
xsLabel: "N/A",
},
{
id: "com_indemnity",
xsLabel: "N/A",
},
{
id: "com_sec_policy",
xsLabel: "N/A",
},
];
export const STANDARD_PLAN_FEATURES = [
{
label: "Standard",
isHeader: true,
style: {
borderBottom: 0,
},
},
{
isHeader: false,
label: () => {
return (
<Box
sx={{
marginTop: "-85px",
textAlign: "left",
}}
>
<span>
Designed for customers who require a commercial license and can
mostly self-support but want the peace of mind that comes with the
MinIO Subscription Networks suite of operational capabilities and
direct-to-engineer interaction. The Standard version is fully
featured but with SLA limitations. <br /> <br /> To learn more about
the MinIO Subscription Network
</span>{" "}
<a
href="https://min.io/product/subnet?ref=con"
className={"link-text"}
target="_blank"
rel="noopener"
>
click here
</a>
.
</Box>
);
},
style: {
height: "400px",
borderBottom: 0,
},
},
{
id: "std_feat_title",
featureTitleRow: true,
},
{
id: "std_license_cost",
label: () => (
<Box
sx={{
fontSize: "16px",
fontWeight: 600,
}}
>
$10 per TiB per month
</Box>
planId: "openSource",
planName: "Open Source",
planType: "open-source",
planIcon: (
<ApplicationLogo applicationName={"console"} subVariant={"AGPL"} />
),
detail: () => (
<Box
sx={{
fontSize: "14px",
fontWeight: 400,
marginBottom: "5px",
}}
>
(Minimum of 200TiB)
</Box>
planDescription: (
<span>
Designed for developers who are building open source applications in
compliance with the GNU AGPL v3 license which requires developers to
distribute their code under the same AGPL v3 license when they
distribute, host or modify MinIO.
</span>
),
},
{
id: "std_release",
label: "1 Year Long Term Support",
},
{
id: "std_sla",
label: "<48 Hours",
detail: "(Local Business Hours)",
},
{
id: "std_support",
label: "L4 Direct Engineering",
detail: "support via SUBNET",
},
{
id: "std_security",
label: "Continuous Scan and Alert",
},
{
id: "std_panic",
label: "1 Per year",
},
{
id: "std_diag",
label: "24/7/365",
},
{
id: "std_arch",
xsLabel: "N/A",
},
{
id: "std_perf",
xsLabel: "N/A",
},
{
id: "std_indemnity",
xsLabel: "N/A",
},
{
id: "std_sec_policy",
xsLabel: "N/A",
},
];
export const ENTERPRISE_PLAN_FEATURES = [
{
label: "Enterprise",
isHeader: true,
style: {
borderBottom: 0,
},
},
{
isHeader: false,
label: () => {
return (
<Box
sx={{
marginTop: "-135px",
textAlign: "left",
}}
>
<span>
Designed for mission critical environments where both a license and
strict SLAs are required. The Enterprise version is fully featured
but comes with additional capabilities. <br /> <br /> To learn more
about the MinIO Subscription Network
</span>{" "}
<a
href="https://min.io/product/subnet?ref=con"
className={"link-text"}
target="_blank"
rel="noopener"
>
click here
</a>
.
</Box>
);
},
style: {
height: "400px",
borderBottom: 0,
},
},
{
id: "end_feat_title",
featureTitleRow: true,
},
{
id: "ent_license_cost",
label: () => (
<Box
sx={{
fontSize: "16px",
fontWeight: 600,
}}
>
$20 per TiB per month
</Box>
planId: "eosLite",
planName: "Enterprise Lite",
planType: "commercial",
planIcon: (
<ApplicationLogo applicationName={"minio"} subVariant={"enterpriseos"} />
),
detail: () => (
<Box
sx={{
fontSize: "14px",
fontWeight: 400,
marginBottom: "5px",
}}
>
(Minimum of 100TiB)
</Box>
planDescription: (
<span>
Designed for customers who require a commercial license and can mostly
self-support but want the peace of mind that comes with an
engineer-backend SLA, additional features and operational capabilities.
</span>
),
},
{
id: "ent_release",
label: "5 Years Long Term Support",
},
{
id: "ent_sla",
label: "<1 hour",
},
{
id: "ent_support",
label: "L4 Direct Engineering support via",
detail: "SUBNET, Phone, Web Conference",
},
{
id: "ent_security",
label: "Continuous Scan and Alert",
},
{
id: "ent_panic",
label: "Unlimited",
},
{
id: "ent_diag",
label: "24/7/365",
},
{
id: "ent_arch",
yesIcon: true,
},
{
id: "ent_perf",
yesIcon: true,
},
{
id: "ent_indemnity",
yesIcon: true,
},
{
id: "ent_sec_policy",
yesIcon: true,
planId: "eosPlus",
planName: "Enterprise Plus",
planType: "commercial",
planIcon: (
<ApplicationLogo applicationName={"minio"} subVariant={"enterpriseos"} />
),
planDescription: (
<span>
Designed for customers where a commercial license and the
strictest,engineer-backed SLA are required. The Plus tiers offers
additional features and operational capabilities, more interaction
options and more enterprise deliverables.
</span>
),
},
];
export const PAID_PLANS = [
LICENSE_PLANS.STANDARD,
LICENSE_PLANS.ENTERPRISE,
LICENSE_PLANS.ENTERPRISE_LITE,
LICENSE_PLANS.ENTERPRISE_PLUS,
];
export const getRenderValue = (val: any) => {
return typeof val === "function" ? val() : val;
};
export const LICENSE_CONSENT_STORE_KEY = "agpl_minio_license_consent";
export const setLicenseConsent = () => {
localStorage.setItem(LICENSE_CONSENT_STORE_KEY, "true");