[MM-51413] - Update Cloud pricing modal to remove mentions of Cloud Free (#22639)

* [MM-51413] - Update Cloud pricing modal to remove mentions of Cloud Free

* cleanup starter from pricing modal

* rm unused import

* enterprise button states

* fix purchase modal

* revert

* revert e2e

* revert

* revert

* revert

* revert

* revert

* revert

* add feature flag

* default FF to false

* revert

* fix feature flag self hosted checks

* add links

* feedback impl

* feedback impl

* fix lint

* feedback impl

* fix translation

* feedback impl

* feedback impl

* fix height

* improve alignment

---------

Co-authored-by: Mattermost Build <build@mattermost.com>
Этот коммит содержится в:
Allan Guwatudde
2023-04-14 00:15:38 +03:00
коммит произвёл GitHub
родитель 21b72c7607
Коммит 5d122e5d7a
7 изменённых файлов: 872 добавлений и 101 удалений

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

@@ -4,9 +4,16 @@
import React, {ReactNode} from 'react'; import React, {ReactNode} from 'react';
import {useIntl} from 'react-intl'; import {useIntl} from 'react-intl';
import styled from 'styled-components'; import styled from 'styled-components';
import classNames from 'classnames';
import useOpenSalesLink from 'components/common/hooks/useOpenSalesLink';
import ExternalLink from 'components/external_link';
import {HostedCustomerLinks} from 'utils/constants';
import BuildingSvg from './building.svg'; import BuildingSvg from './building.svg';
import TadaSvg from './tada.svg'; import TadaSvg from './tada.svg';
import BlankCardImage from './blank_card_image.svg';
export enum ButtonCustomiserClasses { export enum ButtonCustomiserClasses {
grayed = 'grayed', grayed = 'grayed',
@@ -38,7 +45,7 @@ type CardProps = {
topColor: string; topColor: string;
planLabel?: JSX.Element; planLabel?: JSX.Element;
plan: string; plan: string;
planSummary?: string; planSummary?: ReactNode;
price?: string; price?: string;
rate?: ReactNode; rate?: ReactNode;
planExtraInformation?: JSX.Element; planExtraInformation?: JSX.Element;
@@ -48,6 +55,8 @@ type CardProps = {
briefing: PlanBriefing; briefing: PlanBriefing;
planAddonsInfo?: PlanAddonsInfo; planAddonsInfo?: PlanAddonsInfo;
planTrialDisclaimer?: JSX.Element; planTrialDisclaimer?: JSX.Element;
isCloud: boolean;
cloudFreeDeprecated: boolean;
} }
type StyledProps = { type StyledProps = {
@@ -58,28 +67,101 @@ const StyledDiv = styled.div<StyledProps>`
background-color: ${(props) => props.bgColor}; background-color: ${(props) => props.bgColor};
`; `;
export function BlankCard() {
const {formatMessage} = useIntl();
const [, contactSalesLink] = useOpenSalesLink();
return (
<div className='BlankCard'>
<div className='image'>
<BlankCardImage/>
</div>
<div className='description'>
<div className='title'>
<span className='questions'>
{formatMessage({id: 'pricing_modal.questions', defaultMessage: 'Questions?'})}
</span>
<span className='contact'>
<ExternalLink
location='cloud_pricing_modal'
href={contactSalesLink}
>
{formatMessage({id: 'pricing_modal.contact_us', defaultMessage: 'Contact us'})}
</ExternalLink>
</span>
</div>
<div className='content'>
{formatMessage({id: 'pricing_modal.reach_out', defaultMessage: 'Reach out to us and well help you decide which plan is right for you and your organization.'})}
</div>
</div>
<hr/>
<div className='self-hosted-interest'>
<span className='interested'>
{formatMessage({id: 'pricing_modal.interested_self_hosting', defaultMessage: 'Interested in self-hosting?'})}
</span>
<span className='learn'>
<ExternalLink
location='cloud_pricing_modal'
href={HostedCustomerLinks.DOWNLOAD}
>
{formatMessage({id: 'pricing_modal.learn_more', defaultMessage: 'Learn more'})}
</ExternalLink>
</span>
</div>
</div>
);
}
function Card(props: CardProps) { function Card(props: CardProps) {
const {formatMessage} = useIntl(); const {formatMessage} = useIntl();
const bottomClassName = classNames('bottom', {
bottom__round: props.cloudFreeDeprecated && props.isCloud,
});
const contactSalesCTAClassName = classNames('contact_sales_cta', {
contact_sales_cta__reduced: props.cloudFreeDeprecated && props.isCloud,
});
const planBriefingContentClassName = classNames('plan_briefing_content', {
plan_briefing_content__reduced: props.cloudFreeDeprecated,
});
const planPriceRateSectionClassName = classNames('plan_price_rate_section', {
plan_price_rate_section__expanded: props.cloudFreeDeprecated,
});
const planLimitsCtaClassName = classNames('plan_limits_cta', {
plan_limits_cta__expanded: props.cloudFreeDeprecated,
});
const buildingImgClassName = classNames('building_img', {
building_img__expanded: props.cloudFreeDeprecated,
});
return ( return (
<div <div
id={props.id} id={props.id}
className='PlanCard' className='PlanCard'
> >
{props.planLabel} {props.planLabel}
{(!props.cloudFreeDeprecated || !props.isCloud) && (
<StyledDiv <StyledDiv
className='top' className='top'
bgColor={props.topColor} bgColor={props.topColor}
/> />
<div className='bottom'> )}
<div className={bottomClassName}>
<div className='bottom_container'> <div className='bottom_container'>
<div className='plan_price_rate_section'> <div className={planPriceRateSectionClassName}>
<h3>{props.plan}</h3> <h3>{props.plan}</h3>
<p>{props.planSummary}</p> <p>{props.planSummary}</p>
{props.price ? <h1>{props.price}</h1> : <BuildingSvg/>} {props.price ? <h1>{props.price}</h1> : <div className={buildingImgClassName}><BuildingSvg/></div>}
<span>{props.rate}</span> {props.cloudFreeDeprecated ? (<span className='plan_rate'>{props.rate}</span>) : (<span>{props.rate}</span>)}
</div> </div>
<div className='plan_limits_cta'> <div className={planLimitsCtaClassName}>
{props.planExtraInformation} {props.planExtraInformation}
</div> </div>
@@ -96,7 +178,7 @@ function Card(props: CardProps) {
)} )}
</div> </div>
<div className='contact_sales_cta'> <div className={contactSalesCTAClassName}>
{props.contactSalesCTA && ( {props.contactSalesCTA && (
<div> <div>
<p>{formatMessage({id: 'pricing_modal.or', defaultMessage: 'or'})}</p> <p>{formatMessage({id: 'pricing_modal.or', defaultMessage: 'or'})}</p>
@@ -105,9 +187,9 @@ function Card(props: CardProps) {
</div> </div>
<div className='plan_briefing'> <div className='plan_briefing'>
<hr/> {!props.cloudFreeDeprecated && <hr/>}
{props.planTrialDisclaimer} {props.planTrialDisclaimer}
<div className='plan_briefing_content'> <div className={planBriefingContentClassName}>
<span className='title'>{props.briefing.title}</span> <span className='title'>{props.briefing.title}</span>
{props.briefing.items?.map((i) => { {props.briefing.items?.map((i) => {
return ( return (

Просмотреть файл

@@ -20,6 +20,7 @@ import {
} from 'mattermost-redux/selectors/entities/cloud'; } from 'mattermost-redux/selectors/entities/cloud';
import {isCurrentUserSystemAdmin} from 'mattermost-redux/selectors/entities/users'; import {isCurrentUserSystemAdmin} from 'mattermost-redux/selectors/entities/users';
import {DispatchFunc} from 'mattermost-redux/types/actions'; import {DispatchFunc} from 'mattermost-redux/types/actions';
import {deprecateCloudFree} from 'mattermost-redux/selectors/entities/preferences';
import {Feedback} from '@mattermost/types/cloud'; import {Feedback} from '@mattermost/types/cloud';
import useGetUsage from 'components/common/hooks/useGetUsage'; import useGetUsage from 'components/common/hooks/useGetUsage';
@@ -44,7 +45,7 @@ import DowngradeTeamRemovalModal from './downgrade_team_removal_modal';
import ContactSalesCTA from './contact_sales_cta'; import ContactSalesCTA from './contact_sales_cta';
import StarterDisclaimerCTA from './starter_disclaimer_cta'; import StarterDisclaimerCTA from './starter_disclaimer_cta';
import StartTrialCaution from './start_trial_caution'; import StartTrialCaution from './start_trial_caution';
import Card, {ButtonCustomiserClasses} from './card'; import Card, {BlankCard, ButtonCustomiserClasses} from './card';
import './content.scss'; import './content.scss';
@@ -68,6 +69,8 @@ function Content(props: ContentProps) {
const subscription = useSelector(selectCloudSubscription); const subscription = useSelector(selectCloudSubscription);
const currentProduct = useSelector(selectSubscriptionProduct); const currentProduct = useSelector(selectSubscriptionProduct);
const products = useSelector(selectCloudProducts); const products = useSelector(selectCloudProducts);
const cloudFreeDeprecated = useSelector(deprecateCloudFree);
const yearlyProducts = findOnlyYearlyProducts(products || {}); // pricing modal should now only show yearly products const yearlyProducts = findOnlyYearlyProducts(products || {}); // pricing modal should now only show yearly products
const currentSubscriptionIsMonthly = currentProduct?.recurring_interval === RecurringIntervals.MONTH; const currentSubscriptionIsMonthly = currentProduct?.recurring_interval === RecurringIntervals.MONTH;
@@ -118,8 +121,20 @@ function Content(props: ContentProps) {
trial_notification: isPreTrial, trial_notification: isPreTrial,
}); });
const getAdminProfessionalBtnText = () => {
if (currentSubscriptionIsMonthlyProfessional) {
return formatMessage({id: 'pricing_modal.btn.switch_to_annual', defaultMessage: 'Switch to annual billing'});
}
if (cloudFreeDeprecated) {
return formatMessage({id: 'pricing_modal.btn.purchase', defaultMessage: 'Purchase'});
}
return formatMessage({id: 'pricing_modal.btn.upgrade', defaultMessage: 'Upgrade'});
};
const freeTierText = (!isStarter && !currentSubscriptionIsMonthly) ? formatMessage({id: 'pricing_modal.btn.contactSupport', defaultMessage: 'Contact Support'}) : formatMessage({id: 'pricing_modal.btn.downgrade', defaultMessage: 'Downgrade'}); const freeTierText = (!isStarter && !currentSubscriptionIsMonthly) ? formatMessage({id: 'pricing_modal.btn.contactSupport', defaultMessage: 'Contact Support'}) : formatMessage({id: 'pricing_modal.btn.downgrade', defaultMessage: 'Downgrade'});
const adminProfessionalTierText = currentSubscriptionIsMonthlyProfessional ? formatMessage({id: 'pricing_modal.btn.switch_to_annual', defaultMessage: 'Switch to annual billing'}) : formatMessage({id: 'pricing_modal.btn.upgrade', defaultMessage: 'Upgrade'}); const adminProfessionalTierText = getAdminProfessionalBtnText();
const [openContactSales] = useOpenSalesLink(); const [openContactSales] = useOpenSalesLink();
const [openContactSupport] = useOpenCloudZendeskSupportForm('Workspace downgrade', ''); const [openContactSupport] = useOpenCloudZendeskSupportForm('Workspace downgrade', '');
@@ -209,7 +224,7 @@ function Content(props: ContentProps) {
action: () => openPurchaseModal('click_pricing_modal_professional_card_upgrade_button'), action: () => openPurchaseModal('click_pricing_modal_professional_card_upgrade_button'),
text: adminProfessionalTierText, text: adminProfessionalTierText,
disabled: isProfessionalAnnual || (isEnterprise && !isEnterpriseTrial), disabled: isProfessionalAnnual || (isEnterprise && !isEnterpriseTrial),
customClass: isPostTrial ? ButtonCustomiserClasses.special : ButtonCustomiserClasses.active, customClass: (cloudFreeDeprecated || isPostTrial) ? ButtonCustomiserClasses.special : ButtonCustomiserClasses.active,
}; };
} }
@@ -234,7 +249,7 @@ function Content(props: ContentProps) {
}; };
const enterpriseBtnDetails = () => { const enterpriseBtnDetails = () => {
if (isPostTrial && isAdmin) { if (cloudFreeDeprecated || (isPostTrial && isAdmin)) {
return { return {
action: () => { action: () => {
trackEvent(TELEMETRY_CATEGORIES.CLOUD_PRICING, 'click_enterprise_contact_sales'); trackEvent(TELEMETRY_CATEGORIES.CLOUD_PRICING, 'click_enterprise_contact_sales');
@@ -270,7 +285,7 @@ function Content(props: ContentProps) {
}; };
const enterpriseCustomBtnDetails = () => { const enterpriseCustomBtnDetails = () => {
if (!isPostTrial && isAdmin) { if (!isPostTrial && isAdmin && !cloudFreeDeprecated) {
return ( return (
<CloudStartTrialButton <CloudStartTrialButton
message={formatMessage({id: 'pricing_modal.btn.tryDays', defaultMessage: 'Try free for {days} days'}, {days: '30'})} message={formatMessage({id: 'pricing_modal.btn.tryDays', defaultMessage: 'Try free for {days} days'}, {days: '30'})}
@@ -311,6 +326,7 @@ function Content(props: ContentProps) {
/> />
</Modal.Header> </Modal.Header>
<Modal.Body> <Modal.Body>
{!cloudFreeDeprecated && (
<div className='pricing-options-container'> <div className='pricing-options-container'>
<div className='alert-option-container'> <div className='alert-option-container'>
<div className='alert-option'> <div className='alert-option'>
@@ -328,8 +344,13 @@ function Content(props: ContentProps) {
</div> </div>
</div> </div>
</div> </div>
)}
<div className='PricingModal__body'> <div
className='PricingModal__body'
style={{marginTop: cloudFreeDeprecated ? '74px' : ''}}
>
{!cloudFreeDeprecated && (
<Card <Card
id='free' id='free'
topColor='#339970' topColor='#339970'
@@ -337,6 +358,8 @@ function Content(props: ContentProps) {
planSummary={formatMessage({id: 'pricing_modal.planSummary.free', defaultMessage: 'Increased productivity for small teams'})} planSummary={formatMessage({id: 'pricing_modal.planSummary.free', defaultMessage: 'Increased productivity for small teams'})}
price='$0' price='$0'
rate={formatMessage({id: 'pricing_modal.price.freeForever', defaultMessage: 'Free forever'})} rate={formatMessage({id: 'pricing_modal.price.freeForever', defaultMessage: 'Free forever'})}
isCloud={true}
cloudFreeDeprecated={cloudFreeDeprecated}
planLabel={ planLabel={
isStarter ? ( isStarter ? (
<PlanLabel <PlanLabel
@@ -390,20 +413,30 @@ function Content(props: ContentProps) {
}} }}
/> />
)
}
<Card <Card
id='professional' id='professional'
topColor='var(--denim-button-bg)' topColor='var(--denim-button-bg)'
plan='Professional' plan='Professional'
planSummary={formatMessage({id: 'pricing_modal.planSummary.professional', defaultMessage: 'Scalable solutions for growing teams'})} planSummary={formatMessage({id: 'pricing_modal.planSummary.professional', defaultMessage: 'Scalable solutions {br} for growing teams'}, {
br: <br/>,
})}
price={`$${professionalPrice}`} price={`$${professionalPrice}`}
rate={formatMessage({id: 'pricing_modal.rate.seatPerMonth', defaultMessage: 'USD per seat/month {br}<b>(billed annually)</b>'}, { rate={formatMessage({id: 'pricing_modal.rate.seatPerMonth', defaultMessage: 'USD per seat/month {br}<b>(billed annually)</b>'}, {
br: <br/>, br: <br/>,
b: (chunks: React.ReactNode | React.ReactNodeArray) => ( b: (chunks: React.ReactNode | React.ReactNodeArray) => (
<span style={{fontSize: '14px'}}> <span className='billed_annually'>
<b>{chunks}</b> {
cloudFreeDeprecated ? chunks : (<b>{chunks}</b>)
}
</span> </span>
), ),
})} })}
isCloud={true}
cloudFreeDeprecated={cloudFreeDeprecated}
planLabel={isProfessional ? ( planLabel={isProfessional ? (
<PlanLabel <PlanLabel
text={professionalPlanLabelText()} text={professionalPlanLabelText()}
@@ -413,7 +446,7 @@ function Content(props: ContentProps) {
/>) : undefined} />) : undefined}
buttonDetails={professionalBtnDetails()} buttonDetails={professionalBtnDetails()}
briefing={{ briefing={{
title: formatMessage({id: 'pricing_modal.briefing.title', defaultMessage: 'Top features'}), title: cloudFreeDeprecated ? formatMessage({id: 'pricing_modal.briefing.title_no_limit', defaultMessage: 'No limits on your teams usage'}) : formatMessage({id: 'pricing_modal.briefing.title', defaultMessage: 'Top features'}),
items: [ items: [
formatMessage({id: 'pricing_modal.briefing.professional.messageBoardsIntegrationsCalls', defaultMessage: 'Unlimited access to messages and files'}), formatMessage({id: 'pricing_modal.briefing.professional.messageBoardsIntegrationsCalls', defaultMessage: 'Unlimited access to messages and files'}),
formatMessage({id: 'pricing_modal.briefing.professional.unLimitedTeams', defaultMessage: 'Unlimited teams'}), formatMessage({id: 'pricing_modal.briefing.professional.unLimitedTeams', defaultMessage: 'Unlimited teams'}),
@@ -442,6 +475,8 @@ function Content(props: ContentProps) {
topColor='#E07315' topColor='#E07315'
plan='Enterprise' plan='Enterprise'
planSummary={formatMessage({id: 'pricing_modal.planSummary.enterprise', defaultMessage: 'Administration, security, and compliance for large teams'})} planSummary={formatMessage({id: 'pricing_modal.planSummary.enterprise', defaultMessage: 'Administration, security, and compliance for large teams'})}
isCloud={true}
cloudFreeDeprecated={cloudFreeDeprecated}
planLabel={ planLabel={
isEnterprise ? ( isEnterprise ? (
<PlanLabel <PlanLabel
@@ -453,10 +488,10 @@ function Content(props: ContentProps) {
/>) : undefined} />) : undefined}
buttonDetails={enterpriseBtnDetails()} buttonDetails={enterpriseBtnDetails()}
customButtonDetails={enterpriseCustomBtnDetails()} customButtonDetails={enterpriseCustomBtnDetails()}
planTrialDisclaimer={(!isPostTrial && isAdmin) ? <StartTrialCaution/> : undefined} planTrialDisclaimer={(!isPostTrial && isAdmin && !cloudFreeDeprecated) ? <StartTrialCaution/> : undefined}
contactSalesCTA={(isPostTrial || !isAdmin) ? undefined : <ContactSalesCTA/>} contactSalesCTA={(isPostTrial || !isAdmin || cloudFreeDeprecated) ? undefined : <ContactSalesCTA/>}
briefing={{ briefing={{
title: formatMessage({id: 'pricing_modal.briefing.title', defaultMessage: 'Top features'}), title: cloudFreeDeprecated ? formatMessage({id: 'pricing_modal.briefing.title_large_scale', defaultMessage: 'Large scale collaboration'}) : formatMessage({id: 'pricing_modal.briefing.title', defaultMessage: 'Top features'}),
items: [ items: [
formatMessage({id: 'pricing_modal.briefing.enterprise.groupSync', defaultMessage: 'AD/LDAP group sync'}), formatMessage({id: 'pricing_modal.briefing.enterprise.groupSync', defaultMessage: 'AD/LDAP group sync'}),
formatMessage({id: 'pricing_modal.briefing.enterprise.rolesAndPermissions', defaultMessage: 'Advanced roles and permissions'}), formatMessage({id: 'pricing_modal.briefing.enterprise.rolesAndPermissions', defaultMessage: 'Advanced roles and permissions'}),
@@ -480,6 +515,7 @@ function Content(props: ContentProps) {
], ],
}} }}
/> />
{cloudFreeDeprecated && <BlankCard/>}
</div> </div>
</Modal.Body> </Modal.Body>
</div> </div>

Просмотреть файл

@@ -128,6 +128,74 @@
margin-bottom: 111px; margin-bottom: 111px;
gap: 48px; gap: 48px;
.BlankCard {
position: relative;
width: 280px;
.image {
margin-top: 8px;
}
hr {
border-top: 1px solid rgba(var(--center-channel-color-rgb), 0.16);
}
.description {
border-bottom: 1px solid rgba(var(--center-channel-color-rgb) 0.16);
text-align: left;
.title {
margin-top: 24px;
margin-bottom: 8px;
font-family: 'Open Sans';
font-size: 14px;
font-style: normal;
font-weight: 600;
line-height: 20px;
.questions {
margin-right: 2px;
color: var(--center-channel-color);
}
.contact {
color: var(--denim-button-bg);
}
}
.content {
margin-bottom: 25px;
color: rgba(var(--center-channel-color-rgb), 0.72);
font-family: 'Open Sans';
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 20px;
}
}
.self-hosted-interest {
padding: 8px;
margin-top: 24px;
background: rgba(var(--denim-button-bg-rgb), 0.08);
border-radius: 8px;
font-family: 'Open Sans';
font-size: 14px;
font-style: normal;
font-weight: 600;
line-height: 20px;
.interested {
margin-right: 2px;
color: var(--center-channel-color);
}
.learn {
color: var(--denim-button-bg);
}
}
}
.PlanCard { .PlanCard {
position: relative; position: relative;
width: 280px; width: 280px;
@@ -180,6 +248,7 @@
text-align: center; text-align: center;
h3 { h3 {
margin-top: 32px;
color: var(--center-channel-color); color: var(--center-channel-color);
font-family: 'Metropolis'; font-family: 'Metropolis';
font-size: 24px; font-size: 24px;
@@ -210,6 +279,32 @@
font-weight: 400; font-weight: 400;
line-height: 24px; line-height: 24px;
} }
.plan_rate {
margin-bottom: 0;
}
.building_img {
padding-top: 0;
}
.building_img__expanded {
padding-top: 14px;
}
.billed_annually {
font-family: 'Open sans', sans-serif;
font-size: 16px;
}
}
.plan_price_rate_section__expanded {
height: 244px;
span,
p {
margin-bottom: 45px;
}
} }
.contact_sales_cta { .contact_sales_cta {
@@ -218,10 +313,19 @@
text-align: center; text-align: center;
} }
.contact_sales_cta__reduced {
height: 32px;
margin-top: 0;
}
.plan_limits_cta { .plan_limits_cta {
height: 21px; height: 21px;
} }
.plan_limits_cta__expanded {
height: 32px;
}
.plan_buttons { .plan_buttons {
height: auto; height: auto;
@@ -306,6 +410,10 @@
} }
} }
} }
.plan_briefing_content__reduced {
margin-top: 0;
}
} }
} }
@@ -355,6 +463,11 @@
} }
} }
} }
.bottom__round {
border: 1px solid rgba(var(--center-channel-color-rgb), 0.16);
border-radius: 8px;
}
} }
} }
} }

Просмотреть файл

@@ -186,6 +186,8 @@ function SelfHostedContent(props: ContentProps) {
planSummary={formatMessage({id: 'pricing_modal.planSummary.free', defaultMessage: 'Increased productivity for small teams'})} planSummary={formatMessage({id: 'pricing_modal.planSummary.free', defaultMessage: 'Increased productivity for small teams'})}
price='$0' price='$0'
rate={formatMessage({id: 'pricing_modal.price.freeForever', defaultMessage: 'Free forever'})} rate={formatMessage({id: 'pricing_modal.price.freeForever', defaultMessage: 'Free forever'})}
isCloud={false}
cloudFreeDeprecated={false}
planLabel={ planLabel={
isStarter ? ( isStarter ? (
<PlanLabel <PlanLabel
@@ -209,16 +211,20 @@ function SelfHostedContent(props: ContentProps) {
id='professional' id='professional'
topColor='var(--denim-button-bg)' topColor='var(--denim-button-bg)'
plan='Professional' plan='Professional'
planSummary={formatMessage({id: 'pricing_modal.planSummary.professional', defaultMessage: 'Scalable solutions for growing teams'})} planSummary={formatMessage({id: 'pricing_modal.planSummary.professional', defaultMessage: 'Scalable solutions {br} for growing teams'}, {
br: <br/>,
})}
price={professionalPrice} price={professionalPrice}
rate={formatMessage({id: 'pricing_modal.rate.seatPerMonth', defaultMessage: 'USD per seat/month {br}<b>(billed annually)</b>'}, { rate={formatMessage({id: 'pricing_modal.rate.seatPerMonth', defaultMessage: 'USD per seat/month {br}<b>(billed annually)</b>'}, {
br: <br/>, br: <br/>,
b: (chunks: React.ReactNode | React.ReactNodeArray) => ( b: (chunks: React.ReactNode | React.ReactNodeArray) => (
<span style={{fontSize: '14px'}}> <span className='billed_annually'>
<b>{chunks}</b> <b>{chunks}</b>
</span> </span>
), ),
})} })}
isCloud={false}
cloudFreeDeprecated={false}
planLabel={ planLabel={
isProfessional ? ( isProfessional ? (
<PlanLabel <PlanLabel
@@ -277,6 +283,8 @@ function SelfHostedContent(props: ContentProps) {
topColor='#E07315' topColor='#E07315'
plan='Enterprise' plan='Enterprise'
planSummary={formatMessage({id: 'pricing_modal.planSummary.enterprise', defaultMessage: 'Administration, security, and compliance for large teams'})} planSummary={formatMessage({id: 'pricing_modal.planSummary.enterprise', defaultMessage: 'Administration, security, and compliance for large teams'})}
isCloud={false}
cloudFreeDeprecated={false}
planLabel={ planLabel={
isEnterprise ? ( isEnterprise ? (
<PlanLabel <PlanLabel

Просмотреть файл

@@ -4563,23 +4563,29 @@
"pricing_modal.briefing.ssoWithGitLab": "SSO with Gitlab", "pricing_modal.briefing.ssoWithGitLab": "SSO with Gitlab",
"pricing_modal.briefing.storageStarter": "{storage} file storage limit", "pricing_modal.briefing.storageStarter": "{storage} file storage limit",
"pricing_modal.briefing.title": "Top features", "pricing_modal.briefing.title": "Top features",
"pricing_modal.briefing.title_large_scale": "Large scale collaboration",
"pricing_modal.briefing.title_no_limit": "No limits on your teams usage",
"pricing_modal.briefing.unlimitedPlaybookRuns": "Unlimited playbooks and runs", "pricing_modal.briefing.unlimitedPlaybookRuns": "Unlimited playbooks and runs",
"pricing_modal.briefing.unlimitedWorkspaceTeams": "Unlimited workspace teams", "pricing_modal.briefing.unlimitedWorkspaceTeams": "Unlimited workspace teams",
"pricing_modal.btn.contactSales": "Contact Sales", "pricing_modal.btn.contactSales": "Contact Sales",
"pricing_modal.btn.contactSalesForQuote": "Contact Sales", "pricing_modal.btn.contactSalesForQuote": "Contact Sales",
"pricing_modal.btn.contactSupport": "Contact Support", "pricing_modal.btn.contactSupport": "Contact Support",
"pricing_modal.btn.downgrade": "Downgrade", "pricing_modal.btn.downgrade": "Downgrade",
"pricing_modal.btn.purchase": "Purchase",
"pricing_modal.btn.switch_to_annual": "Switch to annual billing", "pricing_modal.btn.switch_to_annual": "Switch to annual billing",
"pricing_modal.btn.tooltip": "Only visible to system admins", "pricing_modal.btn.tooltip": "Only visible to system admins",
"pricing_modal.btn.tryDays": "Try free for {days} days", "pricing_modal.btn.tryDays": "Try free for {days} days",
"pricing_modal.btn.upgrade": "Upgrade", "pricing_modal.btn.upgrade": "Upgrade",
"pricing_modal.btn.viewPlans": "View plans", "pricing_modal.btn.viewPlans": "View plans",
"pricing_modal.contact_us": "Contact us",
"pricing_modal.extra_briefing.cloud.free.calls": "Group calls of up to 8 people, 1:1 calls, and screen share", "pricing_modal.extra_briefing.cloud.free.calls": "Group calls of up to 8 people, 1:1 calls, and screen share",
"pricing_modal.extra_briefing.enterprise.playBookAnalytics": "Playbook analytics dashboard", "pricing_modal.extra_briefing.enterprise.playBookAnalytics": "Playbook analytics dashboard",
"pricing_modal.extra_briefing.free.calls": "Voice calls and screen share", "pricing_modal.extra_briefing.free.calls": "Voice calls and screen share",
"pricing_modal.extra_briefing.professional.guestAccess": "Guest access with MFA enforcement", "pricing_modal.extra_briefing.professional.guestAccess": "Guest access with MFA enforcement",
"pricing_modal.extra_briefing.professional.ssoadLdap": "SSO support with AD/LDAP, Google, O365, OpenID", "pricing_modal.extra_briefing.professional.ssoadLdap": "SSO support with AD/LDAP, Google, O365, OpenID",
"pricing_modal.extra_briefing.professional.ssoSaml": "SSO with SAML 2.0, including Okta, OneLogin, and ADFS", "pricing_modal.extra_briefing.professional.ssoSaml": "SSO with SAML 2.0, including Okta, OneLogin, and ADFS",
"pricing_modal.interested_self_hosting": "Interested in self-hosting?",
"pricing_modal.learn_more": "Learn more",
"pricing_modal.lookingForCloudOption": "Looking for a cloud option?", "pricing_modal.lookingForCloudOption": "Looking for a cloud option?",
"pricing_modal.lookingToSelfHost": "Looking to self-host?", "pricing_modal.lookingToSelfHost": "Looking to self-host?",
"pricing_modal.noitfy_cta.request": "Request admin to upgrade", "pricing_modal.noitfy_cta.request": "Request admin to upgrade",
@@ -4592,9 +4598,11 @@
"pricing_modal.planLabel.mostPopular": "MOST POPULAR", "pricing_modal.planLabel.mostPopular": "MOST POPULAR",
"pricing_modal.planSummary.enterprise": "Administration, security, and compliance for large teams", "pricing_modal.planSummary.enterprise": "Administration, security, and compliance for large teams",
"pricing_modal.planSummary.free": "Increased productivity for small teams", "pricing_modal.planSummary.free": "Increased productivity for small teams",
"pricing_modal.planSummary.professional": "Scalable solutions for growing teams", "pricing_modal.planSummary.professional": "Scalable solutions {br} for growing teams",
"pricing_modal.price.freeForever": "Free forever", "pricing_modal.price.freeForever": "Free forever",
"pricing_modal.questions": "Questions?",
"pricing_modal.rate.seatPerMonth": "USD per seat/month {br}<b>(billed annually)</b>", "pricing_modal.rate.seatPerMonth": "USD per seat/month {br}<b>(billed annually)</b>",
"pricing_modal.reach_out": "Reach out to us and well help you decide which plan is right for you and your organization.",
"pricing_modal.reviewDeploymentOptions": "Review deployment options", "pricing_modal.reviewDeploymentOptions": "Review deployment options",
"pricing_modal.start_trial.disclaimer": "By selecting <span>Try free for 30 days,</span> I agree to the <linkAgreement>Mattermost Software and Services License Agreement</linkAgreement>, <linkPrivacy>Privacy Policy</linkPrivacy>, and receiving product emails.", "pricing_modal.start_trial.disclaimer": "By selecting <span>Try free for 30 days,</span> I agree to the <linkAgreement>Mattermost Software and Services License Agreement</linkAgreement>, <linkPrivacy>Privacy Policy</linkPrivacy>, and receiving product emails.",
"pricing_modal.subtitle": "Choose a plan to get started", "pricing_modal.subtitle": "Choose a plan to get started",

Просмотреть файл

@@ -1080,6 +1080,7 @@ export const HostedCustomerLinks = {
SELF_HOSTED_BILLING: 'https://docs.mattermost.com/manage/self-hosted-billing.html', SELF_HOSTED_BILLING: 'https://docs.mattermost.com/manage/self-hosted-billing.html',
TERMS_AND_CONDITIONS: 'https://mattermost.com/enterprise-edition-terms/', TERMS_AND_CONDITIONS: 'https://mattermost.com/enterprise-edition-terms/',
SECURITY_UPDATES: 'https://mattermost.com/security-updates/', SECURITY_UPDATES: 'https://mattermost.com/security-updates/',
DOWNLOAD: 'https://mattermost.com/download',
NEWSLETTER_UNSUBSCRIBE_LINK: 'https://forms.mattermost.com/UnsubscribePage.html', NEWSLETTER_UNSUBSCRIBE_LINK: 'https://forms.mattermost.com/UnsubscribePage.html',
PRIVACY: 'https://mattermost.com/privacy-policy/', PRIVACY: 'https://mattermost.com/privacy-policy/',
}; };