Skip to content
This repository has been archived by the owner on Mar 13, 2024. It is now read-only.

MM-27454 - Contact Us and Billing Documentation Links #6731

Merged
merged 9 commits into from
Oct 13, 2020
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion components/admin_console/billing/billing_history.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import FormattedAdminHeader from 'components/widgets/admin_console/formatted_adm
import FormattedMarkdownMessage from 'components/formatted_markdown_message';
import noBillingHistoryGraphic from 'images/no_billing_history_graphic.svg';

import {CloudLinks} from 'utils/constants';

import './billing_history.scss';

type Props = {
Expand All @@ -29,7 +31,7 @@ const noBillingHistorySection = (
<a
target='_blank'
rel='noopener noreferrer'
href='http://www.google.com'
marianunez marked this conversation as resolved.
Show resolved Hide resolved
href={CloudLinks.BILLING_DOCS}
className='BillingHistory__noHistory-link'
>
<FormattedMessage
Expand Down
123 changes: 67 additions & 56 deletions components/admin_console/billing/billing_subscriptions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,88 +2,39 @@
// See LICENSE.txt for license information.

import React, {useState, useEffect} from 'react';
import {useDispatch, useStore} from 'react-redux';
import {useDispatch, useStore, useSelector} from 'react-redux';
import {FormattedMessage} from 'react-intl';

import {getCloudSubscription, getCloudProducts} from 'mattermost-redux/actions/cloud';
import {DispatchFunc} from 'mattermost-redux/types/actions';

import {GlobalState} from 'types/store';
import {getCloudContactUsLink, InquiryType} from 'selectors/cloud';

import AlertBanner from 'components/alert_banner';
import FormattedMarkdownMessage from 'components/formatted_markdown_message';
import FormattedAdminHeader from 'components/widgets/admin_console/formatted_admin_header';
import privateCloudImage from 'images/private-cloud-image.svg';
import upgradeMattermostCloudImage from 'images/upgrade-mattermost-cloud-image.svg';

import PlanDetails from './plan_details';
import BillingSummary from './billing_summary';

import './billing_subscriptions.scss';
import BillingSummary from './billing_summary';

type Props = {

};

const upgradeMattermostCloud = () => (
<div className='UpgradeMattermostCloud'>
<div className='UpgradeMattermostCloud__image'>
<img src={upgradeMattermostCloudImage}/>
</div>
<div className='UpgradeMattermostCloud__title'>
<FormattedMessage
id='admin.billing.subscription.upgradeMattermostCloud.title'
defaultMessage='Need more users?'
/>
</div>
<div className='UpgradeMattermostCloud__description'>
<FormattedMarkdownMessage
id='admin.billing.subscription.upgradeMattermostCloud.description'
defaultMessage='The free tier is **limited to 10 users.** Get access to more users, teams and other great features'
/>
</div>
<button className='UpgradeMattermostCloud__upgradeButton'>
<FormattedMessage
id='admin.billing.subscription.upgradeMattermostCloud.upgradeButton'
defaultMessage='Upgrade Mattermost Cloud'
/>
</button>
</div>
);

const privateCloudCard = () => (
<div className='PrivateCloudCard'>
<div className='PrivateCloudCard__text'>
<div className='PrivateCloudCard__text-title'>
<FormattedMessage
id='admin.billing.subscription.privateCloudCard.title'
defaultMessage='Looking for a high-trust private cloud?'
/>
</div>
<div className='PrivateCloudCard__text-description'>
<FormattedMessage
id='admin.billing.subscription.privateCloudCard.description'
defaultMessage='If you need software with dedicated, single-tenant architecture, Mattermost Private Cloud (Beta) is the solution for high-trust collaboration.'
/>
</div>
<button className='PrivateCloudCard__contactSales'>
<FormattedMessage
id='admin.billing.subscription.privateCloudCard.contactSales'
defaultMessage='Contact Sales'
/>
</button>
</div>
<div className='PrivateCloudCard__image'>
<img src={privateCloudImage}/>
</div>
</div>
);

// TODO: temp
const isFree = false;

const BillingSubscriptions: React.FC<Props> = () => {
const dispatch = useDispatch<DispatchFunc>();
const store = useStore();

const contactSalesLink = useSelector((state: GlobalState) => getCloudContactUsLink(state, InquiryType.Sales));

useEffect(() => {
getCloudSubscription()(dispatch, store.getState());
getCloudProducts()(dispatch, store.getState());
Expand All @@ -93,6 +44,66 @@ const BillingSubscriptions: React.FC<Props> = () => {
const [showWarning, setShowWarning] = useState(true);
const [showInfo, setShowInfo] = useState(true);

const upgradeMattermostCloud = () => (
<div className='UpgradeMattermostCloud'>
<div className='UpgradeMattermostCloud__image'>
<img src={upgradeMattermostCloudImage}/>
</div>
<div className='UpgradeMattermostCloud__title'>
<FormattedMessage
id='admin.billing.subscription.upgradeMattermostCloud.title'
defaultMessage='Need more users?'
/>
</div>
<div className='UpgradeMattermostCloud__description'>
<FormattedMarkdownMessage
id='admin.billing.subscription.upgradeMattermostCloud.description'
defaultMessage='The free tier is **limited to 10 users.** Get access to more users, teams and other great features'
/>
</div>
<button className='UpgradeMattermostCloud__upgradeButton'>
<FormattedMessage
id='admin.billing.subscription.upgradeMattermostCloud.upgradeButton'
defaultMessage='Upgrade Mattermost Cloud'
/>
</button>
</div>
);

const privateCloudCard = () => (
<div className='PrivateCloudCard'>
<div className='PrivateCloudCard__text'>
<div className='PrivateCloudCard__text-title'>
<FormattedMessage
id='admin.billing.subscription.privateCloudCard.title'
defaultMessage='Looking for a high-trust private cloud?'
/>
</div>
<div className='PrivateCloudCard__text-description'>
<FormattedMessage
id='admin.billing.subscription.privateCloudCard.description'
defaultMessage='If you need software with dedicated, single-tenant architecture, Mattermost Private Cloud (Beta) is the solution for high-trust collaboration.'
/>
</div>
<button className='PrivateCloudCard__contactSales'>
<a
href={contactSalesLink}
rel='noopener noreferrer'
target='_blank'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

all these _blank elements would be worth checking if they work as expected on the desktop client.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! Fixed them all to use target='_new' which does work properly on both webapp and desktop 👌

>
<FormattedMessage
id='admin.billing.subscription.privateCloudCard.contactSales'
defaultMessage='Contact Sales'
/>
</a>
</button>
</div>
<div className='PrivateCloudCard__image'>
<img src={privateCloudImage}/>
</div>
</div>
);

return (
<div className='wrapper--fixed BillingSubscriptions'>
<FormattedAdminHeader
Expand Down
4 changes: 3 additions & 1 deletion components/admin_console/billing/billing_summary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import React from 'react';
import {FormattedMessage} from 'react-intl';

import {CloudLinks} from 'utils/constants';

import noBillingHistoryGraphic from 'images/no_billing_history_graphic.svg';

import './billing_summary.scss';
Expand All @@ -29,7 +31,7 @@ const noBillingHistory = (
<a
target='_blank'
rel='noopener noreferrer'
href='http://www.google.com'
href={CloudLinks.BILLING_DOCS}
className='BillingSummary__noBillingHistory-link'
>
<FormattedMessage
Expand Down
13 changes: 12 additions & 1 deletion components/admin_console/billing/plan_details.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import OverlayTrigger from 'components/overlay_trigger';
import {getCurrentLocale} from 'selectors/i18n';
import {GlobalState} from 'types/store';
import {getMonthLong} from 'utils/i18n';
import {CloudLinks} from 'utils/constants';
import {localizeMessage} from 'utils/utils';

import './plan_details.scss';
Expand Down Expand Up @@ -67,8 +68,18 @@ const seatsAndSubscriptionDates = (locale: string, userCount: number, numberOfSe
<div className='BillingSubscriptions__tooltipMessage'>
<FormattedMarkdownMessage
id='admin.billing.subscription.planDetails.prolongedOverages'
defaultMessage='Prolonged overages may result in additional charges. [See how billing works](!https://google.com)'
defaultMessage='Prolonged overages may result in additional charges.'
/>
<a
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The markdown formatter already opens the link in a new window/tab, this link feels unnecessary.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I moved out the link from the formatted message so it was not part of the translation string though

target='_blank'
rel='noopener noreferrer'
href={CloudLinks.BILLING_DOCS}
>
<FormattedMarkdownMessage
id='admin.billing.subscription.planDetails.howBillingWorks'
defaultMessage='See how billing works'
/>
</a>
</div>
</Tooltip>
)}
Expand Down
15 changes: 7 additions & 8 deletions components/purchase_modal/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,33 @@

import {connect} from 'react-redux';
import {bindActionCreators, Dispatch, ActionCreatorsMapObject} from 'redux';
import {Stripe} from '@stripe/stripe-js';

import {getConfig} from 'mattermost-redux/selectors/entities/general';
import {GenericAction, ActionFunc} from 'mattermost-redux/types/actions';
import {Stripe} from '@stripe/stripe-js';

import {getCloudProducts} from 'mattermost-redux/actions/cloud';
import {getClientConfig} from 'mattermost-redux/actions/general';

import {
getClientConfig,
} from 'mattermost-redux/actions/general';

import {GlobalState} from 'types/store';
import {BillingDetails} from 'types/cloud/sku';

import {isModalOpen} from 'selectors/views/modals';
import {getCloudContactUsLink, InquiryType} from 'selectors/cloud';

import {ModalIdentifiers} from 'utils/constants';

import {closeModal} from 'actions/views/modals';
import {completeStripeAddPaymentMethod} from 'actions/cloud';

import {GlobalState} from 'types/store';

import PurchaseModal from './purchase_modal';

function mapStateToProps(state: GlobalState) {
return {
show: isModalOpen(state, ModalIdentifiers.CLOUD_PURCHASE),
products: state.entities.cloud!.products,
isDevMode: getConfig(state).EnableDeveloper === 'true',
contactSupportLink: getCloudContactUsLink(state, InquiryType.Technical),
contactSalesLink: getCloudContactUsLink(state, InquiryType.Sales),
};
}
type Actions = {
Expand Down
20 changes: 16 additions & 4 deletions components/purchase_modal/purchase_modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@ import RootPortal from 'components/root_portal';
import FullScreenModal from 'components/widgets/modals/full_screen_modal';
import {areBillingDetailsValid, BillingDetails} from 'types/cloud/sku';
import {getNextBillingDate} from 'utils/utils';
import {CloudLinks} from 'utils/constants';

import PaymentForm from '../payment_form/payment_form';

import ProcessPaymentSetup from './process_payment_setup';

import './purchase.scss';
import 'components/payment_form/payment_form.scss';
import ProcessPaymentSetup from './process_payment_setup';

const STRIPE_CSS_SRC = 'https://fonts.googleapis.com/css?family=Open+Sans:400,400i,600,600i&display=swap';
const STRIPE_PUBLIC_KEY = 'pk_test_ttEpW6dCHksKyfAFzh6MvgBj';
Expand All @@ -36,6 +38,8 @@ type Props = {
show: boolean;
isDevMode: boolean;
products?: Dictionary<Product>;
contactSupportLink: string;
contactSalesLink: string;
actions: {
closeModal: () => void;
getCloudProducts: () => void;
Expand Down Expand Up @@ -116,7 +120,9 @@ export default class PurchaseModal extends React.PureComponent<Props, State> {
</div>
<a
className='footer-text'
href='https://support.mattermost.com/hc/en-us/requests/new?ticket_form_id=360000640492'
href={this.props.contactSupportLink}
rel='noopener noreferrer'
target='_blank'
>
<FormattedMessage
defaultMessage={'Contact Support'}
Expand Down Expand Up @@ -169,7 +175,11 @@ export default class PurchaseModal extends React.PureComponent<Props, State> {
/>
</span>
{'\u00A0'}
<a href='https://support.mattermost.com/hc/en-us/requests/new?ticket_form_id=360000640492'>
<a
href={CloudLinks.BILLING_DOCS}
target='_blank'
marianunez marked this conversation as resolved.
Show resolved Hide resolved
rel='noopener noreferrer'
>
<FormattedMessage
defaultMessage={'See how billing works.'}
id={'admin.billing.subscription.howItWorks'}
Expand All @@ -180,7 +190,9 @@ export default class PurchaseModal extends React.PureComponent<Props, State> {
<div className='footer-text'>{'Need other billing options?'}</div>
<a
className='footer-text'
href='https://support.mattermost.com/hc/en-us/requests/new?ticket_form_id=360000640492'
href={this.props.contactSalesLink}
target='_blank'
rel='noopener noreferrer'
>
<FormattedMessage
defaultMessage={'Contact Sales'}
Expand Down
3 changes: 2 additions & 1 deletion i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -247,10 +247,11 @@
"admin.billing.subscription.planDetails.features.selfServiceDocumentation": "Self-Service documentation and forum support",
"admin.billing.subscription.planDetails.features.unlimitedIntegrations": "Unlimited Integrations",
"admin.billing.subscription.planDetails.features.unlimitedTeamsAndChannels": "Unlimited teams, channels, and search history",
"admin.billing.subscription.planDetails.howBillingWorks": "See how billing works",
"admin.billing.subscription.planDetails.numberOfSeats": "{numberOfSeats} seats",
"admin.billing.subscription.planDetails.numberOfSeatsRegistered": "({userCount} currently registered)",
"admin.billing.subscription.planDetails.perUserPerMonth": "/user/month",
"admin.billing.subscription.planDetails.prolongedOverages": "Prolonged overages may result in additional charges. [See how billing works](!https://google.com)",
"admin.billing.subscription.planDetails.prolongedOverages": "Prolonged overages may result in additional charges.",
"admin.billing.subscription.planDetails.seatCountOverages": "Seat count overages",
"admin.billing.subscription.planDetails.startDate": "Start Date: ",
"admin.billing.subscription.planDetails.tiers.free": "Free",
Expand Down
23 changes: 23 additions & 0 deletions selectors/cloud.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.

import {getConfig} from 'mattermost-redux/selectors/entities/general';
import {getCurrentUser} from 'mattermost-redux/selectors/entities/users';

import {GlobalState} from 'types/store';

export enum InquiryType {
Technical = 'technical',
Sales = 'sales',
Billing = 'billing'
}

export function getCloudContactUsLink(state: GlobalState, inquiry: InquiryType): string {
// cloud/contact-us with query params for name, email and inquiry
const cwsUrl = getConfig(state).CWSUrl;
const user = getCurrentUser(state);
const fullName = `${user.first_name} ${user.last_name}`;

return `${cwsUrl}/cloud/contact-us?email=${encodeURIComponent(user.email)}&name=${encodeURIComponent(fullName)}&inquiry=${inquiry}`;
}
4 changes: 4 additions & 0 deletions utils/constants.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -670,6 +670,10 @@ export const AboutLinks = {
PRIVACY_POLICY: 'https://about.mattermost.com/default-privacy-policy/',
};

export const CloudLinks = {
BILLING_DOCS: 'https://docs.mattermost.com/overview/mattermost-cloud-overview.html#how-billing-works',
};

export const PermissionsScope = {
[Permissions.INVITE_USER]: 'team_scope',
[Permissions.INVITE_GUEST]: 'team_scope',
Expand Down