[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 {useIntl} from 'react-intl';
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 TadaSvg from './tada.svg';
import BlankCardImage from './blank_card_image.svg';
export enum ButtonCustomiserClasses {
grayed = 'grayed',
@@ -38,7 +45,7 @@ type CardProps = {
topColor: string;
planLabel?: JSX.Element;
plan: string;
planSummary?: string;
planSummary?: ReactNode;
price?: string;
rate?: ReactNode;
planExtraInformation?: JSX.Element;
@@ -48,6 +55,8 @@ type CardProps = {
briefing: PlanBriefing;
planAddonsInfo?: PlanAddonsInfo;
planTrialDisclaimer?: JSX.Element;
isCloud: boolean;
cloudFreeDeprecated: boolean;
}
type StyledProps = {
@@ -58,28 +67,101 @@ const StyledDiv = styled.div<StyledProps>`
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) {
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 (
<div
id={props.id}
className='PlanCard'
>
{props.planLabel}
<StyledDiv
className='top'
bgColor={props.topColor}
/>
<div className='bottom'>
{(!props.cloudFreeDeprecated || !props.isCloud) && (
<StyledDiv
className='top'
bgColor={props.topColor}
/>
)}
<div className={bottomClassName}>
<div className='bottom_container'>
<div className='plan_price_rate_section'>
<div className={planPriceRateSectionClassName}>
<h3>{props.plan}</h3>
<p>{props.planSummary}</p>
{props.price ? <h1>{props.price}</h1> : <BuildingSvg/>}
<span>{props.rate}</span>
{props.price ? <h1>{props.price}</h1> : <div className={buildingImgClassName}><BuildingSvg/></div>}
{props.cloudFreeDeprecated ? (<span className='plan_rate'>{props.rate}</span>) : (<span>{props.rate}</span>)}
</div>
<div className='plan_limits_cta'>
<div className={planLimitsCtaClassName}>
{props.planExtraInformation}
</div>
@@ -96,7 +178,7 @@ function Card(props: CardProps) {
)}
</div>
<div className='contact_sales_cta'>
<div className={contactSalesCTAClassName}>
{props.contactSalesCTA && (
<div>
<p>{formatMessage({id: 'pricing_modal.or', defaultMessage: 'or'})}</p>
@@ -105,9 +187,9 @@ function Card(props: CardProps) {
</div>
<div className='plan_briefing'>
<hr/>
{!props.cloudFreeDeprecated && <hr/>}
{props.planTrialDisclaimer}
<div className='plan_briefing_content'>
<div className={planBriefingContentClassName}>
<span className='title'>{props.briefing.title}</span>
{props.briefing.items?.map((i) => {
return (

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

@@ -20,6 +20,7 @@ import {
} from 'mattermost-redux/selectors/entities/cloud';
import {isCurrentUserSystemAdmin} from 'mattermost-redux/selectors/entities/users';
import {DispatchFunc} from 'mattermost-redux/types/actions';
import {deprecateCloudFree} from 'mattermost-redux/selectors/entities/preferences';
import {Feedback} from '@mattermost/types/cloud';
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 StarterDisclaimerCTA from './starter_disclaimer_cta';
import StartTrialCaution from './start_trial_caution';
import Card, {ButtonCustomiserClasses} from './card';
import Card, {BlankCard, ButtonCustomiserClasses} from './card';
import './content.scss';
@@ -68,6 +69,8 @@ function Content(props: ContentProps) {
const subscription = useSelector(selectCloudSubscription);
const currentProduct = useSelector(selectSubscriptionProduct);
const products = useSelector(selectCloudProducts);
const cloudFreeDeprecated = useSelector(deprecateCloudFree);
const yearlyProducts = findOnlyYearlyProducts(products || {}); // pricing modal should now only show yearly products
const currentSubscriptionIsMonthly = currentProduct?.recurring_interval === RecurringIntervals.MONTH;
@@ -118,8 +121,20 @@ function Content(props: ContentProps) {
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 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 [openContactSupport] = useOpenCloudZendeskSupportForm('Workspace downgrade', '');
@@ -209,7 +224,7 @@ function Content(props: ContentProps) {
action: () => openPurchaseModal('click_pricing_modal_professional_card_upgrade_button'),
text: adminProfessionalTierText,
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 = () => {
if (isPostTrial && isAdmin) {
if (cloudFreeDeprecated || (isPostTrial && isAdmin)) {
return {
action: () => {
trackEvent(TELEMETRY_CATEGORIES.CLOUD_PRICING, 'click_enterprise_contact_sales');
@@ -270,7 +285,7 @@ function Content(props: ContentProps) {
};
const enterpriseCustomBtnDetails = () => {
if (!isPostTrial && isAdmin) {
if (!isPostTrial && isAdmin && !cloudFreeDeprecated) {
return (
<CloudStartTrialButton
message={formatMessage({id: 'pricing_modal.btn.tryDays', defaultMessage: 'Try free for {days} days'}, {days: '30'})}
@@ -311,99 +326,117 @@ function Content(props: ContentProps) {
/>
</Modal.Header>
<Modal.Body>
<div className='pricing-options-container'>
<div className='alert-option-container'>
<div className='alert-option'>
<span>{formatMessage({id: 'pricing_modal.lookingToSelfHost', defaultMessage: 'Looking to self-host?'})}</span>
<ExternalLink
onClick={() =>
trackEvent(
TELEMETRY_CATEGORIES.CLOUD_PURCHASING,
'click_looking_to_self_host',
)
}
href={CloudLinks.DEPLOYMENT_OPTIONS}
location='pricing_modal_content'
>{formatMessage({id: 'pricing_modal.reviewDeploymentOptions', defaultMessage: 'Review deployment options'})}</ExternalLink>
{!cloudFreeDeprecated && (
<div className='pricing-options-container'>
<div className='alert-option-container'>
<div className='alert-option'>
<span>{formatMessage({id: 'pricing_modal.lookingToSelfHost', defaultMessage: 'Looking to self-host?'})}</span>
<ExternalLink
onClick={() =>
trackEvent(
TELEMETRY_CATEGORIES.CLOUD_PURCHASING,
'click_looking_to_self_host',
)
}
href={CloudLinks.DEPLOYMENT_OPTIONS}
location='pricing_modal_content'
>{formatMessage({id: 'pricing_modal.reviewDeploymentOptions', defaultMessage: 'Review deployment options'})}</ExternalLink>
</div>
</div>
</div>
</div>
)}
<div className='PricingModal__body'>
<Card
id='free'
topColor='#339970'
plan='Free'
planSummary={formatMessage({id: 'pricing_modal.planSummary.free', defaultMessage: 'Increased productivity for small teams'})}
price='$0'
rate={formatMessage({id: 'pricing_modal.price.freeForever', defaultMessage: 'Free forever'})}
planLabel={
isStarter ? (
<PlanLabel
text={formatMessage({id: 'pricing_modal.planLabel.currentPlan', defaultMessage: 'CURRENT PLAN'})}
color='var(--denim-status-online)'
bgColor='var(--center-channel-bg)'
firstSvg={<CheckMarkSvg/>}
/>) : undefined}
planExtraInformation={<StarterDisclaimerCTA/>}
buttonDetails={{
action: () => {
if (!isStarter && !currentSubscriptionIsMonthly) {
openContactSupport();
return;
}
<div
className='PricingModal__body'
style={{marginTop: cloudFreeDeprecated ? '74px' : ''}}
>
{!cloudFreeDeprecated && (
<Card
id='free'
topColor='#339970'
plan='Free'
planSummary={formatMessage({id: 'pricing_modal.planSummary.free', defaultMessage: 'Increased productivity for small teams'})}
price='$0'
rate={formatMessage({id: 'pricing_modal.price.freeForever', defaultMessage: 'Free forever'})}
isCloud={true}
cloudFreeDeprecated={cloudFreeDeprecated}
planLabel={
isStarter ? (
<PlanLabel
text={formatMessage({id: 'pricing_modal.planLabel.currentPlan', defaultMessage: 'CURRENT PLAN'})}
color='var(--denim-status-online)'
bgColor='var(--center-channel-bg)'
firstSvg={<CheckMarkSvg/>}
/>) : undefined}
planExtraInformation={<StarterDisclaimerCTA/>}
buttonDetails={{
action: () => {
if (!isStarter && !currentSubscriptionIsMonthly) {
openContactSupport();
return;
}
if (!starterProduct) {
return;
}
if (!starterProduct) {
return;
}
if (usage.teams.active > 1) {
dispatch(
openModal({
modalId: ModalIdentifiers.CLOUD_DOWNGRADE_CHOOSE_TEAM,
dialogType: DowngradeTeamRemovalModal,
dialogProps: {
product_id: starterProduct?.id,
starterProduct,
},
}),
);
} else {
dispatch(
openModal({
modalId: ModalIdentifiers.FEEDBACK,
dialogType: DowngradeFeedbackModal,
dialogProps: {
onSubmit: handleClickDowngrade,
},
}),
);
}
},
text: freeTierText,
disabled: isStarter || isEnterprise || !isAdmin,
customClass: (isStarter || isEnterprise || !isAdmin) ? ButtonCustomiserClasses.grayed : ButtonCustomiserClasses.secondary,
}}
briefing={{
title: formatMessage({id: 'pricing_modal.briefing.title', defaultMessage: 'Top features'}),
items: hasLimits ? starterBriefing : legacyStarterBriefing,
}}
/>
if (usage.teams.active > 1) {
dispatch(
openModal({
modalId: ModalIdentifiers.CLOUD_DOWNGRADE_CHOOSE_TEAM,
dialogType: DowngradeTeamRemovalModal,
dialogProps: {
product_id: starterProduct?.id,
starterProduct,
},
}),
);
} else {
dispatch(
openModal({
modalId: ModalIdentifiers.FEEDBACK,
dialogType: DowngradeFeedbackModal,
dialogProps: {
onSubmit: handleClickDowngrade,
},
}),
);
}
},
text: freeTierText,
disabled: isStarter || isEnterprise || !isAdmin,
customClass: (isStarter || isEnterprise || !isAdmin) ? ButtonCustomiserClasses.grayed : ButtonCustomiserClasses.secondary,
}}
briefing={{
title: formatMessage({id: 'pricing_modal.briefing.title', defaultMessage: 'Top features'}),
items: hasLimits ? starterBriefing : legacyStarterBriefing,
}}
/>
)
}
<Card
id='professional'
topColor='var(--denim-button-bg)'
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}`}
rate={formatMessage({id: 'pricing_modal.rate.seatPerMonth', defaultMessage: 'USD per seat/month {br}<b>(billed annually)</b>'}, {
br: <br/>,
b: (chunks: React.ReactNode | React.ReactNodeArray) => (
<span style={{fontSize: '14px'}}>
<b>{chunks}</b>
<span className='billed_annually'>
{
cloudFreeDeprecated ? chunks : (<b>{chunks}</b>)
}
</span>
),
})}
isCloud={true}
cloudFreeDeprecated={cloudFreeDeprecated}
planLabel={isProfessional ? (
<PlanLabel
text={professionalPlanLabelText()}
@@ -413,7 +446,7 @@ function Content(props: ContentProps) {
/>) : undefined}
buttonDetails={professionalBtnDetails()}
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: [
formatMessage({id: 'pricing_modal.briefing.professional.messageBoardsIntegrationsCalls', defaultMessage: 'Unlimited access to messages and files'}),
formatMessage({id: 'pricing_modal.briefing.professional.unLimitedTeams', defaultMessage: 'Unlimited teams'}),
@@ -442,6 +475,8 @@ function Content(props: ContentProps) {
topColor='#E07315'
plan='Enterprise'
planSummary={formatMessage({id: 'pricing_modal.planSummary.enterprise', defaultMessage: 'Administration, security, and compliance for large teams'})}
isCloud={true}
cloudFreeDeprecated={cloudFreeDeprecated}
planLabel={
isEnterprise ? (
<PlanLabel
@@ -453,10 +488,10 @@ function Content(props: ContentProps) {
/>) : undefined}
buttonDetails={enterpriseBtnDetails()}
customButtonDetails={enterpriseCustomBtnDetails()}
planTrialDisclaimer={(!isPostTrial && isAdmin) ? <StartTrialCaution/> : undefined}
contactSalesCTA={(isPostTrial || !isAdmin) ? undefined : <ContactSalesCTA/>}
planTrialDisclaimer={(!isPostTrial && isAdmin && !cloudFreeDeprecated) ? <StartTrialCaution/> : undefined}
contactSalesCTA={(isPostTrial || !isAdmin || cloudFreeDeprecated) ? undefined : <ContactSalesCTA/>}
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: [
formatMessage({id: 'pricing_modal.briefing.enterprise.groupSync', defaultMessage: 'AD/LDAP group sync'}),
formatMessage({id: 'pricing_modal.briefing.enterprise.rolesAndPermissions', defaultMessage: 'Advanced roles and permissions'}),
@@ -480,6 +515,7 @@ function Content(props: ContentProps) {
],
}}
/>
{cloudFreeDeprecated && <BlankCard/>}
</div>
</Modal.Body>
</div>

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

@@ -128,6 +128,74 @@
margin-bottom: 111px;
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 {
position: relative;
width: 280px;
@@ -180,6 +248,7 @@
text-align: center;
h3 {
margin-top: 32px;
color: var(--center-channel-color);
font-family: 'Metropolis';
font-size: 24px;
@@ -210,6 +279,32 @@
font-weight: 400;
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 {
@@ -218,10 +313,19 @@
text-align: center;
}
.contact_sales_cta__reduced {
height: 32px;
margin-top: 0;
}
.plan_limits_cta {
height: 21px;
}
.plan_limits_cta__expanded {
height: 32px;
}
.plan_buttons {
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'})}
price='$0'
rate={formatMessage({id: 'pricing_modal.price.freeForever', defaultMessage: 'Free forever'})}
isCloud={false}
cloudFreeDeprecated={false}
planLabel={
isStarter ? (
<PlanLabel
@@ -209,16 +211,20 @@ function SelfHostedContent(props: ContentProps) {
id='professional'
topColor='var(--denim-button-bg)'
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}
rate={formatMessage({id: 'pricing_modal.rate.seatPerMonth', defaultMessage: 'USD per seat/month {br}<b>(billed annually)</b>'}, {
br: <br/>,
b: (chunks: React.ReactNode | React.ReactNodeArray) => (
<span style={{fontSize: '14px'}}>
<span className='billed_annually'>
<b>{chunks}</b>
</span>
),
})}
isCloud={false}
cloudFreeDeprecated={false}
planLabel={
isProfessional ? (
<PlanLabel
@@ -277,6 +283,8 @@ function SelfHostedContent(props: ContentProps) {
topColor='#E07315'
plan='Enterprise'
planSummary={formatMessage({id: 'pricing_modal.planSummary.enterprise', defaultMessage: 'Administration, security, and compliance for large teams'})}
isCloud={false}
cloudFreeDeprecated={false}
planLabel={
isEnterprise ? (
<PlanLabel

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

@@ -4563,23 +4563,29 @@
"pricing_modal.briefing.ssoWithGitLab": "SSO with Gitlab",
"pricing_modal.briefing.storageStarter": "{storage} file storage limit",
"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.unlimitedWorkspaceTeams": "Unlimited workspace teams",
"pricing_modal.btn.contactSales": "Contact Sales",
"pricing_modal.btn.contactSalesForQuote": "Contact Sales",
"pricing_modal.btn.contactSupport": "Contact Support",
"pricing_modal.btn.downgrade": "Downgrade",
"pricing_modal.btn.purchase": "Purchase",
"pricing_modal.btn.switch_to_annual": "Switch to annual billing",
"pricing_modal.btn.tooltip": "Only visible to system admins",
"pricing_modal.btn.tryDays": "Try free for {days} days",
"pricing_modal.btn.upgrade": "Upgrade",
"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.enterprise.playBookAnalytics": "Playbook analytics dashboard",
"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.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.interested_self_hosting": "Interested in self-hosting?",
"pricing_modal.learn_more": "Learn more",
"pricing_modal.lookingForCloudOption": "Looking for a cloud option?",
"pricing_modal.lookingToSelfHost": "Looking to self-host?",
"pricing_modal.noitfy_cta.request": "Request admin to upgrade",
@@ -4592,9 +4598,11 @@
"pricing_modal.planLabel.mostPopular": "MOST POPULAR",
"pricing_modal.planSummary.enterprise": "Administration, security, and compliance for large 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.questions": "Questions?",
"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.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",

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

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