mirror of
https://github.com/mattermost/mattermost.git
synced 2026-05-28 04:35:04 -04:00
[CLD-9186] Remove onboarding tasklist, add preview banner (#31203)
* Remove pricing modal. Adjust everywhere to instead open mattermost.com/pricing. When air gapped, don't show buttons to view plans. * Fix lint * Further clean up of unused code. Fixes for linter * Remove onboarding tasklist for previews, add Cloud previer banner * Fixes for linter, i18n * Revert dev lines * Fix lint * When below one minute, switch to seconds * fix linter * fixes for PR feedback * useExternalLink for opening pricing modal with enriched params * Fix i17n * Fix style, tests * Update webapp/channels/src/components/announcement_bar/cloud_preview_announcement_bar/index.tsx Co-authored-by: Guillermo Vayá <guillermo.vaya@mattermost.com> * Fix linter --------- Co-authored-by: Guillermo Vayá <guillermo.vaya@mattermost.com>
This commit is contained in:
parent
93d1ec5f1c
commit
91862811f5
10 changed files with 471 additions and 5 deletions
|
|
@ -62,7 +62,34 @@ func ensureCloudInterface(c *Context, where string) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func getPreviewSubscription(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
license := c.App.Channels().License()
|
||||
subscription := &model.Subscription{
|
||||
ID: "cloud-preview",
|
||||
ProductID: license.SkuName,
|
||||
StartAt: license.StartsAt,
|
||||
TrialEndAt: license.ExpiresAt,
|
||||
EndAt: license.ExpiresAt,
|
||||
IsFreeTrial: "true",
|
||||
IsCloudPreview: true,
|
||||
}
|
||||
|
||||
json, err := json.Marshal(subscription)
|
||||
if err != nil {
|
||||
c.Err = model.NewAppError("Api4.getSubscription", "api.cloud.request_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
return
|
||||
}
|
||||
|
||||
w.Write(json)
|
||||
}
|
||||
|
||||
func getSubscription(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
// Preview subscription is a special case for cloud preview licenses.
|
||||
if c.App.Channels().License().IsCloudPreview() {
|
||||
getPreviewSubscription(c, w, r)
|
||||
return
|
||||
}
|
||||
|
||||
ensured := ensureCloudInterface(c, "Api4.getSubscription")
|
||||
if !ensured {
|
||||
return
|
||||
|
|
|
|||
|
|
@ -191,6 +191,7 @@ type Subscription struct {
|
|||
CancelAt *int64 `json:"cancel_at"`
|
||||
WillRenew string `json:"will_renew"`
|
||||
SimulatedCurrentTimeMs *int64 `json:"simulated_current_time_ms"`
|
||||
IsCloudPreview bool `json:"is_cloud_preview"`
|
||||
}
|
||||
|
||||
func (s *Subscription) DaysToExpiration() int64 {
|
||||
|
|
|
|||
|
|
@ -350,6 +350,11 @@ func (l *License) IsStarted() bool {
|
|||
return l.StartsAt < GetMillis()
|
||||
}
|
||||
|
||||
// Cloud preview is a cloud license, that is also a trial, and the difference between the start and end date is exactly 1 hour.
|
||||
func (l *License) IsCloudPreview() bool {
|
||||
return l.IsCloud() && l.IsTrialLicense() && l.ExpiresAt-l.StartsAt == 1*time.Hour.Milliseconds()
|
||||
}
|
||||
|
||||
func (l *License) IsCloud() bool {
|
||||
return l != nil && l.Features != nil && l.Features.Cloud != nil && *l.Features.Cloud
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import type {ClientLicense, ClientConfig, WarnMetricStatus} from '@mattermost/ty
|
|||
|
||||
import withGetCloudSubscription from 'components/common/hocs/cloud/with_get_cloud_subscription';
|
||||
|
||||
import CloudPreviewAnnouncementBar from './cloud_preview_announcement_bar';
|
||||
import CloudTrialAnnouncementBar from './cloud_trial_announcement_bar';
|
||||
import CloudTrialEndAnnouncementBar from './cloud_trial_ended_announcement_bar';
|
||||
import ConfigurationAnnouncementBar from './configuration_bar';
|
||||
|
|
@ -66,6 +67,7 @@ class AnnouncementBarController extends React.PureComponent<Props> {
|
|||
let paymentAnnouncementBar = null;
|
||||
let cloudTrialAnnouncementBar = null;
|
||||
let cloudTrialEndAnnouncementBar = null;
|
||||
let cloudPreviewAnnouncementBar = null;
|
||||
const notifyAdminDowngradeDelinquencyBar = null;
|
||||
const toYearlyNudgeBannerDismissable = null;
|
||||
if (this.props.license?.Cloud === 'true') {
|
||||
|
|
@ -78,6 +80,9 @@ class AnnouncementBarController extends React.PureComponent<Props> {
|
|||
cloudTrialEndAnnouncementBar = (
|
||||
<CloudTrialEndAnnouncementBar/>
|
||||
);
|
||||
cloudPreviewAnnouncementBar = (
|
||||
<CloudPreviewAnnouncementBar/>
|
||||
);
|
||||
}
|
||||
|
||||
let autoStartTrialModal = null;
|
||||
|
|
@ -109,6 +114,7 @@ class AnnouncementBarController extends React.PureComponent<Props> {
|
|||
{paymentAnnouncementBar}
|
||||
{cloudTrialAnnouncementBar}
|
||||
{cloudTrialEndAnnouncementBar}
|
||||
{cloudPreviewAnnouncementBar}
|
||||
{notifyAdminDowngradeDelinquencyBar}
|
||||
{toYearlyNudgeBannerDismissable}
|
||||
{this.props.license?.Cloud !== 'true' && <OverageUsersBanner/>}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,283 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import * as reactRedux from 'react-redux';
|
||||
|
||||
import type {Subscription} from '@mattermost/types/cloud';
|
||||
|
||||
import {mountWithIntl} from 'tests/helpers/intl-test-helper';
|
||||
import mockStore from 'tests/test_store';
|
||||
|
||||
import CloudPreviewAnnouncementBar from './index';
|
||||
|
||||
describe('components/announcement_bar/CloudPreviewAnnouncementBar', () => {
|
||||
const useDispatchMock = jest.spyOn(reactRedux, 'useDispatch');
|
||||
|
||||
beforeEach(() => {
|
||||
useDispatchMock.mockClear();
|
||||
});
|
||||
|
||||
const baseSubscription: Subscription = {
|
||||
id: 'test-id',
|
||||
customer_id: 'test-customer',
|
||||
product_id: 'test-product',
|
||||
add_ons: [],
|
||||
start_at: Date.now() - (24 * 60 * 60 * 1000), // 1 day ago
|
||||
end_at: Date.now() + (2 * 60 * 60 * 1000), // 2 hours from now
|
||||
create_at: Date.now() - (24 * 60 * 60 * 1000),
|
||||
seats: 10,
|
||||
trial_end_at: 0,
|
||||
is_free_trial: 'false',
|
||||
is_cloud_preview: true,
|
||||
};
|
||||
|
||||
const initialState = {
|
||||
views: {
|
||||
announcementBar: {
|
||||
announcementBarState: {
|
||||
announcementBarCount: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
entities: {
|
||||
general: {
|
||||
license: {
|
||||
IsLicensed: 'true',
|
||||
Cloud: 'true',
|
||||
},
|
||||
},
|
||||
users: {
|
||||
currentUserId: 'current_user_id',
|
||||
profiles: {
|
||||
current_user_id: {roles: 'system_admin'},
|
||||
},
|
||||
},
|
||||
cloud: {
|
||||
subscription: baseSubscription,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
it('should show banner when is_cloud_preview is true and isCloud is true', () => {
|
||||
const store = mockStore(initialState);
|
||||
|
||||
const dummyDispatch = jest.fn();
|
||||
useDispatchMock.mockReturnValue(dummyDispatch);
|
||||
|
||||
const wrapper = mountWithIntl(
|
||||
<reactRedux.Provider store={store}>
|
||||
<CloudPreviewAnnouncementBar/>
|
||||
</reactRedux.Provider>,
|
||||
);
|
||||
|
||||
expect(wrapper.find('AnnouncementBar').exists()).toEqual(true);
|
||||
expect(wrapper.text()).toContain('This is your Mattermost preview environment');
|
||||
});
|
||||
|
||||
it('should not show banner when is_cloud_preview is false', () => {
|
||||
const state = JSON.parse(JSON.stringify(initialState));
|
||||
state.entities.cloud.subscription = {
|
||||
...baseSubscription,
|
||||
is_cloud_preview: false,
|
||||
};
|
||||
|
||||
const store = mockStore(state);
|
||||
|
||||
const dummyDispatch = jest.fn();
|
||||
useDispatchMock.mockReturnValue(dummyDispatch);
|
||||
|
||||
const wrapper = mountWithIntl(
|
||||
<reactRedux.Provider store={store}>
|
||||
<CloudPreviewAnnouncementBar/>
|
||||
</reactRedux.Provider>,
|
||||
);
|
||||
|
||||
expect(wrapper.find('AnnouncementBar').exists()).toEqual(false);
|
||||
});
|
||||
|
||||
it('should not show banner when not cloud', () => {
|
||||
const state = JSON.parse(JSON.stringify(initialState));
|
||||
state.entities.general.license.Cloud = 'false';
|
||||
|
||||
const store = mockStore(state);
|
||||
|
||||
const dummyDispatch = jest.fn();
|
||||
useDispatchMock.mockReturnValue(dummyDispatch);
|
||||
|
||||
const wrapper = mountWithIntl(
|
||||
<reactRedux.Provider store={store}>
|
||||
<CloudPreviewAnnouncementBar/>
|
||||
</reactRedux.Provider>,
|
||||
);
|
||||
|
||||
expect(wrapper.find('AnnouncementBar').exists()).toEqual(false);
|
||||
});
|
||||
|
||||
it('should not show banner when subscription is undefined', () => {
|
||||
const state = JSON.parse(JSON.stringify(initialState));
|
||||
state.entities.cloud.subscription = undefined;
|
||||
|
||||
const store = mockStore(state);
|
||||
|
||||
const dummyDispatch = jest.fn();
|
||||
useDispatchMock.mockReturnValue(dummyDispatch);
|
||||
|
||||
const wrapper = mountWithIntl(
|
||||
<reactRedux.Provider store={store}>
|
||||
<CloudPreviewAnnouncementBar/>
|
||||
</reactRedux.Provider>,
|
||||
);
|
||||
|
||||
expect(wrapper.find('AnnouncementBar').exists()).toEqual(false);
|
||||
});
|
||||
|
||||
it('should display time in correct format when less than a day', () => {
|
||||
const store = mockStore(initialState);
|
||||
|
||||
const dummyDispatch = jest.fn();
|
||||
useDispatchMock.mockReturnValue(dummyDispatch);
|
||||
|
||||
const wrapper = mountWithIntl(
|
||||
<reactRedux.Provider store={store}>
|
||||
<CloudPreviewAnnouncementBar/>
|
||||
</reactRedux.Provider>,
|
||||
);
|
||||
|
||||
// Should show format like "02h 00m"
|
||||
expect(wrapper.text()).toMatch(/Time left: \d{2}h \d{2}m/);
|
||||
});
|
||||
|
||||
it('should display time with days when more than a day', () => {
|
||||
const state = JSON.parse(JSON.stringify(initialState));
|
||||
state.entities.cloud.subscription = {
|
||||
...baseSubscription,
|
||||
end_at: Date.now() + (25 * 60 * 60 * 1000), // 25 hours from now
|
||||
};
|
||||
|
||||
const store = mockStore(state);
|
||||
|
||||
const dummyDispatch = jest.fn();
|
||||
useDispatchMock.mockReturnValue(dummyDispatch);
|
||||
|
||||
const wrapper = mountWithIntl(
|
||||
<reactRedux.Provider store={store}>
|
||||
<CloudPreviewAnnouncementBar/>
|
||||
</reactRedux.Provider>,
|
||||
);
|
||||
|
||||
// Should show format like "1d 01h 00m"
|
||||
expect(wrapper.text()).toMatch(/Time left: 1d \d{2}h \d{2}m/);
|
||||
});
|
||||
|
||||
it('should display only minutes when less than an hour', () => {
|
||||
const state = JSON.parse(JSON.stringify(initialState));
|
||||
state.entities.cloud.subscription = {
|
||||
...baseSubscription,
|
||||
end_at: Date.now() + (45 * 60 * 1000), // 45 minutes from now
|
||||
};
|
||||
|
||||
const store = mockStore(state);
|
||||
|
||||
const dummyDispatch = jest.fn();
|
||||
useDispatchMock.mockReturnValue(dummyDispatch);
|
||||
|
||||
const wrapper = mountWithIntl(
|
||||
<reactRedux.Provider store={store}>
|
||||
<CloudPreviewAnnouncementBar/>
|
||||
</reactRedux.Provider>,
|
||||
);
|
||||
|
||||
// Should show format like "45m"
|
||||
expect(wrapper.text()).toMatch(/Time left: \d{2}m/);
|
||||
});
|
||||
|
||||
it('should display seconds when less than a minute', () => {
|
||||
const state = JSON.parse(JSON.stringify(initialState));
|
||||
state.entities.cloud.subscription = {
|
||||
...baseSubscription,
|
||||
end_at: Date.now() + (30 * 1000), // 30 seconds from now
|
||||
};
|
||||
|
||||
const store = mockStore(state);
|
||||
|
||||
const dummyDispatch = jest.fn();
|
||||
useDispatchMock.mockReturnValue(dummyDispatch);
|
||||
|
||||
const wrapper = mountWithIntl(
|
||||
<reactRedux.Provider store={store}>
|
||||
<CloudPreviewAnnouncementBar/>
|
||||
</reactRedux.Provider>,
|
||||
);
|
||||
|
||||
// Should show format like "30s"
|
||||
expect(wrapper.text()).toMatch(/Time left: \d+s/);
|
||||
});
|
||||
|
||||
it('should display 00:00 when time has expired', () => {
|
||||
const state = JSON.parse(JSON.stringify(initialState));
|
||||
state.entities.cloud.subscription = {
|
||||
...baseSubscription,
|
||||
end_at: Date.now() - 1000, // 1 second ago
|
||||
};
|
||||
|
||||
const store = mockStore(state);
|
||||
|
||||
const dummyDispatch = jest.fn();
|
||||
useDispatchMock.mockReturnValue(dummyDispatch);
|
||||
|
||||
const wrapper = mountWithIntl(
|
||||
<reactRedux.Provider store={store}>
|
||||
<CloudPreviewAnnouncementBar/>
|
||||
</reactRedux.Provider>,
|
||||
);
|
||||
|
||||
expect(wrapper.text()).toContain('Time left: 00:00');
|
||||
});
|
||||
|
||||
it('should not be dismissable', () => {
|
||||
const store = mockStore(initialState);
|
||||
|
||||
const dummyDispatch = jest.fn();
|
||||
useDispatchMock.mockReturnValue(dummyDispatch);
|
||||
|
||||
const wrapper = mountWithIntl(
|
||||
<reactRedux.Provider store={store}>
|
||||
<CloudPreviewAnnouncementBar/>
|
||||
</reactRedux.Provider>,
|
||||
);
|
||||
|
||||
expect(wrapper.find('AnnouncementBar').prop('showCloseButton')).toEqual(false);
|
||||
});
|
||||
|
||||
it('should show contact sales button', () => {
|
||||
const store = mockStore(initialState);
|
||||
|
||||
const dummyDispatch = jest.fn();
|
||||
useDispatchMock.mockReturnValue(dummyDispatch);
|
||||
|
||||
const wrapper = mountWithIntl(
|
||||
<reactRedux.Provider store={store}>
|
||||
<CloudPreviewAnnouncementBar/>
|
||||
</reactRedux.Provider>,
|
||||
);
|
||||
|
||||
expect(wrapper.text()).toContain('Contact sales');
|
||||
expect(wrapper.find('AnnouncementBar').prop('showLinkAsButton')).toEqual(true);
|
||||
});
|
||||
|
||||
it('should have advisor type', () => {
|
||||
const store = mockStore(initialState);
|
||||
|
||||
const dummyDispatch = jest.fn();
|
||||
useDispatchMock.mockReturnValue(dummyDispatch);
|
||||
|
||||
const wrapper = mountWithIntl(
|
||||
<reactRedux.Provider store={store}>
|
||||
<CloudPreviewAnnouncementBar/>
|
||||
</reactRedux.Provider>,
|
||||
);
|
||||
|
||||
expect(wrapper.find('AnnouncementBar').prop('type')).toEqual('advisor');
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React, {useEffect, useState, useCallback} from 'react';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
import {useSelector} from 'react-redux';
|
||||
|
||||
import {InformationOutlineIcon} from '@mattermost/compass-icons/components';
|
||||
|
||||
import {getCloudSubscription} from 'mattermost-redux/selectors/entities/cloud';
|
||||
import {getLicense} from 'mattermost-redux/selectors/entities/general';
|
||||
|
||||
import {trackEvent} from 'actions/telemetry_actions';
|
||||
|
||||
import useOpenSalesLink from 'components/common/hooks/useOpenSalesLink';
|
||||
|
||||
import {AnnouncementBarTypes} from 'utils/constants';
|
||||
|
||||
import AnnouncementBar from '../default_announcement_bar';
|
||||
|
||||
const CloudPreviewAnnouncementBar: React.FC = () => {
|
||||
const subscription = useSelector(getCloudSubscription);
|
||||
const license = useSelector(getLicense);
|
||||
const isCloud = license?.Cloud === 'true';
|
||||
const [openContactSales] = useOpenSalesLink();
|
||||
|
||||
const [timeLeft, setTimeLeft] = useState<string>('');
|
||||
|
||||
const calculateTimeLeft = useCallback(() => {
|
||||
if (!subscription?.end_at) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const now = Date.now();
|
||||
const endTime = subscription.end_at;
|
||||
const timeDiff = endTime - now;
|
||||
|
||||
if (timeDiff <= 0) {
|
||||
return '00:00';
|
||||
}
|
||||
|
||||
const days = Math.floor(timeDiff / (1000 * 60 * 60 * 24));
|
||||
const hours = Math.floor((timeDiff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
|
||||
const minutes = Math.floor((timeDiff % (1000 * 60 * 60)) / (1000 * 60));
|
||||
|
||||
// If less than 1 minute, show seconds
|
||||
if (days === 0 && hours === 0 && minutes === 0) {
|
||||
const seconds = Math.floor((timeDiff % (1000 * 60)) / 1000);
|
||||
return `${seconds}s`;
|
||||
}
|
||||
|
||||
// Build time string based on what units are needed
|
||||
const parts = [];
|
||||
if (days > 0) {
|
||||
parts.push(`${days}d`);
|
||||
}
|
||||
if (hours > 0 || days > 0) {
|
||||
parts.push(`${hours.toString().padStart(2, '0')}h`);
|
||||
}
|
||||
parts.push(`${minutes.toString().padStart(2, '0')}m`);
|
||||
|
||||
return parts.join(' ');
|
||||
}, [subscription?.end_at]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!subscription?.is_cloud_preview || !isCloud) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let interval: NodeJS.Timeout;
|
||||
|
||||
const updateTimeAndScheduleNext = () => {
|
||||
setTimeLeft(calculateTimeLeft());
|
||||
|
||||
// Calculate time remaining to determine next interval
|
||||
const now = Date.now();
|
||||
const endTime = subscription.end_at || 0;
|
||||
const timeDiff = endTime - now;
|
||||
const minutesLeft = Math.floor(timeDiff / (1000 * 60));
|
||||
|
||||
// Use 1 second interval if less than 1 minute remains, otherwise 60 seconds
|
||||
const intervalTime = minutesLeft < 1 ? 1000 : 60000;
|
||||
|
||||
// Schedule the next update
|
||||
interval = setTimeout(updateTimeAndScheduleNext, intervalTime);
|
||||
};
|
||||
|
||||
// Start the update cycle
|
||||
updateTimeAndScheduleNext();
|
||||
|
||||
return () => {
|
||||
if (interval) {
|
||||
clearTimeout(interval);
|
||||
}
|
||||
};
|
||||
}, [subscription, isCloud, calculateTimeLeft]);
|
||||
|
||||
const handleContactSalesClick = useCallback((e: React.MouseEvent<HTMLButtonElement>) => {
|
||||
e.preventDefault();
|
||||
trackEvent('admin', 'cloud_preview_announcement_bar_contact_sales');
|
||||
openContactSales();
|
||||
}, [openContactSales]);
|
||||
|
||||
if (!subscription?.is_cloud_preview || !isCloud) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const message = (
|
||||
<FormattedMessage
|
||||
id='announcement_bar.cloud_preview.message'
|
||||
defaultMessage='This is your Mattermost preview environment. Time left: {timeLeft}'
|
||||
values={{timeLeft: timeLeft || '00:00'}}
|
||||
/>
|
||||
);
|
||||
|
||||
const contactSalesText = (
|
||||
<FormattedMessage
|
||||
id='announcement_bar.cloud_preview.contact_sales'
|
||||
defaultMessage='Contact sales'
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<AnnouncementBar
|
||||
type={AnnouncementBarTypes.ADVISOR}
|
||||
showCloseButton={false}
|
||||
message={message}
|
||||
icon={<InformationOutlineIcon size={16}/>}
|
||||
showLinkAsButton={true}
|
||||
onButtonClick={handleContactSalesClick}
|
||||
ctaText={contactSalesText}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default CloudPreviewAnnouncementBar;
|
||||
|
|
@ -88,8 +88,8 @@ class CloudTrialAnnouncementBarInternal extends React.PureComponent<PropsWithPri
|
|||
};
|
||||
|
||||
shouldShowBanner = () => {
|
||||
const {isFreeTrial, userIsAdmin, isCloud, isAirGapped} = this.props;
|
||||
return isFreeTrial && userIsAdmin && isCloud && !isAirGapped;
|
||||
const {isFreeTrial, userIsAdmin, isCloud, isAirGapped, subscription} = this.props;
|
||||
return isFreeTrial && userIsAdmin && isCloud && !isAirGapped && !subscription?.is_cloud_preview;
|
||||
};
|
||||
|
||||
isDismissable = () => {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,8 @@ import {CloseIcon, PlaylistCheckIcon} from '@mattermost/compass-icons/components
|
|||
|
||||
import {getPrevTrialLicense} from 'mattermost-redux/actions/admin';
|
||||
import {getMyPreferences, savePreferences} from 'mattermost-redux/actions/preferences';
|
||||
import {getConfig} from 'mattermost-redux/selectors/entities/general';
|
||||
import {getCloudSubscription} from 'mattermost-redux/selectors/entities/cloud';
|
||||
import {getConfig, getLicense} from 'mattermost-redux/selectors/entities/general';
|
||||
import {
|
||||
getBool,
|
||||
getMyPreferences as getMyPreferencesSelector,
|
||||
|
|
@ -128,6 +129,10 @@ const Button = styled.button<{open: boolean}>(({open}) => {
|
|||
const OnBoardingTaskList = (): JSX.Element | null => {
|
||||
const {formatMessage} = useIntl();
|
||||
const hasPreferences = useSelector((state: GlobalState) => Object.keys(getMyPreferencesSelector(state)).length !== 0);
|
||||
const subscription = useSelector(getCloudSubscription);
|
||||
const license = useSelector(getLicense);
|
||||
const isCloud = license?.Cloud === 'true';
|
||||
const isCloudPreview = subscription?.is_cloud_preview === true;
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(getPrevTrialLicense());
|
||||
|
|
@ -244,7 +249,7 @@ const OnBoardingTaskList = (): JSX.Element | null => {
|
|||
trackEvent(OnboardingTaskCategory, open ? OnboardingTaskList.ONBOARDING_TASK_LIST_CLOSE : OnboardingTaskList.ONBOARDING_TASK_LIST_OPEN);
|
||||
}, [open, currentUserId]);
|
||||
|
||||
if (!hasPreferences || !showTaskList || !isEnableOnboardingFlow) {
|
||||
if (!hasPreferences || !showTaskList || !isEnableOnboardingFlow || (isCloud && isCloudPreview)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
@ -303,7 +308,7 @@ const OnBoardingTaskList = (): JSX.Element | null => {
|
|||
>
|
||||
<FormattedMessage
|
||||
id='onboardingTask.checklist.dismiss_link'
|
||||
defaultMessage='No thanks, I’ll figure it out myself'
|
||||
defaultMessage="No thanks, I'll figure it out myself"
|
||||
/>
|
||||
</span>
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -3148,6 +3148,8 @@
|
|||
"analytics.team.totalUsers": "Total Activated Users",
|
||||
"analytics.team.totalUsers.title.tooltip.hint": "Also called Registered Users",
|
||||
"analytics.team.totalUsers.title.tooltip.title": "Activated users on this server",
|
||||
"announcement_bar.cloud_preview.contact_sales": "Contact sales",
|
||||
"announcement_bar.cloud_preview.message": "This is your Mattermost preview environment. Time left: {timeLeft}",
|
||||
"announcement_bar.error.email_verification_required": "Check your email inbox to verify the address.",
|
||||
"announcement_bar.error.license_expired": "{licenseSku} license is expired and some features may be disabled.",
|
||||
"announcement_bar.error.license_expiring": "{licenseSku} license expires on {date, date, long}.",
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ export type Subscription = {
|
|||
cancel_at?: number;
|
||||
will_renew?: string;
|
||||
simulated_current_time_ms?: number;
|
||||
is_cloud_preview?: boolean;
|
||||
}
|
||||
|
||||
export type Product = {
|
||||
|
|
|
|||
Loading…
Reference in a new issue