[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>
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
21b72c7607
Коммит
5d122e5d7a
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
@@ -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 we’ll 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 team’s 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 team’s 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 we’ll 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/',
|
||||||
};
|
};
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user