From bf8c22f33d19fd93bbe58c4fa23ce5aae34616c9 Mon Sep 17 00:00:00 2001 From: David Hartunian Date: Mon, 19 Aug 2024 17:20:19 -0400 Subject: [PATCH] ui: add license change notification to db console This change adds a dismissable alert to the Overview page of DB Console that informs users about upcoming license changes. This popup is only shown if the cluster does not have an active "Enterprise" license The popup links to this page: "https://www.cockroachlabs.com/enterprise-license-update/" When the popup is dismissed, the dismissal is stored in the DB for this user and they don't see this notification again. Resolves: CRDB-40939 Release note: None --- .../workspaces/db-console/src/redux/alerts.ts | 115 +++++++++++++++--- .../workspaces/db-console/src/redux/uiData.ts | 5 + pkg/ui/workspaces/db-console/src/util/docs.ts | 1 + .../shared/components/alertBox/alertbox.styl | 7 ++ .../shared/components/alertBox/index.tsx | 3 + .../views/shared/components/icons/index.tsx | 13 ++ 6 files changed, 130 insertions(+), 14 deletions(-) diff --git a/pkg/ui/workspaces/db-console/src/redux/alerts.ts b/pkg/ui/workspaces/db-console/src/redux/alerts.ts index 22b24c796a45..a6c7b9f0e6f6 100644 --- a/pkg/ui/workspaces/db-console/src/redux/alerts.ts +++ b/pkg/ui/workspaces/db-console/src/redux/alerts.ts @@ -47,6 +47,7 @@ import { import { LocalSetting } from "./localsettings"; import { AdminUIState, AppDispatch } from "./state"; import { + LICENSE_UPDATE_DISMISSED_KEY, VERSION_DISMISSED_KEY, INSTRUCTIONS_BOX_COLLAPSED_KEY, saveUIData, @@ -61,6 +62,7 @@ export enum AlertLevel { WARNING, CRITICAL, SUCCESS, + INFORMATION, } export interface AlertInfo { @@ -635,20 +637,6 @@ export const upgradeNotFinalizedWarningSelector = createSelector( }, ); -/** - * Selector which returns an array of all active alerts which should be - * displayed in the overview list page, these should be non-critical alerts. - */ - -export const overviewListAlertsSelector = createSelector( - staggeredVersionWarningSelector, - clusterPreserveDowngradeOptionOvertimeSelector, - upgradeNotFinalizedWarningSelector, - (...alerts: Alert[]): Alert[] => { - return without(alerts, null, undefined); - }, -); - /** * Selector which returns an array of all active alerts which should be * displayed in the alerts panel, which is embedded within the cluster overview @@ -707,6 +695,105 @@ export const licenseTypeSelector = createSelector( data => licenseTypeNames.get(data.LicenseType) || "None", ); +export const licenseUpdateDismissedLocalSetting = new LocalSetting( + "license_update_dismissed", + localSettingsSelector, + moment(0), +); + +const licenseUpdateDismissedPersistentLoadedSelector = createSelector( + (state: AdminUIState) => state.uiData, + uiData => uiData && has(uiData, LICENSE_UPDATE_DISMISSED_KEY), +); + +const licenseUpdateDismissedPersistentSelector = createSelector( + (state: AdminUIState) => state.uiData, + uiData => { + return ( + (uiData && + uiData[LICENSE_UPDATE_DISMISSED_KEY] && + uiData[LICENSE_UPDATE_DISMISSED_KEY].data && + moment(uiData[LICENSE_UPDATE_DISMISSED_KEY].data)) || + moment(0) + ); + }, +); + +export const licenseUpdateNotificationSelector = createSelector( + licenseTypeSelector, + licenseUpdateDismissedLocalSetting.selector, + licenseUpdateDismissedPersistentSelector, + licenseUpdateDismissedPersistentLoadedSelector, + ( + licenseType, + licenseUpdateDismissed, + licenseUpdateDismissedPersistent, + licenseUpdateDismissedPersistentLoaded, + ): Alert => { + // If customer has Enterprise license they don't need to worry about this. + if (licenseType === "Enterprise") { + return undefined; + } + + // If the notification has been dismissed based on the session storage + // timestamp, don't show it. + if (licenseUpdateDismissed.isAfter(moment(0))) { + return undefined; + } + + // If the notification has been dismissed based on the uiData + // storage in the cluster, don't show it. Note that this is + // different from how version upgrade notifications work, this one + // is dismissed forever and won't return even if you upgrade + // further or time passes. + if ( + licenseUpdateDismissedPersistentLoaded && + licenseUpdateDismissedPersistent ) { + return undefined; + } + + return { + level: AlertLevel.INFORMATION, + title: "Coming November 18, 2024", + text: "Important changes to CockroachDB’s licensing model.", + link: docsURL.enterpriseLicenseUpdate, + dismiss: (dispatch: any) => { + const dismissedAt = moment(); + // Note(davidh): I haven't been able to find historical context + // for why some alerts have both a "local" and a "persistent" + // dismissal. My thinking is that just the persistent dismissal + // should be adequate, but I'm preserving that behavior here to + // match the version upgrade notification. + + // Dismiss locally. + dispatch(licenseUpdateDismissedLocalSetting.set(dismissedAt)); + // Dismiss persistently. + return dispatch( + saveUIData({ + key: LICENSE_UPDATE_DISMISSED_KEY, + value: dismissedAt.valueOf(), + }) + ); + }, + }; + }, +); + +/** + * Selector which returns an array of all active alerts which should be + * displayed in the overview list page, these should be non-critical alerts. + */ + +export const overviewListAlertsSelector = createSelector( + staggeredVersionWarningSelector, + clusterPreserveDowngradeOptionOvertimeSelector, + upgradeNotFinalizedWarningSelector, + licenseUpdateNotificationSelector, + (...alerts: Alert[]): Alert[] => { + return without(alerts, null, undefined); + }, +); + // daysUntilLicenseExpiresSelector returns number of days remaining before license expires. export const daysUntilLicenseExpiresSelector = createSelector( getDataFromServer, diff --git a/pkg/ui/workspaces/db-console/src/redux/uiData.ts b/pkg/ui/workspaces/db-console/src/redux/uiData.ts index 7286006a19fc..f3b7c76f5cbc 100644 --- a/pkg/ui/workspaces/db-console/src/redux/uiData.ts +++ b/pkg/ui/workspaces/db-console/src/redux/uiData.ts @@ -62,6 +62,11 @@ export class OptInAttributes { // was last dismissed. export const VERSION_DISMISSED_KEY = "version_dismissed"; +// LICENSE_UPDATE_DISMISSED_KEY is the uiData key on the server that tracks when the licence +// update banner was last dismissed. This banner notifies the user that we've changed our +// licensing if they're deployed without an active license. +export const LICENSE_UPDATE_DISMISSED_KEY = "license_update_dismissed"; + // INSTRUCTIONS_BOX_COLLAPSED_KEY is the uiData key on the server that tracks whether the // instructions box on the cluster viz has been collapsed or not. export const INSTRUCTIONS_BOX_COLLAPSED_KEY = diff --git a/pkg/ui/workspaces/db-console/src/util/docs.ts b/pkg/ui/workspaces/db-console/src/util/docs.ts index f1ace7749197..c9df508f7c27 100644 --- a/pkg/ui/workspaces/db-console/src/util/docs.ts +++ b/pkg/ui/workspaces/db-console/src/util/docs.ts @@ -63,6 +63,7 @@ export let upgradeTroubleshooting: string; export let licensingFaqs: string; // Note that these explicitly don't use the current version, since we want to // link to the most up-to-date documentation available. +export const enterpriseLicenseUpdate = "https://www.cockroachlabs.com/enterprise-license-update/" export const upgradeCockroachVersion = "https://www.cockroachlabs.com/docs/stable/upgrade-cockroach-version.html"; export const enterpriseLicensing = diff --git a/pkg/ui/workspaces/db-console/src/views/shared/components/alertBox/alertbox.styl b/pkg/ui/workspaces/db-console/src/views/shared/components/alertBox/alertbox.styl index 3b80a4e2bd34..d0773a910572 100644 --- a/pkg/ui/workspaces/db-console/src/views/shared/components/alertBox/alertbox.styl +++ b/pkg/ui/workspaces/db-console/src/views/shared/components/alertBox/alertbox.styl @@ -46,6 +46,13 @@ border-color $notification-info-border-color background-color $notification-info-fill-color + &--information + color $body-color + border-color $notification-info-border-color + background-color $notification-info-fill-color + path + fill $colors--primary-blue-3 + &--warning color $body-color border-color $notification-warning-border-color diff --git a/pkg/ui/workspaces/db-console/src/views/shared/components/alertBox/index.tsx b/pkg/ui/workspaces/db-console/src/views/shared/components/alertBox/index.tsx index 2974edf9aa59..c14cd7fbb016 100644 --- a/pkg/ui/workspaces/db-console/src/views/shared/components/alertBox/index.tsx +++ b/pkg/ui/workspaces/db-console/src/views/shared/components/alertBox/index.tsx @@ -17,6 +17,7 @@ import { warningIcon, notificationIcon, criticalIcon, + informationIcon, } from "src/views/shared/components/icons"; import "./alertbox.styl"; @@ -27,6 +28,8 @@ function alertIcon(level: AlertLevel) { return trustIcon(criticalIcon); case AlertLevel.WARNING: return trustIcon(warningIcon); + case AlertLevel.INFORMATION: + return trustIcon(informationIcon); default: return trustIcon(notificationIcon); } diff --git a/pkg/ui/workspaces/db-console/src/views/shared/components/icons/index.tsx b/pkg/ui/workspaces/db-console/src/views/shared/components/icons/index.tsx index 6b56105e646b..e44910770229 100644 --- a/pkg/ui/workspaces/db-console/src/views/shared/components/icons/index.tsx +++ b/pkg/ui/workspaces/db-console/src/views/shared/components/icons/index.tsx @@ -98,6 +98,19 @@ export const warningIcon = ` `; +export const informationIcon = ` + + + + + + + + + + +` + export const notificationIcon = `