[CLD-6536] Adjustments to cloud annual renewal announcement bar (#25927)

* Adjustments to cloud annual renewal announcement bar

* Add exclusion for trials

* Add more exceptions for when on trial during renewal period

* Add support for simulated_current_time_ms

* A few more changes to allow us to test this post-merge

* Fix tests, pipeline

* Final fix around emails

---------

Co-authored-by: Mattermost Build <build@mattermost.com>
Этот коммит содержится в:
Nick Misasi
2024-01-16 17:48:59 -05:00
коммит произвёл GitHub
родитель d1e37783cc
Коммит 7039176d31
8 изменённых файлов: 50 добавлений и 22 удалений

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

@@ -11,7 +11,7 @@ import useGetSubscription from 'components/common/hooks/useGetSubscription';
import useOpenCloudPurchaseModal from 'components/common/hooks/useOpenCloudPurchaseModal';
import useOpenSalesLink from 'components/common/hooks/useOpenSalesLink';
import {daysToExpiration} from 'utils/cloud_utils';
import {daysToCancellation, daysToExpiration} from 'utils/cloud_utils';
export const creditCardExpiredBanner = (setShowCreditCardBanner: (value: boolean) => void) => {
return (
@@ -68,8 +68,8 @@ export const CloudAnnualRenewalBanner = () => {
if (!subscription || !subscription.cancel_at || (subscription.will_renew === 'true' && !subscription.delinquent_since)) {
return null;
}
const daysUntilExpiration = daysToExpiration(subscription?.end_at);
const daysUntilCancelation = daysToExpiration(subscription?.cancel_at);
const daysUntilExpiration = daysToExpiration(subscription);
const daysUntilCancelation = daysToCancellation(subscription);
const renewButton = (
<button
className='btn btn-primary'
@@ -96,6 +96,11 @@ export const CloudAnnualRenewalBanner = () => {
message: <></>,
};
// If outside the 60 day window or on a trial, don't show this banner.
if (daysUntilExpiration > 60 || subscription.is_free_trial === 'true') {
return null;
}
if (daysUntilExpiration <= 7) {
alertBannerProps.mode = 'danger';
}

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

@@ -59,7 +59,7 @@ const CloudAnnualRenewalAnnouncementBar = () => {
return 0;
}
return daysToExpiration(subscription.end_at);
return daysToExpiration(subscription);
}, [subscription]);
const handleDismiss = (banner: string) => {
@@ -89,7 +89,7 @@ const CloudAnnualRenewalAnnouncementBar = () => {
...defaultProps,
type: '',
};
if (between(daysUntilExpiration, 31, 60) && !hasDismissed60DayBanner) {
if (between(daysUntilExpiration, 31, 60)) {
if (hasDismissed60DayBanner) {
return null;
}
@@ -112,6 +112,7 @@ const CloudAnnualRenewalAnnouncementBar = () => {
handleClose: () => handleDismiss(CloudBanners.ANNUAL_RENEWAL_30_DAY),
};
} else if (between(daysUntilExpiration, 0, 7) && !isDelinquencySubscription()) {
// This banner is not dismissable
bannerProps = {
...defaultProps,
message: (<>{formatMessage({id: 'cloud_annual_renewal.banner.message.7', defaultMessage: 'Your annual subscription expires in {days} days. Failure to renew will result in your workspace being deleted.'}, {days: daysUntilExpiration})}</>),
@@ -119,13 +120,16 @@ const CloudAnnualRenewalAnnouncementBar = () => {
type: AnnouncementBarTypes.CRITICAL,
showCloseButton: false,
};
} else {
// If none of the above, return null, so that a blank announcement bar isn't visible
return null;
}
return <AnnouncementBar {...bannerProps}/>;
}, [daysUntilExpiration, hasDismissed60DayBanner, hasDismissed30DayBanner]);
// Delinquent subscriptions will have a cancel_at time, but the banner is handled separately
if (!cloudAnnualRenewalsEnabled || !subscription || !subscription.cancel_at || subscription.will_renew === 'true' || isDelinquencySubscription() || !isAdmin || daysUntilExpiration > 60) {
if (!cloudAnnualRenewalsEnabled || !subscription || !subscription.cancel_at || subscription.is_free_trial === 'true' || subscription.will_renew === 'true' || isDelinquencySubscription() || !isAdmin || daysUntilExpiration > 60) {
return null;
}

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

@@ -34,7 +34,7 @@ function mapStateToProps(state: GlobalState) {
const subscription = state.entities.cloud.subscription;
const isDelinquencyModal = Boolean(state.entities.cloud.subscription?.delinquent_since);
const isRenewalModal = daysToExpiration(Number(state.entities.cloud.subscription?.end_at)) <= 60 && !isDelinquencyModal && getConfig(state).FeatureFlags?.CloudAnnualRenewals;
const isRenewalModal = daysToExpiration(state.entities.cloud.subscription) <= 60 && state.entities.cloud.subscription?.is_free_trial === 'false' && !isDelinquencyModal && getConfig(state).FeatureFlags?.CloudAnnualRenewals;
const products = state.entities.cloud!.products;
const yearlyProducts = findOnlyYearlyProducts(products || {});

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

@@ -1,7 +1,7 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import type {CloudCustomer, InvoiceLineItem} from '@mattermost/types/cloud';
import type {CloudCustomer, InvoiceLineItem, Subscription} from '@mattermost/types/cloud';
import {trackEvent} from 'actions/telemetry_actions';
@@ -47,9 +47,22 @@ export function openExternalPricingLink() {
export const FREEMIUM_TO_ENTERPRISE_TRIAL_LENGTH_DAYS = 30;
export function daysToExpiration(expirationDate: number): number {
const now = new Date();
const expiration = new Date(expirationDate);
export function daysUntil(end?: number, simulatedCurrentTimeMs?: number) {
let now = new Date();
if (simulatedCurrentTimeMs) {
now = new Date(simulatedCurrentTimeMs);
}
const expiration = new Date(end || 0);
const diff = expiration.getTime() - now.getTime();
return Math.ceil(diff / (1000 * 3600 * 24));
}
export function daysToExpiration(subscription?: Subscription): number {
return daysUntil(subscription?.end_at, subscription?.simulated_current_time_ms);
}
export function daysToCancellation(subscription?: Subscription): number {
return daysUntil(subscription?.cancel_at, subscription?.simulated_current_time_ms);
}

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

@@ -51,6 +51,7 @@ export type Subscription = {
billing_type?: string;
cancel_at?: number;
will_renew?: string;
simulated_current_time_ms?: number;
}
export type Product = {