move self hosted purchase modals into one folder for shared resources, update banners to redirect to the self hosted purchase modal (in the case of air gapped, link to CWS. In the future, this will be a modal).

Этот коммит содержится в:
Conor Macpherson
2023-04-03 17:02:54 -04:00
родитель 09e93fc5e4
Коммит 0ce1b6c12c
32 изменённых файлов: 214 добавлений и 40 удалений

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

@@ -23,6 +23,8 @@ import useOpenPricingModal from 'components/common/hooks/useOpenPricingModal';
import useCanSelfHostedExpand from 'components/common/hooks/useCanSelfHostedExpand'; import useCanSelfHostedExpand from 'components/common/hooks/useCanSelfHostedExpand';
import {getExpandSeatsLink} from 'selectors/cloud'; import {getExpandSeatsLink} from 'selectors/cloud';
import useControlSelfHostedExpansionModal from 'components/common/hooks/useControlSelfHostedExpansionModal'; import useControlSelfHostedExpansionModal from 'components/common/hooks/useControlSelfHostedExpansionModal';
import {useQuery} from 'utils/http_utils';
import {STORAGE_KEY_EXPANSION_IN_PROGRESS} from 'components/self_hosted_purchases/constants';
const DAYS_UNTIL_EXPIRY_WARNING_DISPLAY_THRESHOLD = 30; const DAYS_UNTIL_EXPIRY_WARNING_DISPLAY_THRESHOLD = 30;
const DAYS_UNTIL_EXPIRY_DANGER_DISPLAY_THRESHOLD = 5; const DAYS_UNTIL_EXPIRY_DANGER_DISPLAY_THRESHOLD = 5;
@@ -58,6 +60,19 @@ const EnterpriseEditionLeftPanel = ({
const canExpand = useCanSelfHostedExpand(); const canExpand = useCanSelfHostedExpand();
const selfHostedExpansionModal = useControlSelfHostedExpansionModal({trackingLocation: 'license_settings_add_seats'}); const selfHostedExpansionModal = useControlSelfHostedExpansionModal({trackingLocation: 'license_settings_add_seats'});
const expandableLink = useSelector(getExpandSeatsLink); const expandableLink = useSelector(getExpandSeatsLink);
const isSelfHostedExpansionEnabled = useSelector(getConfig)?.ServiceSettings?.SelfHostedExpansion;
const query = useQuery();
const actionQueryParam = query.get('action');
useEffect(() => {
console.log(actionQueryParam);
if (actionQueryParam === 'show_expansion_modal' && canExpand && isSelfHostedExpansionEnabled) {
console.log("Open modal!");
selfHostedExpansionModal.open();
query.set('action', '');
}
}, [])
useEffect(() => { useEffect(() => {
async function fetchUnSanitizedLicense() { async function fetchUnSanitizedLicense() {
@@ -73,7 +88,6 @@ const EnterpriseEditionLeftPanel = ({
const skuName = getSkuDisplayName(unsanitizedLicense.SkuShortName, unsanitizedLicense.IsGovSku === 'true'); const skuName = getSkuDisplayName(unsanitizedLicense.SkuShortName, unsanitizedLicense.IsGovSku === 'true');
const expirationDays = getRemainingDaysFromFutureTimestamp(parseInt(unsanitizedLicense.ExpiresAt, 10)); const expirationDays = getRemainingDaysFromFutureTimestamp(parseInt(unsanitizedLicense.ExpiresAt, 10));
const isSelfHostedExpansionEnabled = useSelector(getConfig)?.ServiceSettings?.SelfHostedExpansion;
const viewPlansButton = ( const viewPlansButton = (
<button <button

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

@@ -4,6 +4,7 @@
import React, {useMemo} from 'react'; import React, {useMemo} from 'react';
import {FormattedMessage} from 'react-intl'; import {FormattedMessage} from 'react-intl';
import {useDispatch, useSelector} from 'react-redux'; import {useDispatch, useSelector} from 'react-redux';
import {useHistory} from 'react-router-dom';
import {getCurrentUser, isCurrentUserSystemAdmin} from 'mattermost-redux/selectors/entities/users'; import {getCurrentUser, isCurrentUserSystemAdmin} from 'mattermost-redux/selectors/entities/users';
import {GlobalState} from 'types/store'; import {GlobalState} from 'types/store';
@@ -16,9 +17,10 @@ import {makeGetCategory} from 'mattermost-redux/selectors/entities/preferences';
import {PreferenceType} from '@mattermost/types/preferences'; import {PreferenceType} from '@mattermost/types/preferences';
import {useExpandOverageUsersCheck} from 'components/common/hooks/useExpandOverageUsersCheck'; import {useExpandOverageUsersCheck} from 'components/common/hooks/useExpandOverageUsersCheck';
import useOpenSalesLink from 'components/common/hooks/useOpenSalesLink'; import useOpenSalesLink from 'components/common/hooks/useOpenSalesLink';
import {StatTypes, Preferences, AnnouncementBarTypes} from 'utils/constants'; import {StatTypes, Preferences, AnnouncementBarTypes, ConsolePages} from 'utils/constants';
import './overage_users_banner.scss'; import './overage_users_banner.scss';
import useCWSAvailabilityCheck from 'components/common/hooks/useCWSAvailabilityCheck';
type AdminHasDismissedItArgs = { type AdminHasDismissedItArgs = {
preferenceName: string; preferenceName: string;
@@ -56,6 +58,8 @@ const OverageUsersBanner = () => {
const prefixPreferences = isOver10PercerntPurchasedSeats ? 'error' : 'warn'; const prefixPreferences = isOver10PercerntPurchasedSeats ? 'error' : 'warn';
const prefixLicenseId = (license.Id || '').substring(0, 8); const prefixLicenseId = (license.Id || '').substring(0, 8);
const preferenceName = `${prefixPreferences}_overage_seats_${prefixLicenseId}`; const preferenceName = `${prefixPreferences}_overage_seats_${prefixLicenseId}`;
const history = useHistory();
const isAirGapped = !useCWSAvailabilityCheck();
const overageByUsers = activeUsers - seatsPurchased; const overageByUsers = activeUsers - seatsPurchased;
@@ -86,7 +90,14 @@ const OverageUsersBanner = () => {
const handleUpdateSeatsSelfServeClick = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => { const handleUpdateSeatsSelfServeClick = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
e.preventDefault(); e.preventDefault();
trackEventFn('Self Serve'); trackEventFn('Self Serve');
window.open(expandableLink(license.Id), '_blank');
if (isAirGapped) {
window.open(expandableLink(license.Id), '_blank');
}
if (isExpandable) {
history.push(`${ConsolePages.LICENSE}?action=show_expansion_modal`);
}
}; };
const handleContactSalesClick = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => { const handleContactSalesClick = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {

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

@@ -12,8 +12,8 @@ import {Client4} from 'mattermost-redux/client';
import {getCurrentUser} from 'mattermost-redux/selectors/entities/common'; import {getCurrentUser} from 'mattermost-redux/selectors/entities/common';
import {HostedCustomerTypes} from 'mattermost-redux/action_types'; import {HostedCustomerTypes} from 'mattermost-redux/action_types';
import {STORAGE_KEY_EXPANSION_IN_PROGRESS} from 'components/self_hosted_expansion_modal/constants'; import {STORAGE_KEY_EXPANSION_IN_PROGRESS} from 'components/self_hosted_purchases/constants';
import SelfHostedExpansionModal from 'components/self_hosted_expansion_modal'; import SelfHostedExpansionModal from 'components/self_hosted_purchases/self_hosted_expansion_modal';
import {useControlModal, ControlModal} from './useControlModal'; import {useControlModal, ControlModal} from './useControlModal';
import useCanSelfHostedExpand from './useCanSelfHostedExpand'; import useCanSelfHostedExpand from './useCanSelfHostedExpand';

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

@@ -7,8 +7,8 @@ import {useDispatch, useSelector} from 'react-redux';
import {trackEvent} from 'actions/telemetry_actions'; import {trackEvent} from 'actions/telemetry_actions';
import {closeModal, openModal} from 'actions/views/modals'; import {closeModal, openModal} from 'actions/views/modals';
import {ModalIdentifiers, TELEMETRY_CATEGORIES} from 'utils/constants'; import {ModalIdentifiers, TELEMETRY_CATEGORIES} from 'utils/constants';
import SelfHostedPurchaseModal from 'components/self_hosted_purchase_modal'; import SelfHostedPurchaseModal from 'components/self_hosted_purchases/self_hosted_purchase_modal';
import {STORAGE_KEY_PURCHASE_IN_PROGRESS} from 'components/self_hosted_purchase_modal/constants'; import {STORAGE_KEY_PURCHASE_IN_PROGRESS} from 'components/self_hosted_purchases/constants';
import PurchaseInProgressModal from 'components/purchase_in_progress_modal'; import PurchaseInProgressModal from 'components/purchase_in_progress_modal';
import {Client4} from 'mattermost-redux/client'; import {Client4} from 'mattermost-redux/client';
import {getCurrentUser} from 'mattermost-redux/selectors/entities/common'; import {getCurrentUser} from 'mattermost-redux/selectors/entities/common';

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

@@ -16,10 +16,13 @@ import {savePreferences} from 'mattermost-redux/actions/preferences';
import {makeGetCategory} from 'mattermost-redux/selectors/entities/preferences'; import {makeGetCategory} from 'mattermost-redux/selectors/entities/preferences';
import {PreferenceType} from '@mattermost/types/preferences'; import {PreferenceType} from '@mattermost/types/preferences';
import {useExpandOverageUsersCheck} from 'components/common/hooks/useExpandOverageUsersCheck'; import {useExpandOverageUsersCheck} from 'components/common/hooks/useExpandOverageUsersCheck';
import {LicenseLinks, StatTypes, Preferences} from 'utils/constants'; import {LicenseLinks, StatTypes, Preferences, ConsolePages} from 'utils/constants';
import './overage_users_banner_notice.scss'; import './overage_users_banner_notice.scss';
import ExternalLink from 'components/external_link'; import ExternalLink from 'components/external_link';
import useControlSelfHostedExpansionModal from 'components/common/hooks/useControlSelfHostedExpansionModal';
import {NavLink} from 'react-router-dom';
import useCWSAvailabilityCheck from 'components/common/hooks/useCWSAvailabilityCheck';
type AdminHasDismissedArgs = { type AdminHasDismissedArgs = {
preferenceName: string; preferenceName: string;
@@ -53,15 +56,16 @@ const OverageUsersBannerNotice = () => {
const prefixLicenseId = (license.Id || '').substring(0, 8); const prefixLicenseId = (license.Id || '').substring(0, 8);
const preferenceName = `${prefixPreferences}_overage_seats_${prefixLicenseId}`; const preferenceName = `${prefixPreferences}_overage_seats_${prefixLicenseId}`;
const isAirGapped = !useCWSAvailabilityCheck();
const overageByUsers = activeUsers - seatsPurchased; const overageByUsers = activeUsers - seatsPurchased;
const isOverageState = isBetween5PercerntAnd10PercentPurchasedSeats || isOver10PercerntPurchasedSeats; const isOverageState = isBetween5PercerntAnd10PercentPurchasedSeats || isOver10PercerntPurchasedSeats;
const hasPermission = isAdmin && isOverageState && !isCloud; const hasPermission = isAdmin && isOverageState && !isCloud;
const { const {
cta, cta,
expandableLink,
trackEventFn, trackEventFn,
getRequestState, getRequestState,
isExpandable, isExpandable,
expandableLink,
} = useExpandOverageUsersCheck({ } = useExpandOverageUsersCheck({
shouldRequest: hasPermission && !adminHasDismissed({overagePreferences, preferenceName}), shouldRequest: hasPermission && !adminHasDismissed({overagePreferences, preferenceName}),
licenseId: license.Id, licenseId: license.Id,
@@ -69,6 +73,8 @@ const OverageUsersBannerNotice = () => {
banner: 'invite modal', banner: 'invite modal',
}); });
const selfHostedExpansionModal = useControlSelfHostedExpansionModal({trackingLocation: 'overage_user_banner_notice'});
if (!hasPermission || adminHasDismissed({overagePreferences, preferenceName})) { if (!hasPermission || adminHasDismissed({overagePreferences, preferenceName})) {
return null; return null;
} }
@@ -96,12 +102,31 @@ const OverageUsersBannerNotice = () => {
const handleClick = () => { const handleClick = () => {
trackEventFn(isExpandable ? 'Self Serve' : 'Contact Sales'); trackEventFn(isExpandable ? 'Self Serve' : 'Contact Sales');
if (isExpandable) {
selfHostedExpansionModal.open()
}
}; };
if (isAirGapped) {
window.open(expandableLink(license.Id), '_blank');
}
if (isExpandable) {
return (
<NavLink
to={`${ConsolePages.LICENSE}?action=show_expansion_modal`}
target='_blank'
rel='noreferrer'
>
{cta}
</NavLink>
)
}
return ( return (
<ExternalLink <ExternalLink
className='overage_users_banner__button' className='overage_users_banner__button'
href={isExpandable ? expandableLink(license.Id) : LicenseLinks.CONTACT_SALES} href={LicenseLinks.CONTACT_SALES}
onClick={handleClick} onClick={handleClick}
> >
{cta} {cta}

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

@@ -11,7 +11,7 @@ import {GlobalState} from 'types/store';
import {TestHelper as TH} from 'utils/test_helper'; import {TestHelper as TH} from 'utils/test_helper';
import {Client4} from 'mattermost-redux/client'; import {Client4} from 'mattermost-redux/client';
import {STORAGE_KEY_PURCHASE_IN_PROGRESS} from 'components/self_hosted_purchase_modal/constants'; import {STORAGE_KEY_PURCHASE_IN_PROGRESS} from 'components/self_hosted_purchases/constants';
import PurchaseInProgressModal from './'; import PurchaseInProgressModal from './';

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

@@ -1,4 +0,0 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
export const STORAGE_KEY_EXPANSION_IN_PROGRESS = 'EXPANSION_IN_PROGRESS';

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

@@ -2,3 +2,4 @@
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
export const STORAGE_KEY_PURCHASE_IN_PROGRESS = 'PURCHASE_IN_PROGRESS'; export const STORAGE_KEY_PURCHASE_IN_PROGRESS = 'PURCHASE_IN_PROGRESS';
export const STORAGE_KEY_EXPANSION_IN_PROGRESS = 'EXPANSION_IN_PROGRESS';

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

@@ -13,6 +13,7 @@ import './error_page.scss';
interface Props { interface Props {
canRetry: boolean; canRetry: boolean;
tryAgain: () => void;
} }
export default function SelfHostedExpansionErrorPage(props: Props) { export default function SelfHostedExpansionErrorPage(props: Props) {
@@ -70,9 +71,7 @@ export default function SelfHostedExpansionErrorPage(props: Props) {
icon={icon} icon={icon}
error={true} error={true}
formattedButtonText={formattedButtonText} formattedButtonText={formattedButtonText}
buttonHandler={() => { buttonHandler={props.tryAgain}
//TODO: Open self hosted expansion modal
}}
formattedTertiaryButonText={tertiaryButtonText} formattedTertiaryButonText={tertiaryButtonText}
tertiaryButtonHandler={() => window.open(contactSupportLink, '_blank', 'noreferrer')} tertiaryButtonHandler={() => window.open(contactSupportLink, '_blank', 'noreferrer')}
/> />

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

@@ -11,10 +11,10 @@ import {StripeCardElementChangeEvent} from '@stripe/stripe-js';
import UpgradeSvg from 'components/common/svg_images_components/upgrade_svg'; import UpgradeSvg from 'components/common/svg_images_components/upgrade_svg';
import RootPortal from 'components/root_portal'; import RootPortal from 'components/root_portal';
import ContactSalesLink from 'components/self_hosted_purchase_modal/contact_sales_link'; import ContactSalesLink from 'components/self_hosted_purchases/contact_sales_link';
import ErrorPage from 'components/self_hosted_expansion_modal/error_page'; import ErrorPage from 'components/self_hosted_purchases/self_hosted_expansion_modal/error_page';
import SuccessPage from 'components/self_hosted_expansion_modal/success_page'; import SuccessPage from 'components/self_hosted_purchases/self_hosted_expansion_modal/success_page';
import Submitting from 'components/self_hosted_purchase_modal/submitting'; import Submitting from './submitting';
import useLoadStripe from 'components/common/hooks/useLoadStripe'; import useLoadStripe from 'components/common/hooks/useLoadStripe';
import CardInput, {CardInputType} from 'components/payment_form/card_input'; import CardInput, {CardInputType} from 'components/payment_form/card_input';
@@ -23,7 +23,7 @@ import Input from 'components/widgets/inputs/input/input';
import BackgroundSvg from 'components/common/svg_images_components/background_svg'; import BackgroundSvg from 'components/common/svg_images_components/background_svg';
import {getTheme} from 'mattermost-redux/selectors/entities/preferences'; import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
import StripeProvider from '../self_hosted_purchase_modal/stripe_provider'; import StripeProvider from '../stripe_provider';
import {closeModal} from 'actions/views/modals'; import {closeModal} from 'actions/views/modals';
import {ModalIdentifiers, TELEMETRY_CATEGORIES} from 'utils/constants'; import {ModalIdentifiers, TELEMETRY_CATEGORIES} from 'utils/constants';
@@ -46,11 +46,12 @@ import SelfHostedExpansionCard from './expansion_card';
import './self_hosted_expansion_modal.scss'; import './self_hosted_expansion_modal.scss';
import {STORAGE_KEY_EXPANSION_IN_PROGRESS} from './constants'; import {STORAGE_KEY_EXPANSION_IN_PROGRESS} from '../constants';
import Address from 'components/self_hosted_purchase_modal/address'; import Address from 'components/self_hosted_purchases/address';
import ChooseDifferentShipping from 'components/choose_different_shipping'; import ChooseDifferentShipping from 'components/choose_different_shipping';
import Terms from 'components/self_hosted_purchase_modal/terms'; import Terms from 'components/self_hosted_purchases/self_hosted_purchase_modal/terms';
import useControlSelfHostedExpansionModal from 'components/common/hooks/useControlSelfHostedExpansionModal'; import useControlSelfHostedExpansionModal from 'components/common/hooks/useControlSelfHostedExpansionModal';
import classNames from 'classnames';
export interface FormState { export interface FormState {
cardName: string; cardName: string;
@@ -189,6 +190,7 @@ export default function SelfHostedExpansionModal() {
const [formState, setFormState] = useState<FormState>(initialState); const [formState, setFormState] = useState<FormState>(initialState);
const [show] = useState(true); const [show] = useState(true);
const canRetry = formState.error !== '422'; const canRetry = formState.error !== '422';
const showForm = progress !== SelfHostedSignupProgress.PAID && progress !== SelfHostedSignupProgress.CREATED_LICENSE && !formState.submitting && !formState.error && !formState.succeeded;
const title = intl.formatMessage({ const title = intl.formatMessage({
id: 'self_hosted_expansion.expansion_modal.title', id: 'self_hosted_expansion.expansion_modal.title',
@@ -200,6 +202,7 @@ export default function SelfHostedExpansionModal() {
const submit = async () => { const submit = async () => {
let submitProgress = progress; let submitProgress = progress;
let signupCustomerResult: SelfHostedSignupCustomerResponse | null = null; let signupCustomerResult: SelfHostedSignupCustomerResponse | null = null;
setFormState({...formState, submitting: true});
try { try {
const [firstName, lastName] = inferNames(user, formState.cardName); const [firstName, lastName] = inferNames(user, formState.cardName);
@@ -344,8 +347,7 @@ export default function SelfHostedExpansionModal() {
}} }}
> >
<div className='SelfHostedExpansionModal'> <div className='SelfHostedExpansionModal'>
<div className='form-view'> {<div className={classNames('form-view', {'form-view--hide': !showForm})}> <div className='lhs'>
<div className='lhs'>
<h2 className='title'>{title}</h2> <h2 className='title'>{title}</h2>
<UpgradeSvg <UpgradeSvg
width={267} width={267}
@@ -503,21 +505,21 @@ export default function SelfHostedExpansionModal() {
initialSeats={additionalSeats} initialSeats={additionalSeats}
/> />
</div> </div>
</div> </div>}
{((formState.succeeded || progress === SelfHostedSignupProgress.CREATED_LICENSE)) && !formState.error && !formState.submitting && ( {((formState.succeeded || progress === SelfHostedSignupProgress.CREATED_LICENSE)) && !formState.error && !formState.submitting && (
<SuccessPage <SuccessPage
onClose={selfHostedExpansionModal.close} onClose={selfHostedExpansionModal.close}
/> />
)} )}
{formState.submitting && !formState.error && ( {formState.submitting && (
<Submitting <Submitting
desiredPlanName={currentPlan} currentPlan={currentPlan}
progressBar={formState.progressBar}
/> />
)} )}
{formState.error && ( {formState.error && (
<ErrorPage <ErrorPage
canRetry={canRetry} canRetry={canRetry}
tryAgain={() => {setFormState({...formState, submitting: false, error: ''})}}
/> />
)} )}
<div className='background-svg'> <div className='background-svg'>

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

@@ -83,6 +83,10 @@
} }
} }
&--hide {
display: none;
}
>.lhs { >.lhs {
width: 25%; width: 25%;
} }

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

@@ -0,0 +1,122 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React, {useEffect, useState} from 'react';
import {FormattedMessage, useIntl} from 'react-intl';
import {useSelector} from 'react-redux';
import {getSelfHostedSignupProgress} from 'mattermost-redux/selectors/entities/hosted_customer';
import {SelfHostedSignupProgress} from '@mattermost/types/hosted_customer';
import {ValueOf} from '@mattermost/types/utilities';
import CreditCardSvg from 'components/common/svg_images_components/credit_card_svg';
import IconMessage from 'components/purchase_modal/icon_message';
function useConvertProgressToWaitingExplanation(progress: ValueOf<typeof SelfHostedSignupProgress>, planName: string): React.ReactNode {
const intl = useIntl();
switch (progress) {
case SelfHostedSignupProgress.START:
case SelfHostedSignupProgress.CREATED_CUSTOMER:
case SelfHostedSignupProgress.CREATED_INTENT:
return intl.formatMessage({
id: 'self_hosted_signup.progress_step.submitting_payment',
defaultMessage: 'Submitting payment information',
});
case SelfHostedSignupProgress.CONFIRMED_INTENT:
case SelfHostedSignupProgress.CREATED_SUBSCRIPTION:
return intl.formatMessage({
id: 'self_hosted_signup.progress_step.verifying_payment',
defaultMessage: 'Verifying payment details',
});
case SelfHostedSignupProgress.PAID:
case SelfHostedSignupProgress.CREATED_LICENSE:
return intl.formatMessage({
id: 'self_hosted_signup.progress_step.applying_license',
defaultMessage: 'Applying your {planName} license to your Mattermost instance',
}, {planName});
default:
return intl.formatMessage({
id: 'self_hosted_signup.progress_step.submitting_payment',
defaultMessage: 'Submitting payment information',
});
}
}
export function convertProgressToBar(progress: ValueOf<typeof SelfHostedSignupProgress>): number {
switch (progress) {
case SelfHostedSignupProgress.START:
return 15;
case SelfHostedSignupProgress.CREATED_CUSTOMER:
return 30;
case SelfHostedSignupProgress.CREATED_INTENT:
return 45;
case SelfHostedSignupProgress.CONFIRMED_INTENT:
return 60;
case SelfHostedSignupProgress.CREATED_SUBSCRIPTION:
return 75;
case SelfHostedSignupProgress.PAID:
return 85;
case SelfHostedSignupProgress.CREATED_LICENSE:
return 100;
default:
return 0;
}
}
interface Props {
currentPlan: string;
}
const maxProgressBar = 100;
const maxFakeProgressIncrement = 5;
const fakeProgressInterval = 600;
export default function Submitting(props: Props) {
const [barProgress, setBarProgress] = useState(0);
const signupProgress = useSelector(getSelfHostedSignupProgress);
const waitingExplanation = useConvertProgressToWaitingExplanation(signupProgress, props.currentPlan);
const footer = (
<div className='ProcessPayment-progress'>
<div
className='ProcessPayment-progress-fill'
style={{width: `${barProgress}%`}}
/>
</div>
);
useEffect(() => {
const maxProgressForCurrentSignupProgress = convertProgressToBar(signupProgress);
const interval = setInterval(() => {
if (barProgress < maxProgressBar) {
setBarProgress(Math.min(maxProgressForCurrentSignupProgress, barProgress + maxFakeProgressIncrement));
}
}, fakeProgressInterval);
return () => clearInterval(interval);
}, [barProgress]);
return (
<div className='submitting'>
<IconMessage
formattedTitle={(
<FormattedMessage
id='admin.billing.subscription.verifyPaymentInformation'
defaultMessage='Verifying your payment information'
/>
)}
formattedSubtitle={waitingExplanation}
icon={
<CreditCardSvg
width={444}
height={313}
/>
}
footer={footer}
className={'processing'}
/>
</div>
);
}

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

@@ -15,7 +15,7 @@ import {SelfHostedProducts, ModalIdentifiers} from 'utils/constants';
import {DeepPartial} from '@mattermost/types/utilities'; import {DeepPartial} from '@mattermost/types/utilities';
import SelfHostedPurchaseModal, {makeInitialState, canSubmit, State} from './'; import SelfHostedPurchaseModal, {makeInitialState, canSubmit, State} from '.';
interface MockCardInputProps { interface MockCardInputProps {
onCardInputChange: (event: {complete: boolean}) => void; onCardInputChange: (event: {complete: boolean}) => void;

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

@@ -53,23 +53,23 @@ import {
SelfHostedSignupCustomerResponse, SelfHostedSignupCustomerResponse,
} from '@mattermost/types/hosted_customer'; } from '@mattermost/types/hosted_customer';
import {Seats, errorInvalidNumber} from '../seats_calculator'; import {Seats, errorInvalidNumber} from '../../seats_calculator';
import ContactSalesLink from './contact_sales_link'; import ContactSalesLink from '../contact_sales_link';
import Submitting, {convertProgressToBar} from './submitting'; import Submitting, {convertProgressToBar} from './submitting';
import ErrorPage from './error'; import ErrorPage from './error';
import SuccessPage from './success_page'; import SuccessPage from './success_page';
import SelfHostedCard from './self_hosted_card'; import SelfHostedCard from './self_hosted_card';
import StripeProvider from './stripe_provider'; import StripeProvider from '../stripe_provider';
import Terms from './terms'; import Terms from './terms';
import Address from './address'; import Address from '../address';
import useNoEscape from './useNoEscape'; import useNoEscape from './useNoEscape';
import {SetPrefix, UnionSetActions} from './types'; import {SetPrefix, UnionSetActions} from './types';
import './self_hosted_purchase_modal.scss'; import './self_hosted_purchase_modal.scss';
import {STORAGE_KEY_PURCHASE_IN_PROGRESS} from './constants'; import {STORAGE_KEY_PURCHASE_IN_PROGRESS} from '../constants';
export interface State { export interface State {

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

@@ -17,8 +17,8 @@ import {
SelfHostedProducts, SelfHostedProducts,
} from 'utils/constants'; } from 'utils/constants';
import Consequences from '../seats_calculator/consequences'; import Consequences from '../../seats_calculator/consequences';
import SeatsCalculator, {Seats} from '../seats_calculator'; import SeatsCalculator, {Seats} from '../../seats_calculator';
// Card has a bunch of props needed for monthly/yearly payments that // Card has a bunch of props needed for monthly/yearly payments that
// do not apply to self-hosted. // do not apply to self-hosted.