[MM-51922] Enforce minimum 10 seats in seats selector (#22916)
* Enforce minimum 10 seats in seats selector * Ensure initial value is validated as well * Fix linter * fix tests * Add a test for 10 seat minimum * Fix linter, merge master
Этот коммит содержится в:
@@ -1,7 +1,7 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import React from 'react';
|
import React, {useEffect} from 'react';
|
||||||
import {useIntl, FormattedMessage, FormattedNumber} from 'react-intl';
|
import {useIntl, FormattedMessage, FormattedNumber} from 'react-intl';
|
||||||
|
|
||||||
import {InformationOutlineIcon} from '@mattermost/compass-icons/components';
|
import {InformationOutlineIcon} from '@mattermost/compass-icons/components';
|
||||||
@@ -41,6 +41,12 @@ export const errorInvalidNumber = (
|
|||||||
defaultMessage='Enter a valid number of seats'
|
defaultMessage='Enter a valid number of seats'
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
export const errorMinSeats = (
|
||||||
|
<FormattedMessage
|
||||||
|
id='cloud_upgrade.error_min_seats'
|
||||||
|
defaultMessage='Minimum of 10 seats required'
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
function validateSeats(seats: string, annualPricePerSeat: number, minSeats: number, cloud: boolean): Seats {
|
function validateSeats(seats: string, annualPricePerSeat: number, minSeats: number, cloud: boolean): Seats {
|
||||||
if (seats === '') {
|
if (seats === '') {
|
||||||
@@ -58,6 +64,13 @@ function validateSeats(seats: string, annualPricePerSeat: number, minSeats: numb
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (seatsNumber < 10) {
|
||||||
|
return {
|
||||||
|
quantity: seats,
|
||||||
|
error: errorMinSeats,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const maxSeats = calculateMaxUsers(annualPricePerSeat);
|
const maxSeats = calculateMaxUsers(annualPricePerSeat);
|
||||||
const tooFewUsersErrorMessage = (
|
const tooFewUsersErrorMessage = (
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
@@ -134,6 +147,10 @@ export default function SeatsCalculator(props: Props) {
|
|||||||
props.onChange(validateSeats(value, annualPricePerSeat, props.existingUsers, props.isCloud));
|
props.onChange(validateSeats(value, annualPricePerSeat, props.existingUsers, props.isCloud));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
props.onChange(validateSeats(props.seats.quantity, annualPricePerSeat, props.existingUsers, props.isCloud));
|
||||||
|
}, []);
|
||||||
|
|
||||||
const maxSeats = calculateMaxUsers(annualPricePerSeat);
|
const maxSeats = calculateMaxUsers(annualPricePerSeat);
|
||||||
const total = '$' + intl.formatNumber((parseFloat(props.seats.quantity) || 0) * annualPricePerSeat, {maximumFractionDigits: 2});
|
const total = '$' + intl.formatNumber((parseFloat(props.seats.quantity) || 0) * annualPricePerSeat, {maximumFractionDigits: 2});
|
||||||
const userCountTooltip = (
|
const userCountTooltip = (
|
||||||
|
|||||||
@@ -2,6 +2,10 @@
|
|||||||
padding: 14px 0;
|
padding: 14px 0;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
|
||||||
|
.Input___customMessage.Input___error {
|
||||||
|
line-height: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
&__seats-item {
|
&__seats-item {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ const mockCreatedIntent = SelfHostedSignupProgress.CREATED_INTENT;
|
|||||||
const mockCreatedLicense = SelfHostedSignupProgress.CREATED_LICENSE;
|
const mockCreatedLicense = SelfHostedSignupProgress.CREATED_LICENSE;
|
||||||
const failOrg = 'failorg';
|
const failOrg = 'failorg';
|
||||||
|
|
||||||
const existingUsers = 10;
|
const existingUsers = 11;
|
||||||
|
|
||||||
jest.mock('mattermost-redux/client', () => {
|
jest.mock('mattermost-redux/client', () => {
|
||||||
const original = jest.requireActual('mattermost-redux/client');
|
const original = jest.requireActual('mattermost-redux/client');
|
||||||
@@ -275,7 +275,17 @@ describe('SelfHostedPurchaseModal', () => {
|
|||||||
const tooFewSeats = existingUsers - 1;
|
const tooFewSeats = existingUsers - 1;
|
||||||
fireEvent.change(screen.getByTestId('selfHostedPurchaseSeatsInput'), valueEvent(tooFewSeats.toString()));
|
fireEvent.change(screen.getByTestId('selfHostedPurchaseSeatsInput'), valueEvent(tooFewSeats.toString()));
|
||||||
expect(screen.getByText('Upgrade')).toBeDisabled();
|
expect(screen.getByText('Upgrade')).toBeDisabled();
|
||||||
screen.getByText('Your workspace currently has 10 users', {exact: false});
|
screen.getByText('Your workspace currently has 11 users', {exact: false});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Minimum of 10 seats is required for sign up', () => {
|
||||||
|
renderWithIntlAndStore(<div id='root-portal'><SelfHostedPurchaseModal productId={'prod_professional'}/></div>, initialState);
|
||||||
|
fillForm(defaultSuccessForm);
|
||||||
|
|
||||||
|
const tooFewSeats = 9;
|
||||||
|
fireEvent.change(screen.getByTestId('selfHostedPurchaseSeatsInput'), valueEvent(tooFewSeats.toString()));
|
||||||
|
expect(screen.getByText('Upgrade')).toBeDisabled();
|
||||||
|
screen.getByText('Minimum of 10 seats required', {exact: false});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('happy path submit shows success screen', async () => {
|
it('happy path submit shows success screen', async () => {
|
||||||
|
|||||||
@@ -3068,6 +3068,7 @@
|
|||||||
"cloud_delinquency.post_downgrade_banner.title": "Update your billing information now to re-activate paid features.",
|
"cloud_delinquency.post_downgrade_banner.title": "Update your billing information now to re-activate paid features.",
|
||||||
"cloud_signup.signup_consequences": "Your credit card will be charged today. <a>See how billing works.</a>",
|
"cloud_signup.signup_consequences": "Your credit card will be charged today. <a>See how billing works.</a>",
|
||||||
"cloud_subscribe.contact_support": "Compare plans",
|
"cloud_subscribe.contact_support": "Compare plans",
|
||||||
|
"cloud_upgrade.error_min_seats": "Minimum of 10 seats required",
|
||||||
"cloud.fetch_error": "Error fetching billing data. Please try again later.",
|
"cloud.fetch_error": "Error fetching billing data. Please try again later.",
|
||||||
"cloud.fetch_error.retry": "Retry",
|
"cloud.fetch_error.retry": "Retry",
|
||||||
"cloud.invoice_pdf_preview.download": "<downloadLink>Download</downloadLink> this page for your records",
|
"cloud.invoice_pdf_preview.download": "<downloadLink>Download</downloadLink> this page for your records",
|
||||||
|
|||||||
@@ -1983,6 +1983,7 @@ export const Constants = {
|
|||||||
CHANNEL_HEADER_BUTTON_DISABLE_TIMEOUT: 1000,
|
CHANNEL_HEADER_BUTTON_DISABLE_TIMEOUT: 1000,
|
||||||
FIRST_ADMIN_ROLE: 'first_admin',
|
FIRST_ADMIN_ROLE: 'first_admin',
|
||||||
MAX_PURCHASE_SEATS: 1000000,
|
MAX_PURCHASE_SEATS: 1000000,
|
||||||
|
MIN_PURCHASE_SEATS: 10,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ValidationErrors = {
|
export const ValidationErrors = {
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user