[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 {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 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) {
|
||||
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 team’s 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 team’s 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 we’ll 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/',
|
||||
};
|
||||
|
||||
Ссылка в новой задаче
Block a user