User usage alert in License and System Statistics page (#31063)
* Added user over usager alert in License and System Statistics page * Fixed case below 90 * Fixed constant * Linting * Change over user notification threshold to 0 * Fix tests * Saved dismissed preference * Text tweek * Fix tests * More tests fixed * Snapshot updated
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
2116a6d94a
Коммит
2213cdbaf7
@@ -19,6 +19,22 @@ exports[`components/admin_console/license_settings/LicenseSettings load screen a
|
|||||||
<div
|
<div
|
||||||
className="admin-console__banner_section"
|
className="admin-console__banner_section"
|
||||||
>
|
>
|
||||||
|
<UserSeatAlertBanner
|
||||||
|
license={
|
||||||
|
Object {
|
||||||
|
"Company": "Mattermost Inc.",
|
||||||
|
"ExpiresAt": "1620335443650",
|
||||||
|
"IsLicensed": "true",
|
||||||
|
"IssuedAt": "1517714643650",
|
||||||
|
"Name": "LicenseName",
|
||||||
|
"SkuShortName": "E20",
|
||||||
|
"StartsAt": "1517714643650",
|
||||||
|
"Users": "100",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
location="license_settings"
|
||||||
|
totalUsers={10}
|
||||||
|
/>
|
||||||
<RenewLicenseCard
|
<RenewLicenseCard
|
||||||
isLicenseExpired={false}
|
isLicenseExpired={false}
|
||||||
license={
|
license={
|
||||||
@@ -123,6 +139,22 @@ exports[`components/admin_console/license_settings/LicenseSettings load screen w
|
|||||||
<div
|
<div
|
||||||
className="admin-console__banner_section"
|
className="admin-console__banner_section"
|
||||||
>
|
>
|
||||||
|
<UserSeatAlertBanner
|
||||||
|
license={
|
||||||
|
Object {
|
||||||
|
"Company": "Mattermost Inc.",
|
||||||
|
"ExpiresAt": "1620335443650",
|
||||||
|
"IsLicensed": "true",
|
||||||
|
"IssuedAt": "1517714643650",
|
||||||
|
"Name": "LicenseName",
|
||||||
|
"SkuShortName": "E20",
|
||||||
|
"StartsAt": "1517714643650",
|
||||||
|
"Users": "100",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
location="license_settings"
|
||||||
|
totalUsers={10}
|
||||||
|
/>
|
||||||
<RenewLicenseCard
|
<RenewLicenseCard
|
||||||
isLicenseExpired={false}
|
isLicenseExpired={false}
|
||||||
license={
|
license={
|
||||||
@@ -227,6 +259,22 @@ exports[`components/admin_console/license_settings/LicenseSettings should match
|
|||||||
<div
|
<div
|
||||||
className="admin-console__banner_section"
|
className="admin-console__banner_section"
|
||||||
>
|
>
|
||||||
|
<UserSeatAlertBanner
|
||||||
|
license={
|
||||||
|
Object {
|
||||||
|
"Company": "Mattermost Inc.",
|
||||||
|
"ExpiresAt": "1620335443650",
|
||||||
|
"IsLicensed": "true",
|
||||||
|
"IssuedAt": "1517714643650",
|
||||||
|
"Name": "LicenseName",
|
||||||
|
"SkuShortName": "E10",
|
||||||
|
"StartsAt": "1517714643650",
|
||||||
|
"Users": "100",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
location="license_settings"
|
||||||
|
totalUsers={10}
|
||||||
|
/>
|
||||||
<RenewLicenseCard
|
<RenewLicenseCard
|
||||||
isLicenseExpired={false}
|
isLicenseExpired={false}
|
||||||
license={
|
license={
|
||||||
@@ -362,6 +410,22 @@ exports[`components/admin_console/license_settings/LicenseSettings should match
|
|||||||
<div
|
<div
|
||||||
className="admin-console__banner_section"
|
className="admin-console__banner_section"
|
||||||
>
|
>
|
||||||
|
<UserSeatAlertBanner
|
||||||
|
license={
|
||||||
|
Object {
|
||||||
|
"Company": "Mattermost Inc.",
|
||||||
|
"ExpiresAt": "1620335443650",
|
||||||
|
"IsLicensed": "true",
|
||||||
|
"IssuedAt": "1517714643650",
|
||||||
|
"Name": "LicenseName",
|
||||||
|
"SkuShortName": "E20",
|
||||||
|
"StartsAt": "1517714643650",
|
||||||
|
"Users": "100",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
location="license_settings"
|
||||||
|
totalUsers={10}
|
||||||
|
/>
|
||||||
<RenewLicenseCard
|
<RenewLicenseCard
|
||||||
isLicenseExpired={false}
|
isLicenseExpired={false}
|
||||||
license={
|
license={
|
||||||
@@ -485,6 +549,22 @@ exports[`components/admin_console/license_settings/LicenseSettings should match
|
|||||||
<div
|
<div
|
||||||
className="admin-console__banner_section"
|
className="admin-console__banner_section"
|
||||||
>
|
>
|
||||||
|
<UserSeatAlertBanner
|
||||||
|
license={
|
||||||
|
Object {
|
||||||
|
"Company": "Mattermost Inc.",
|
||||||
|
"ExpiresAt": "1620335443650",
|
||||||
|
"IsLicensed": "true",
|
||||||
|
"IssuedAt": "1517714643650",
|
||||||
|
"Name": "LicenseName",
|
||||||
|
"SkuShortName": "enterprise",
|
||||||
|
"StartsAt": "1517714643650",
|
||||||
|
"Users": "100",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
location="license_settings"
|
||||||
|
totalUsers={10}
|
||||||
|
/>
|
||||||
<RenewLicenseCard
|
<RenewLicenseCard
|
||||||
isLicenseExpired={false}
|
isLicenseExpired={false}
|
||||||
license={
|
license={
|
||||||
@@ -608,6 +688,22 @@ exports[`components/admin_console/license_settings/LicenseSettings should match
|
|||||||
<div
|
<div
|
||||||
className="admin-console__banner_section"
|
className="admin-console__banner_section"
|
||||||
>
|
>
|
||||||
|
<UserSeatAlertBanner
|
||||||
|
license={
|
||||||
|
Object {
|
||||||
|
"Company": "Mattermost Inc.",
|
||||||
|
"ExpiresAt": "1620335443650",
|
||||||
|
"IsLicensed": "true",
|
||||||
|
"IssuedAt": "1517714643650",
|
||||||
|
"Name": "LicenseName",
|
||||||
|
"SkuShortName": "E20",
|
||||||
|
"StartsAt": "1517714643650",
|
||||||
|
"Users": "100",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
location="license_settings"
|
||||||
|
totalUsers={10}
|
||||||
|
/>
|
||||||
<RenewLicenseCard
|
<RenewLicenseCard
|
||||||
isLicenseExpired={false}
|
isLicenseExpired={false}
|
||||||
license={
|
license={
|
||||||
@@ -731,6 +827,22 @@ exports[`components/admin_console/license_settings/LicenseSettings should match
|
|||||||
<div
|
<div
|
||||||
className="admin-console__banner_section"
|
className="admin-console__banner_section"
|
||||||
>
|
>
|
||||||
|
<UserSeatAlertBanner
|
||||||
|
license={
|
||||||
|
Object {
|
||||||
|
"Company": "Mattermost Inc.",
|
||||||
|
"ExpiresAt": "1620335443650",
|
||||||
|
"IsLicensed": "true",
|
||||||
|
"IssuedAt": "1517714643650",
|
||||||
|
"Name": "LicenseName",
|
||||||
|
"SkuShortName": "E20",
|
||||||
|
"StartsAt": "1517714643650",
|
||||||
|
"Users": "100",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
location="license_settings"
|
||||||
|
totalUsers={10}
|
||||||
|
/>
|
||||||
<RenewLicenseCard
|
<RenewLicenseCard
|
||||||
isLicenseExpired={false}
|
isLicenseExpired={false}
|
||||||
license={
|
license={
|
||||||
@@ -854,6 +966,22 @@ exports[`components/admin_console/license_settings/LicenseSettings should match
|
|||||||
<div
|
<div
|
||||||
className="admin-console__banner_section"
|
className="admin-console__banner_section"
|
||||||
>
|
>
|
||||||
|
<UserSeatAlertBanner
|
||||||
|
license={
|
||||||
|
Object {
|
||||||
|
"Company": "Mattermost Inc.",
|
||||||
|
"ExpiresAt": "1620335443650",
|
||||||
|
"IsLicensed": "true",
|
||||||
|
"IssuedAt": "1517714643650",
|
||||||
|
"Name": "LicenseName",
|
||||||
|
"SkuShortName": "E20",
|
||||||
|
"StartsAt": "1517714643650",
|
||||||
|
"Users": "100",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
location="license_settings"
|
||||||
|
totalUsers={10}
|
||||||
|
/>
|
||||||
<RenewLicenseCard
|
<RenewLicenseCard
|
||||||
isLicenseExpired={false}
|
isLicenseExpired={false}
|
||||||
license={
|
license={
|
||||||
@@ -977,6 +1105,18 @@ exports[`components/admin_console/license_settings/LicenseSettings should match
|
|||||||
<div
|
<div
|
||||||
className="admin-console__banner_section"
|
className="admin-console__banner_section"
|
||||||
>
|
>
|
||||||
|
<UserSeatAlertBanner
|
||||||
|
license={
|
||||||
|
Object {
|
||||||
|
"ExpiresAt": "1620335443650",
|
||||||
|
"IsLicensed": "true",
|
||||||
|
"IssuedAt": "1617714643650",
|
||||||
|
"StartsAt": "1617714643650",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
location="license_settings"
|
||||||
|
totalUsers={10}
|
||||||
|
/>
|
||||||
<TrialLicenseCard
|
<TrialLicenseCard
|
||||||
license={
|
license={
|
||||||
Object {
|
Object {
|
||||||
@@ -1078,6 +1218,15 @@ exports[`components/admin_console/license_settings/LicenseSettings should match
|
|||||||
<div
|
<div
|
||||||
className="admin-console__banner_section"
|
className="admin-console__banner_section"
|
||||||
>
|
>
|
||||||
|
<UserSeatAlertBanner
|
||||||
|
license={
|
||||||
|
Object {
|
||||||
|
"IsLicensed": "false",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
location="license_settings"
|
||||||
|
totalUsers={10}
|
||||||
|
/>
|
||||||
<Component
|
<Component
|
||||||
enterpriseReady={true}
|
enterpriseReady={true}
|
||||||
gettingTrial={false}
|
gettingTrial={false}
|
||||||
@@ -1190,6 +1339,15 @@ exports[`components/admin_console/license_settings/LicenseSettings should match
|
|||||||
<div
|
<div
|
||||||
className="admin-console__banner_section"
|
className="admin-console__banner_section"
|
||||||
>
|
>
|
||||||
|
<UserSeatAlertBanner
|
||||||
|
license={
|
||||||
|
Object {
|
||||||
|
"IsLicensed": "false",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
location="license_settings"
|
||||||
|
totalUsers={10}
|
||||||
|
/>
|
||||||
<Component
|
<Component
|
||||||
enterpriseReady={true}
|
enterpriseReady={true}
|
||||||
gettingTrial={false}
|
gettingTrial={false}
|
||||||
@@ -1302,6 +1460,22 @@ exports[`components/admin_console/license_settings/LicenseSettings should match
|
|||||||
<div
|
<div
|
||||||
className="admin-console__banner_section"
|
className="admin-console__banner_section"
|
||||||
>
|
>
|
||||||
|
<UserSeatAlertBanner
|
||||||
|
license={
|
||||||
|
Object {
|
||||||
|
"Company": "Mattermost Inc.",
|
||||||
|
"ExpiresAt": "1620335443650",
|
||||||
|
"IsLicensed": "true",
|
||||||
|
"IssuedAt": "1517714643650",
|
||||||
|
"Name": "LicenseName",
|
||||||
|
"SkuShortName": "E20",
|
||||||
|
"StartsAt": "1517714643650",
|
||||||
|
"Users": "100",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
location="license_settings"
|
||||||
|
totalUsers={10}
|
||||||
|
/>
|
||||||
<RenewLicenseCard
|
<RenewLicenseCard
|
||||||
isLicenseExpired={false}
|
isLicenseExpired={false}
|
||||||
license={
|
license={
|
||||||
@@ -1406,6 +1580,15 @@ exports[`components/admin_console/license_settings/LicenseSettings should match
|
|||||||
<div
|
<div
|
||||||
className="admin-console__banner_section"
|
className="admin-console__banner_section"
|
||||||
>
|
>
|
||||||
|
<UserSeatAlertBanner
|
||||||
|
license={
|
||||||
|
Object {
|
||||||
|
"IsLicensed": "false",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
location="license_settings"
|
||||||
|
totalUsers={10}
|
||||||
|
/>
|
||||||
<Component
|
<Component
|
||||||
enterpriseReady={false}
|
enterpriseReady={false}
|
||||||
gettingTrial={false}
|
gettingTrial={false}
|
||||||
@@ -1519,7 +1702,17 @@ exports[`components/admin_console/license_settings/LicenseSettings should match
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="admin-console__banner_section"
|
className="admin-console__banner_section"
|
||||||
/>
|
>
|
||||||
|
<UserSeatAlertBanner
|
||||||
|
license={
|
||||||
|
Object {
|
||||||
|
"IsLicensed": "false",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
location="license_settings"
|
||||||
|
totalUsers={10}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
className="top-wrapper"
|
className="top-wrapper"
|
||||||
>
|
>
|
||||||
@@ -1616,7 +1809,25 @@ exports[`components/admin_console/license_settings/LicenseSettings should match
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="admin-console__banner_section"
|
className="admin-console__banner_section"
|
||||||
/>
|
>
|
||||||
|
<UserSeatAlertBanner
|
||||||
|
license={
|
||||||
|
Object {
|
||||||
|
"Cloud": "true",
|
||||||
|
"Company": "Mattermost Inc.",
|
||||||
|
"ExpiresAt": "1620993600000",
|
||||||
|
"IsLicensed": "true",
|
||||||
|
"IssuedAt": "1517714643650",
|
||||||
|
"Name": "LicenseName",
|
||||||
|
"SkuShortName": "E20",
|
||||||
|
"StartsAt": "1517714643650",
|
||||||
|
"Users": "100",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
location="license_settings"
|
||||||
|
totalUsers={10}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
className="top-wrapper"
|
className="top-wrapper"
|
||||||
>
|
>
|
||||||
@@ -1725,6 +1936,22 @@ exports[`components/admin_console/license_settings/LicenseSettings should match
|
|||||||
<div
|
<div
|
||||||
className="admin-console__banner_section"
|
className="admin-console__banner_section"
|
||||||
>
|
>
|
||||||
|
<UserSeatAlertBanner
|
||||||
|
license={
|
||||||
|
Object {
|
||||||
|
"Company": "Mattermost Inc.",
|
||||||
|
"ExpiresAt": "1620993600000",
|
||||||
|
"IsLicensed": "true",
|
||||||
|
"IssuedAt": "1517714643650",
|
||||||
|
"Name": "LicenseName",
|
||||||
|
"SkuShortName": "E20",
|
||||||
|
"StartsAt": "1517714643650",
|
||||||
|
"Users": "100",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
location="license_settings"
|
||||||
|
totalUsers={10}
|
||||||
|
/>
|
||||||
<RenewLicenseCard
|
<RenewLicenseCard
|
||||||
isLicenseExpired={false}
|
isLicenseExpired={false}
|
||||||
license={
|
license={
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import mergeObjects from 'packages/mattermost-redux/test/merge_objects';
|
|||||||
import {mountWithIntl} from 'tests/helpers/intl-test-helper';
|
import {mountWithIntl} from 'tests/helpers/intl-test-helper';
|
||||||
import {renderWithContext, screen} from 'tests/react_testing_utils';
|
import {renderWithContext, screen} from 'tests/react_testing_utils';
|
||||||
import mockStore from 'tests/test_store';
|
import mockStore from 'tests/test_store';
|
||||||
import {OverActiveUserLimits, SelfHostedProducts} from 'utils/constants';
|
import {SelfHostedProducts} from 'utils/constants';
|
||||||
import {TestHelper} from 'utils/test_helper';
|
import {TestHelper} from 'utils/test_helper';
|
||||||
|
|
||||||
import EnterpriseEditionLeftPanel from './enterprise_edition_left_panel';
|
import EnterpriseEditionLeftPanel from './enterprise_edition_left_panel';
|
||||||
@@ -132,30 +132,9 @@ describe('components/admin_console/license_settings/enterprise_edition/enterpris
|
|||||||
expect(screen.getByText('ACTIVE USERS:')).not.toHaveClass('legend--over-seats-purchased');
|
expect(screen.getByText('ACTIVE USERS:')).not.toHaveClass('legend--over-seats-purchased');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should add warning class to active users', () => {
|
|
||||||
const minWarning = Math.ceil(parseInt(license.Users, 10) * OverActiveUserLimits.MIN) + parseInt(license.Users, 10);
|
|
||||||
const props = {
|
|
||||||
...baseProps,
|
|
||||||
statsActiveUsers: minWarning,
|
|
||||||
};
|
|
||||||
|
|
||||||
renderWithContext(
|
|
||||||
<EnterpriseEditionLeftPanel
|
|
||||||
{...props}
|
|
||||||
/>,
|
|
||||||
initialState,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(screen.getByText(Intl.NumberFormat('en').format(minWarning))).toHaveClass('value');
|
|
||||||
expect(screen.getByText(Intl.NumberFormat('en').format(minWarning))).toHaveClass('value--warning-over-seats-purchased');
|
|
||||||
expect(screen.getByText(Intl.NumberFormat('en').format(minWarning))).not.toHaveClass('value--over-seats-purchased');
|
|
||||||
expect(screen.getByText('ACTIVE USERS:')).toHaveClass('legend');
|
|
||||||
expect(screen.getByText('ACTIVE USERS:')).toHaveClass('legend--warning-over-seats-purchased');
|
|
||||||
expect(screen.getByText('ACTIVE USERS:')).not.toHaveClass('legend--over-seats-purchased');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should add over-seats-purchased class to active users', () => {
|
test('should add over-seats-purchased class to active users', () => {
|
||||||
const exceedHighLimitExtraUsersError = Math.ceil(parseInt(license.Users, 10) * OverActiveUserLimits.MAX) + parseInt(license.Users, 10);
|
// Changed to not use the constant OverActiveUserLimits.MAX given that we are currently set to 0. So the active users will be 0
|
||||||
|
const exceedHighLimitExtraUsersError = Math.ceil(parseInt(license.Users, 10) * 0.2) + parseInt(license.Users, 10);
|
||||||
const props = {
|
const props = {
|
||||||
...baseProps,
|
...baseProps,
|
||||||
statsActiveUsers: exceedHighLimitExtraUsersError,
|
statsActiveUsers: exceedHighLimitExtraUsersError,
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import TeamEditionLeftPanel from './team_edition/team_edition_left_panel';
|
|||||||
import TeamEditionRightPanel from './team_edition/team_edition_right_panel';
|
import TeamEditionRightPanel from './team_edition/team_edition_right_panel';
|
||||||
import TrialBanner from './trial_banner/trial_banner';
|
import TrialBanner from './trial_banner/trial_banner';
|
||||||
import TrialLicenseCard from './trial_license_card/trial_license_card';
|
import TrialLicenseCard from './trial_license_card/trial_license_card';
|
||||||
|
import UserSeatAlertBanner from './user_seat_alert_banner';
|
||||||
|
|
||||||
import './license_settings.scss';
|
import './license_settings.scss';
|
||||||
|
|
||||||
@@ -354,6 +355,11 @@ export default class LicenseSettings extends React.PureComponent<Props, State> {
|
|||||||
<div className='admin-console__wrapper'>
|
<div className='admin-console__wrapper'>
|
||||||
<div className='admin-console__content'>
|
<div className='admin-console__content'>
|
||||||
<div className='admin-console__banner_section'>
|
<div className='admin-console__banner_section'>
|
||||||
|
<UserSeatAlertBanner
|
||||||
|
license={license}
|
||||||
|
totalUsers={this.props.totalUsers}
|
||||||
|
location='license_settings'
|
||||||
|
/>
|
||||||
{!this.state.clickNormalUpgradeBtn && license.IsLicensed !== 'true' &&
|
{!this.state.clickNormalUpgradeBtn && license.IsLicensed !== 'true' &&
|
||||||
this.props.prevTrialLicense?.IsLicensed !== 'true' &&
|
this.props.prevTrialLicense?.IsLicensed !== 'true' &&
|
||||||
<TrialBanner
|
<TrialBanner
|
||||||
|
|||||||
@@ -0,0 +1,156 @@
|
|||||||
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
|
import React, {useState, useEffect} from 'react';
|
||||||
|
import {FormattedMessage, useIntl} from 'react-intl';
|
||||||
|
import {useDispatch, useSelector} from 'react-redux';
|
||||||
|
|
||||||
|
import type {ClientLicense} from '@mattermost/types/config';
|
||||||
|
|
||||||
|
import {savePreferences} from 'mattermost-redux/actions/preferences';
|
||||||
|
import {get as selectPreference} from 'mattermost-redux/selectors/entities/preferences';
|
||||||
|
import {getCurrentUser} from 'mattermost-redux/selectors/entities/users';
|
||||||
|
|
||||||
|
import ExternalLink from 'components/external_link';
|
||||||
|
|
||||||
|
import {Preferences, LicenseLinks} from 'utils/constants';
|
||||||
|
|
||||||
|
import type {GlobalState} from 'types/store';
|
||||||
|
|
||||||
|
import AlertBanner from '../../alert_banner';
|
||||||
|
|
||||||
|
export interface UserSeatAlertBannerProps {
|
||||||
|
license: ClientLicense;
|
||||||
|
totalUsers: number;
|
||||||
|
location: 'license_settings' | 'system_statistics';
|
||||||
|
}
|
||||||
|
|
||||||
|
const UserSeatAlertBanner: React.FC<UserSeatAlertBannerProps> = ({license, totalUsers, location}) => {
|
||||||
|
const {formatMessage} = useIntl();
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const currentUser = useSelector(getCurrentUser);
|
||||||
|
|
||||||
|
const getPreferenceName = (percentUsed: number) => {
|
||||||
|
if (percentUsed < 90) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
const locationSuffix = location === 'license_settings' ? 'license_settings' : 'system_statistics';
|
||||||
|
if (percentUsed >= 100) {
|
||||||
|
return `100_seat_${locationSuffix}`;
|
||||||
|
} else if (percentUsed >= 95) {
|
||||||
|
return `95_seat_${locationSuffix}`;
|
||||||
|
}
|
||||||
|
return `90_seat_${locationSuffix}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const calculatePercentUsed = () => {
|
||||||
|
if (!license || !license.Users) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
const licensedUsers = parseInt(license.Users, 10);
|
||||||
|
|
||||||
|
if (!licensedUsers || licensedUsers === 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return (totalUsers / licensedUsers) * 100;
|
||||||
|
};
|
||||||
|
|
||||||
|
const percentUsed = calculatePercentUsed();
|
||||||
|
const preferenceName = getPreferenceName(percentUsed);
|
||||||
|
const dismissed = useSelector((state: GlobalState) => selectPreference(state, Preferences.CATEGORY_SYSTEM_NOTICE, preferenceName, 'false'));
|
||||||
|
const [visible, setVisible] = useState(dismissed === 'false');
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setVisible(dismissed === 'false');
|
||||||
|
}, [dismissed]);
|
||||||
|
|
||||||
|
const handleDismiss = () => {
|
||||||
|
setVisible(false);
|
||||||
|
|
||||||
|
dispatch(savePreferences(currentUser.id, [{
|
||||||
|
user_id: currentUser.id,
|
||||||
|
category: Preferences.CATEGORY_SYSTEM_NOTICE,
|
||||||
|
name: preferenceName,
|
||||||
|
value: 'true',
|
||||||
|
}]));
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!visible || !license || !license.Users || percentUsed < 90) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mode: 'success' | 'info' | 'danger' = 'info';
|
||||||
|
let title: React.ReactNode = null;
|
||||||
|
let message: React.ReactNode = null;
|
||||||
|
|
||||||
|
if (percentUsed >= 90 && percentUsed < 95) {
|
||||||
|
mode = 'success';
|
||||||
|
title = (
|
||||||
|
<FormattedMessage
|
||||||
|
id='admin.license.userSeatAlert.successTitle'
|
||||||
|
defaultMessage='Your workspace has reached 90% of your licensed seats'
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
message = (
|
||||||
|
<FormattedMessage
|
||||||
|
id='admin.license.userSeatAlert.successMessage'
|
||||||
|
defaultMessage='Congratulations! Platform adoption is strong across your organization. To ensure uninterrupted growth, our team can assist in scaling your license to meet operational requirements.'
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
} else if (percentUsed >= 95 && percentUsed < 100) {
|
||||||
|
mode = 'info';
|
||||||
|
title = (
|
||||||
|
<FormattedMessage
|
||||||
|
id='admin.license.userSeatAlert.infoTitle'
|
||||||
|
defaultMessage='Your workspace has reached 95% of your licensed seats'
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
message = (
|
||||||
|
<FormattedMessage
|
||||||
|
id='admin.license.userSeatAlert.infoMessage'
|
||||||
|
defaultMessage='Your organization is approaching full license utilization. Now is a good time to assess future needs and align usage with procurement planning. Contact us to explore available options.'
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
} else if (percentUsed >= 100) {
|
||||||
|
mode = 'danger';
|
||||||
|
title = (
|
||||||
|
<FormattedMessage
|
||||||
|
id='admin.license.userSeatAlert.dangerTitle'
|
||||||
|
defaultMessage='Your workspace has reached 100% of your licensed seats'
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
message = (
|
||||||
|
<FormattedMessage
|
||||||
|
id='admin.license.userSeatAlert.dangerMessage'
|
||||||
|
defaultMessage='All licensed seats are now in use. Additional users may result in true-up charges at your next renewal. To maintain compliance and uninterrupted access, you may limit new sign-ups, or contact us to extend your license.'
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const actionButtonLeft = (
|
||||||
|
<ExternalLink
|
||||||
|
href={LicenseLinks.CONTACT_SALES}
|
||||||
|
location='license_settings_user_seat_alert'
|
||||||
|
className='style-button AlertBanner__buttonLeft'
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id='admin.license.userSeatAlert.contactSales'
|
||||||
|
defaultMessage='Contact Sales'
|
||||||
|
/>
|
||||||
|
</ExternalLink>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AlertBanner
|
||||||
|
mode={mode}
|
||||||
|
title={title}
|
||||||
|
message={message}
|
||||||
|
actionButtonLeft={actionButtonLeft}
|
||||||
|
onDismiss={handleDismiss}
|
||||||
|
closeBtnTooltip={formatMessage({id: 'admin.license.userSeatAlert.closeBtnTooltip', defaultMessage: 'Dismiss'})}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default UserSeatAlertBanner;
|
||||||
@@ -49,7 +49,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.AlertBanner__actionButtons {
|
.AlertBanner__actionButtons {
|
||||||
margin-top: 12px;
|
margin-top: 20px;
|
||||||
|
margin-bottom: 16px;
|
||||||
font-family: 'Open Sans';
|
font-family: 'Open Sans';
|
||||||
|
|
||||||
.AlertBanner__buttonLeft {
|
.AlertBanner__buttonLeft {
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ const ActivatedUserCard = ({activatedUsers, seatsPurchased, isCloud}: ActivatedU
|
|||||||
/>
|
/>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='analytics.team.overageUsersSeats'
|
id='analytics.team.overageUsersSeats'
|
||||||
defaultMessage='This exceeds total paid seats'
|
defaultMessage='This exceeds total licensed seats'
|
||||||
>
|
>
|
||||||
{(text) => <span>{text}</span>}
|
{(text) => <span>{text}</span>}
|
||||||
</FormattedMessage>
|
</FormattedMessage>
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {getFormattedFileSize} from 'mattermost-redux/utils/file_utils';
|
|||||||
|
|
||||||
import * as AdminActions from 'actions/admin_actions.jsx';
|
import * as AdminActions from 'actions/admin_actions.jsx';
|
||||||
|
|
||||||
|
import UserSeatAlertBanner from 'components/admin_console/license_settings/user_seat_alert_banner';
|
||||||
import ActivatedUserCard from 'components/analytics/activated_users_card';
|
import ActivatedUserCard from 'components/analytics/activated_users_card';
|
||||||
import ExternalLink from 'components/external_link';
|
import ExternalLink from 'components/external_link';
|
||||||
import AdminHeader from 'components/widgets/admin_console/admin_header';
|
import AdminHeader from 'components/widgets/admin_console/admin_header';
|
||||||
@@ -510,6 +511,11 @@ export default class SystemAnalytics extends React.PureComponent<Props, State> {
|
|||||||
</AdminHeader>
|
</AdminHeader>
|
||||||
<div className='admin-console__wrapper'>
|
<div className='admin-console__wrapper'>
|
||||||
<div className='admin-console__content'>
|
<div className='admin-console__content'>
|
||||||
|
<UserSeatAlertBanner
|
||||||
|
license={this.props.license}
|
||||||
|
totalUsers={this.getStatValue(stats[StatTypes.TOTAL_USERS]) || 0}
|
||||||
|
location='system_statistics'
|
||||||
|
/>
|
||||||
{banner}
|
{banner}
|
||||||
<div className='grid-statistics'>
|
<div className='grid-statistics'>
|
||||||
{systemCards}
|
{systemCards}
|
||||||
|
|||||||
@@ -27,15 +27,10 @@ import './overage_users_banner.scss';
|
|||||||
type AdminHasDismissedItArgs = {
|
type AdminHasDismissedItArgs = {
|
||||||
preferenceName: string;
|
preferenceName: string;
|
||||||
overagePreferences: PreferenceType[];
|
overagePreferences: PreferenceType[];
|
||||||
isWarningBanner: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const adminHasDismissed = ({preferenceName, overagePreferences, isWarningBanner}: AdminHasDismissedItArgs): boolean => {
|
const adminHasDismissed = ({preferenceName, overagePreferences}: AdminHasDismissedItArgs): boolean => {
|
||||||
if (isWarningBanner) {
|
return overagePreferences.find((value) => value.name === preferenceName) !== undefined;
|
||||||
return overagePreferences.find((value) => value.name === preferenceName) !== undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const OverageUsersBanner = () => {
|
const OverageUsersBanner = () => {
|
||||||
@@ -62,7 +57,7 @@ const OverageUsersBanner = () => {
|
|||||||
|
|
||||||
const overageByUsers = activeUsers - seatsPurchased;
|
const overageByUsers = activeUsers - seatsPurchased;
|
||||||
|
|
||||||
const isOverageState = isBetween5PercerntAnd10PercentPurchasedSeats || isOver10PercerntPurchasedSeats;
|
const isOverageState = overageByUsers > 0 && (isBetween5PercerntAnd10PercentPurchasedSeats || isOver10PercerntPurchasedSeats);
|
||||||
const hasPermission = isAdmin && isOverageState && !isCloud;
|
const hasPermission = isAdmin && isOverageState && !isCloud;
|
||||||
const {
|
const {
|
||||||
cta,
|
cta,
|
||||||
@@ -89,14 +84,14 @@ const OverageUsersBanner = () => {
|
|||||||
|
|
||||||
const handleClick = handleContactSalesClick;
|
const handleClick = handleContactSalesClick;
|
||||||
|
|
||||||
if (!hasPermission || adminHasDismissed({isWarningBanner: isBetween5PercerntAnd10PercentPurchasedSeats, overagePreferences, preferenceName})) {
|
if (!hasPermission || adminHasDismissed({overagePreferences, preferenceName})) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const message = (
|
const message = (
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='licensingPage.overageUsersBanner.text'
|
id='licensingPage.overageUsersBanner.text'
|
||||||
defaultMessage='(Only visible to admins) Your workspace user count has exceeded your paid license seat count by {seats, number} {seats, plural, one {seat} other {seats}}. Purchase additional seats to remain compliant.'
|
defaultMessage='(Only visible to admins) The user count exceeds the number of licensed seats by {seats, number} {seats, plural, one {seat} other {seats}}. Purchase more seats to stay compliant.'
|
||||||
values={{
|
values={{
|
||||||
seats: overageByUsers,
|
seats: overageByUsers,
|
||||||
}}
|
}}
|
||||||
@@ -105,7 +100,7 @@ const OverageUsersBanner = () => {
|
|||||||
return (
|
return (
|
||||||
<AnnouncementBar
|
<AnnouncementBar
|
||||||
type={isBetween5PercerntAnd10PercentPurchasedSeats ? AnnouncementBarTypes.ADVISOR : AnnouncementBarTypes.CRITICAL}
|
type={isBetween5PercerntAnd10PercentPurchasedSeats ? AnnouncementBarTypes.ADVISOR : AnnouncementBarTypes.CRITICAL}
|
||||||
showCloseButton={isBetween5PercerntAnd10PercentPurchasedSeats}
|
showCloseButton={true}
|
||||||
onButtonClick={handleClick}
|
onButtonClick={handleClick}
|
||||||
modalButtonText={cta}
|
modalButtonText={cta}
|
||||||
message={message}
|
message={message}
|
||||||
|
|||||||
@@ -39,12 +39,12 @@ jest.mock('actions/telemetry_actions', () => ({
|
|||||||
const seatsPurchased = 40;
|
const seatsPurchased = 40;
|
||||||
const email = 'test@mattermost.com';
|
const email = 'test@mattermost.com';
|
||||||
|
|
||||||
const seatsMinimumFor5PercentageState = (Math.ceil(seatsPurchased * OverActiveUserLimits.MIN)) + seatsPurchased;
|
const seatsMinimumFor5PercentageState = (Math.ceil(seatsPurchased * OverActiveUserLimits.MIN)) + seatsPurchased + 1;
|
||||||
|
|
||||||
const seatsMinimumFor10PercentageState = (Math.ceil(seatsPurchased * OverActiveUserLimits.MAX)) + seatsPurchased;
|
const seatsMinimumFor10PercentageState = (Math.ceil(seatsPurchased * OverActiveUserLimits.MAX)) + seatsPurchased + 1;
|
||||||
|
|
||||||
const text5PercentageState = `(Only visible to admins) Your workspace user count has exceeded your paid license seat count by ${seatsMinimumFor5PercentageState - seatsPurchased} seats. Purchase additional seats to remain compliant.`;
|
const text5PercentageState = `(Only visible to admins) The user count exceeds the number of licensed seats by ${seatsMinimumFor5PercentageState - seatsPurchased} seat. Purchase more seats to stay compliant.`;
|
||||||
const text10PercentageState = `(Only visible to admins) Your workspace user count has exceeded your paid license seat count by ${seatsMinimumFor10PercentageState - seatsPurchased} seats. Purchase additional seats to remain compliant.`;
|
const text10PercentageState = `(Only visible to admins) The user count exceeds the number of licensed seats by ${seatsMinimumFor10PercentageState - seatsPurchased} seat. Purchase more seats to stay compliant.`;
|
||||||
|
|
||||||
const contactSalesTextLink = 'Contact Sales';
|
const contactSalesTextLink = 'Contact Sales';
|
||||||
|
|
||||||
@@ -127,7 +127,7 @@ describe('components/overage_users_banner', () => {
|
|||||||
it('should not render the banner because we are not on overage state', () => {
|
it('should not render the banner because we are not on overage state', () => {
|
||||||
renderWithContext(<OverageUsersBanner/>);
|
renderWithContext(<OverageUsersBanner/>);
|
||||||
|
|
||||||
expect(screen.queryByText('(Only visible to admins) Your workspace user count has exceeded your paid license seat count by', {exact: false})).not.toBeInTheDocument();
|
expect(screen.queryByText('(Only visible to admins) The user count exceeds the number of licensed seats by', {exact: false})).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not render the banner because we are not admins', () => {
|
it('should not render the banner because we are not admins', () => {
|
||||||
@@ -146,7 +146,7 @@ describe('components/overage_users_banner', () => {
|
|||||||
|
|
||||||
renderWithContext(<OverageUsersBanner/>, store);
|
renderWithContext(<OverageUsersBanner/>, store);
|
||||||
|
|
||||||
expect(screen.queryByText('Your workspace user count has exceeded your paid license seat count by', {exact: false})).not.toBeInTheDocument();
|
expect(screen.queryByText('Your workspace user count has exceeded your licensed seat count by', {exact: false})).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not render the banner because it\'s cloud licenese', () => {
|
it('should not render the banner because it\'s cloud licenese', () => {
|
||||||
@@ -159,7 +159,7 @@ describe('components/overage_users_banner', () => {
|
|||||||
|
|
||||||
renderWithContext(<OverageUsersBanner/>, store);
|
renderWithContext(<OverageUsersBanner/>, store);
|
||||||
|
|
||||||
expect(screen.queryByText('Your workspace user count has exceeded your paid license seat count by', {exact: false})).not.toBeInTheDocument();
|
expect(screen.queryByText('Your workspace user count has exceeded your licensed seat count by', {exact: false})).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not render the 5% banner because we have dissmised it', () => {
|
it('should not render the 5% banner because we have dissmised it', () => {
|
||||||
@@ -170,7 +170,7 @@ describe('components/overage_users_banner', () => {
|
|||||||
{
|
{
|
||||||
category: Preferences.OVERAGE_USERS_BANNER,
|
category: Preferences.OVERAGE_USERS_BANNER,
|
||||||
value: 'Overage users banner watched',
|
value: 'Overage users banner watched',
|
||||||
name: `warn_overage_seats_${licenseId.substring(0, 8)}`,
|
name: `error_overage_seats_${licenseId.substring(0, 8)}`,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@@ -230,7 +230,7 @@ describe('components/overage_users_banner', () => {
|
|||||||
const salesLinkWithEncodedParams = 'https://mattermost.com/contact-sales/?qk=&qp=&qw=&qx=dGVzdEBtYXR0ZXJtb3N0LmNvbQ==&utm_source=mattermost&utm_medium=in-product';
|
const salesLinkWithEncodedParams = 'https://mattermost.com/contact-sales/?qk=&qp=&qw=&qx=dGVzdEBtYXR0ZXJtb3N0LmNvbQ==&utm_source=mattermost&utm_medium=in-product';
|
||||||
expect(windowSpy).toBeCalledWith(salesLinkWithEncodedParams, '_blank');
|
expect(windowSpy).toBeCalledWith(salesLinkWithEncodedParams, '_blank');
|
||||||
expect(trackEvent).toBeCalledTimes(1);
|
expect(trackEvent).toBeCalledTimes(1);
|
||||||
expect(trackEvent).toBeCalledWith('insights', 'click_true_up_warning', {
|
expect(trackEvent).toBeCalledWith('insights', 'click_true_up_error', {
|
||||||
cta: 'Contact Sales',
|
cta: 'Contact Sales',
|
||||||
banner: 'global banner',
|
banner: 'global banner',
|
||||||
});
|
});
|
||||||
@@ -248,7 +248,7 @@ describe('components/overage_users_banner', () => {
|
|||||||
{
|
{
|
||||||
category: Preferences.OVERAGE_USERS_BANNER,
|
category: Preferences.OVERAGE_USERS_BANNER,
|
||||||
value: 'Overage users banner watched',
|
value: 'Overage users banner watched',
|
||||||
name: `warn_overage_seats_${10}`,
|
name: `error_overage_seats_${10}`,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@@ -283,7 +283,7 @@ describe('components/overage_users_banner', () => {
|
|||||||
expect(savePreferences).toBeCalledTimes(1);
|
expect(savePreferences).toBeCalledTimes(1);
|
||||||
expect(savePreferences).toBeCalledWith(store.entities.users.profiles.current_user.id, [{
|
expect(savePreferences).toBeCalledWith(store.entities.users.profiles.current_user.id, [{
|
||||||
category: Preferences.OVERAGE_USERS_BANNER,
|
category: Preferences.OVERAGE_USERS_BANNER,
|
||||||
name: `warn_overage_seats_${licenseId.substring(0, 8)}`,
|
name: `error_overage_seats_${licenseId.substring(0, 8)}`,
|
||||||
user_id: store.entities.users.profiles.current_user.id,
|
user_id: store.entities.users.profiles.current_user.id,
|
||||||
value: 'Overage users banner watched',
|
value: 'Overage users banner watched',
|
||||||
}]);
|
}]);
|
||||||
@@ -319,7 +319,7 @@ describe('components/overage_users_banner', () => {
|
|||||||
store.entities.admin = {
|
store.entities.admin = {
|
||||||
...store.entities.admin,
|
...store.entities.admin,
|
||||||
analytics: {
|
analytics: {
|
||||||
[StatTypes.TOTAL_USERS]: seatsMinimumFor10PercentageState,
|
[StatTypes.TOTAL_USERS]: seatsMinimumFor10PercentageState + 1,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ const OverageUsersBannerNotice = () => {
|
|||||||
const preferenceName = `${prefixPreferences}_overage_seats_${prefixLicenseId}`;
|
const preferenceName = `${prefixPreferences}_overage_seats_${prefixLicenseId}`;
|
||||||
|
|
||||||
const overageByUsers = activeUsers - seatsPurchased;
|
const overageByUsers = activeUsers - seatsPurchased;
|
||||||
const isOverageState = isBetween5PercerntAnd10PercentPurchasedSeats || isOver10PercerntPurchasedSeats;
|
const isOverageState = overageByUsers > 0 && (isBetween5PercerntAnd10PercentPurchasedSeats || isOver10PercerntPurchasedSeats);
|
||||||
const hasPermission = isAdmin && isOverageState && !isCloud;
|
const hasPermission = isAdmin && isOverageState && !isCloud;
|
||||||
const {
|
const {
|
||||||
cta,
|
cta,
|
||||||
@@ -120,7 +120,7 @@ const OverageUsersBannerNotice = () => {
|
|||||||
title={
|
title={
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='licensingPage.overageUsersBanner.noticeTitle'
|
id='licensingPage.overageUsersBanner.noticeTitle'
|
||||||
defaultMessage='Your workspace user count has exceeded your paid license seat count by {seats, number} {seats, plural, one {seat} other {seats}}'
|
defaultMessage='Your workspace user count has exceeded your licensed seat count by {seats, number} {seats, plural, one {seat} other {seats}}'
|
||||||
values={{
|
values={{
|
||||||
seats: overageByUsers,
|
seats: overageByUsers,
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -41,12 +41,12 @@ jest.mock('actions/telemetry_actions', () => ({
|
|||||||
|
|
||||||
const seatsPurchased = 40;
|
const seatsPurchased = 40;
|
||||||
|
|
||||||
const seatsMinimumFor5PercentageState = (Math.ceil(seatsPurchased * OverActiveUserLimits.MIN)) + seatsPurchased;
|
const seatsMinimumFor5PercentageState = (Math.ceil(seatsPurchased * OverActiveUserLimits.MIN)) + seatsPurchased + 1;
|
||||||
|
|
||||||
const seatsMinimumFor10PercentageState = (Math.ceil(seatsPurchased * OverActiveUserLimits.MAX)) + seatsPurchased;
|
const seatsMinimumFor10PercentageState = (Math.ceil(seatsPurchased * OverActiveUserLimits.MAX)) + seatsPurchased + 1;
|
||||||
|
|
||||||
const text5PercentageState = `Your workspace user count has exceeded your paid license seat count by ${seatsMinimumFor5PercentageState - seatsPurchased} seats`;
|
const text5PercentageState = `Your workspace user count has exceeded your licensed seat count by ${seatsMinimumFor5PercentageState - seatsPurchased} seat`;
|
||||||
const text10PercentageState = `Your workspace user count has exceeded your paid license seat count by ${seatsMinimumFor10PercentageState - seatsPurchased} seats`;
|
const text10PercentageState = `Your workspace user count has exceeded your licensed seat count by ${seatsMinimumFor10PercentageState - seatsPurchased} seat`;
|
||||||
const notifyText = 'Notify your Customer Success Manager on your next true-up check';
|
const notifyText = 'Notify your Customer Success Manager on your next true-up check';
|
||||||
|
|
||||||
const contactSalesTextLink = 'Contact Sales';
|
const contactSalesTextLink = 'Contact Sales';
|
||||||
@@ -169,7 +169,7 @@ describe('components/invitation_modal/overage_users_banner_notice', () => {
|
|||||||
{
|
{
|
||||||
category: Preferences.OVERAGE_USERS_BANNER,
|
category: Preferences.OVERAGE_USERS_BANNER,
|
||||||
value: 'Overage users banner watched',
|
value: 'Overage users banner watched',
|
||||||
name: `warn_overage_seats_${licenseId.substring(0, 8)}`,
|
name: `error_overage_seats_${licenseId.substring(0, 8)}`,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@@ -234,7 +234,7 @@ describe('components/invitation_modal/overage_users_banner_notice', () => {
|
|||||||
'?utm_source=mattermost&utm_medium=in-product&utm_content=overage_users_banner&uid=current_user&sid=',
|
'?utm_source=mattermost&utm_medium=in-product&utm_content=overage_users_banner&uid=current_user&sid=',
|
||||||
);
|
);
|
||||||
expect(trackEvent).toBeCalledTimes(2);
|
expect(trackEvent).toBeCalledTimes(2);
|
||||||
expect(trackEvent).toBeCalledWith('insights', 'click_true_up_warning', {
|
expect(trackEvent).toBeCalledWith('insights', 'click_true_up_error', {
|
||||||
cta: 'Contact Sales',
|
cta: 'Contact Sales',
|
||||||
banner: 'invite modal',
|
banner: 'invite modal',
|
||||||
});
|
});
|
||||||
@@ -248,7 +248,7 @@ describe('components/invitation_modal/overage_users_banner_notice', () => {
|
|||||||
{
|
{
|
||||||
category: Preferences.OVERAGE_USERS_BANNER,
|
category: Preferences.OVERAGE_USERS_BANNER,
|
||||||
value: 'Overage users banner watched',
|
value: 'Overage users banner watched',
|
||||||
name: `warn_overage_seats_${generateId().substring(0, 8)}`,
|
name: `error_overage_seats_${generateId().substring(0, 8)}`,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@@ -289,7 +289,7 @@ describe('components/invitation_modal/overage_users_banner_notice', () => {
|
|||||||
expect(savePreferences).toBeCalledTimes(1);
|
expect(savePreferences).toBeCalledTimes(1);
|
||||||
expect(savePreferences).toBeCalledWith(store.entities.users.profiles.current_user.id, [{
|
expect(savePreferences).toBeCalledWith(store.entities.users.profiles.current_user.id, [{
|
||||||
category: Preferences.OVERAGE_USERS_BANNER,
|
category: Preferences.OVERAGE_USERS_BANNER,
|
||||||
name: `warn_overage_seats_${licenseId.substring(0, 8)}`,
|
name: `error_overage_seats_${licenseId.substring(0, 8)}`,
|
||||||
user_id: store.entities.users.profiles.current_user.id,
|
user_id: store.entities.users.profiles.current_user.id,
|
||||||
value: 'Overage users banner watched',
|
value: 'Overage users banner watched',
|
||||||
}]);
|
}]);
|
||||||
|
|||||||
@@ -1565,6 +1565,14 @@
|
|||||||
"admin.license.upload-modal.successfulUpgradeText": "You have upgraded to the {skuName} plan for {licensedUsersNum, number} seats. This is effective from {startsAt} until {expiresAt}. ",
|
"admin.license.upload-modal.successfulUpgradeText": "You have upgraded to the {skuName} plan for {licensedUsersNum, number} seats. This is effective from {startsAt} until {expiresAt}. ",
|
||||||
"admin.license.upload-modal.title": "Upload a License Key",
|
"admin.license.upload-modal.title": "Upload a License Key",
|
||||||
"admin.license.uploadFile": "Upload File",
|
"admin.license.uploadFile": "Upload File",
|
||||||
|
"admin.license.userSeatAlert.closeBtnTooltip": "Dismiss",
|
||||||
|
"admin.license.userSeatAlert.contactSales": "Contact Sales",
|
||||||
|
"admin.license.userSeatAlert.dangerMessage": "All licensed seats are now in use. Additional users may result in true-up charges at your next renewal. To maintain compliance and uninterrupted access, you may limit new sign-ups, or contact us to extend your license.",
|
||||||
|
"admin.license.userSeatAlert.dangerTitle": "Your workspace has reached 100% of your licensed seats",
|
||||||
|
"admin.license.userSeatAlert.infoMessage": "Your organization is approaching full license utilization. Now is a good time to assess future needs and align usage with procurement planning. Contact us to explore available options.",
|
||||||
|
"admin.license.userSeatAlert.infoTitle": "Your workspace has reached 95% of your licensed seats",
|
||||||
|
"admin.license.userSeatAlert.successMessage": "Congratulations! Platform adoption is strong across your organization. To ensure uninterrupted growth, our team can assist in scaling your license to meet operational requirements.",
|
||||||
|
"admin.license.userSeatAlert.successTitle": "Your workspace has reached 90% of your licensed seats",
|
||||||
"admin.licenseSettings.teamEdition.teamEditionRightPanel.acceptTermsFinal": ". Upgrading will download the binary and update your team edition.",
|
"admin.licenseSettings.teamEdition.teamEditionRightPanel.acceptTermsFinal": ". Upgrading will download the binary and update your team edition.",
|
||||||
"admin.licenseSettings.teamEdition.teamEditionRightPanel.acceptTermsInitial": "By clicking <b>Upgrade</b>, I agree to the terms of the Mattermost ",
|
"admin.licenseSettings.teamEdition.teamEditionRightPanel.acceptTermsInitial": "By clicking <b>Upgrade</b>, I agree to the terms of the Mattermost ",
|
||||||
"admin.licenseSettings.teamEdition.teamEditionRightPanel.eeLicenseLink": "Enterprise Edition License",
|
"admin.licenseSettings.teamEdition.teamEditionRightPanel.eeLicenseLink": "Enterprise Edition License",
|
||||||
@@ -3105,7 +3113,7 @@
|
|||||||
"analytics.team.activeUsers": "Active Users With Posts",
|
"analytics.team.activeUsers": "Active Users With Posts",
|
||||||
"analytics.team.newlyCreated": "Newly Created Users",
|
"analytics.team.newlyCreated": "Newly Created Users",
|
||||||
"analytics.team.noTeams": "This server has no teams for which to view statistics.",
|
"analytics.team.noTeams": "This server has no teams for which to view statistics.",
|
||||||
"analytics.team.overageUsersSeats": "This exceeds total paid seats",
|
"analytics.team.overageUsersSeats": "This exceeds total licensed seats",
|
||||||
"analytics.team.privateGroups": "Private Channels",
|
"analytics.team.privateGroups": "Private Channels",
|
||||||
"analytics.team.publicChannels": "Public Channels",
|
"analytics.team.publicChannels": "Public Channels",
|
||||||
"analytics.team.recentUsers": "Recent Active Users",
|
"analytics.team.recentUsers": "Recent Active Users",
|
||||||
@@ -4462,8 +4470,8 @@
|
|||||||
"licensingPage.infoBanner.startTrialTitle": "Start a free 30-day tral of Mattermost Enterprise Advanced",
|
"licensingPage.infoBanner.startTrialTitle": "Start a free 30-day tral of Mattermost Enterprise Advanced",
|
||||||
"licensingPage.overageUsersBanner.cta": "Contact Sales",
|
"licensingPage.overageUsersBanner.cta": "Contact Sales",
|
||||||
"licensingPage.overageUsersBanner.noticeDescription": "Notify your Customer Success Manager on your next true-up check. <a></a>",
|
"licensingPage.overageUsersBanner.noticeDescription": "Notify your Customer Success Manager on your next true-up check. <a></a>",
|
||||||
"licensingPage.overageUsersBanner.noticeTitle": "Your workspace user count has exceeded your paid license seat count by {seats, number} {seats, plural, one {seat} other {seats}}",
|
"licensingPage.overageUsersBanner.noticeTitle": "Your workspace user count has exceeded your licensed seat count by {seats, number} {seats, plural, one {seat} other {seats}}",
|
||||||
"licensingPage.overageUsersBanner.text": "(Only visible to admins) Your workspace user count has exceeded your paid license seat count by {seats, number} {seats, plural, one {seat} other {seats}}. Purchase additional seats to remain compliant.",
|
"licensingPage.overageUsersBanner.text": "(Only visible to admins) The user count exceeds the number of licensed seats by {seats, number} {seats, plural, one {seat} other {seats}}. Purchase more seats to stay compliant.",
|
||||||
"link_preview.image_preview": "Show Image preview",
|
"link_preview.image_preview": "Show Image preview",
|
||||||
"link_preview.remove_link_preview": "Remove link preview",
|
"link_preview.remove_link_preview": "Remove link preview",
|
||||||
"list_modal.paginatorCount": "{startCount, number} - {endCount, number} of {total, number} total",
|
"list_modal.paginatorCount": "{startCount, number} - {endCount, number} of {total, number} total",
|
||||||
|
|||||||
@@ -2250,9 +2250,12 @@ export const DataSearchTypes = {
|
|||||||
MESSAGES_SEARCH_TYPE: 'messages',
|
MESSAGES_SEARCH_TYPE: 'messages',
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
// Currently, the overage user limits are set to 0, which means that the overage active users warning
|
||||||
|
// is immediatly shown when over 100% usage.
|
||||||
|
// Originally it was set to Min 5% and Max 10% (5% would be a blue warning and 10% would be a red warning).
|
||||||
export const OverActiveUserLimits = {
|
export const OverActiveUserLimits = {
|
||||||
MIN: 0.05,
|
MIN: 0,
|
||||||
MAX: 0.1,
|
MAX: 0,
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const PageLoadContext = {
|
export const PageLoadContext = {
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user