From 413570c49dc800cfed0581b8ae1798aa0b4bbd50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Thu, 17 Aug 2023 14:00:17 +0200 Subject: [PATCH] feat(editor): Show banner for non-production licenses (#6943) https://linear.app/n8n/issue/PAY-692 --- packages/cli/src/requests.ts | 4 ++-- packages/editor-ui/src/Interface.ts | 4 ++-- packages/editor-ui/src/api/ui.ts | 4 ++-- .../src/components/banners/BannerStack.vue | 6 ++++-- .../editor-ui/src/components/banners/BaseBanner.vue | 4 ++-- .../banners/NonProductionLicenseBanner.vue | 12 ++++++++++++ packages/editor-ui/src/plugins/i18n/locales/en.json | 1 + packages/editor-ui/src/stores/settings.store.ts | 3 +++ packages/editor-ui/src/stores/ui.store.ts | 13 ++++--------- packages/workflow/src/Interfaces.ts | 2 +- 10 files changed, 33 insertions(+), 20 deletions(-) create mode 100644 packages/editor-ui/src/components/banners/NonProductionLicenseBanner.vue diff --git a/packages/cli/src/requests.ts b/packages/cli/src/requests.ts index dfe295cea93ee..8050365a57bfb 100644 --- a/packages/cli/src/requests.ts +++ b/packages/cli/src/requests.ts @@ -1,6 +1,6 @@ import type express from 'express'; import type { - Banners, + BannerName, IConnections, ICredentialDataDecryptedObject, ICredentialNodeAccess, @@ -216,7 +216,7 @@ export interface UserSetupPayload { export declare namespace OwnerRequest { type Post = AuthenticatedRequest<{}, {}, UserSetupPayload, {}>; - type DismissBanner = AuthenticatedRequest<{}, {}, Partial<{ bannerName: Banners }>, {}>; + type DismissBanner = AuthenticatedRequest<{}, {}, Partial<{ bannerName: BannerName }>, {}>; } // ---------------------------------- diff --git a/packages/editor-ui/src/Interface.ts b/packages/editor-ui/src/Interface.ts index d38bef1f124bc..2705992a8a904 100644 --- a/packages/editor-ui/src/Interface.ts +++ b/packages/editor-ui/src/Interface.ts @@ -34,7 +34,7 @@ import type { IUserManagementSettings, WorkflowSettings, IUserSettings, - Banners, + BannerName, } from 'n8n-workflow'; import type { SignInType } from './constants'; import type { @@ -1074,7 +1074,7 @@ export interface UIState { addFirstStepOnLoad: boolean; executionSidebarAutoRefresh: boolean; bannersHeight: number; - banners: { [key in Banners]: { dismissed: boolean; type?: 'temporary' | 'permanent' } }; + banners: { [key in BannerName]: { dismissed: boolean; type?: 'temporary' | 'permanent' } }; } export type IFakeDoor = { diff --git a/packages/editor-ui/src/api/ui.ts b/packages/editor-ui/src/api/ui.ts index 25683ad46e3fd..6f4be13b07a2f 100644 --- a/packages/editor-ui/src/api/ui.ts +++ b/packages/editor-ui/src/api/ui.ts @@ -1,10 +1,10 @@ import type { IRestApiContext } from '@/Interface'; import { makeRestApiRequest } from '@/utils/apiUtils'; -import type { Banners } from 'n8n-workflow'; +import type { BannerName } from 'n8n-workflow'; export async function dismissBannerPermanently( context: IRestApiContext, - data: { bannerName: Banners; dismissedBanners: string[] }, + data: { bannerName: BannerName; dismissedBanners: string[] }, ): Promise { return makeRestApiRequest(context, 'POST', '/owner/dismiss-banner', { banner: data.bannerName }); } diff --git a/packages/editor-ui/src/components/banners/BannerStack.vue b/packages/editor-ui/src/components/banners/BannerStack.vue index c7f16b765ea60..248ac67f70a16 100644 --- a/packages/editor-ui/src/components/banners/BannerStack.vue +++ b/packages/editor-ui/src/components/banners/BannerStack.vue @@ -1,15 +1,16 @@ + + diff --git a/packages/editor-ui/src/plugins/i18n/locales/en.json b/packages/editor-ui/src/plugins/i18n/locales/en.json index 3cc1d63ccd200..e4641639a5700 100644 --- a/packages/editor-ui/src/plugins/i18n/locales/en.json +++ b/packages/editor-ui/src/plugins/i18n/locales/en.json @@ -115,6 +115,7 @@ "auth.signup.setupYourAccount": "Set up your account", "auth.signup.setupYourAccountError": "Problem setting up your account", "auth.signup.tokenValidationError": "Issue validating invite token", + "banners.nonProductionLicense.message": "This n8n instance is not licensed for production purposes!", "banners.trial.message": "1 day left in your n8n trial | {count} days left in your n8n trial", "banners.trialOver.message": "Your trial is over. Upgrade now to keep automating.", "banners.v1.message": "n8n has been updated to version 1, introducing some breaking changes. Please consult the migration guide for more information.", diff --git a/packages/editor-ui/src/stores/settings.store.ts b/packages/editor-ui/src/stores/settings.store.ts index 5fbc12ceeb41f..9b8dcd758031b 100644 --- a/packages/editor-ui/src/stores/settings.store.ts +++ b/packages/editor-ui/src/stores/settings.store.ts @@ -198,6 +198,9 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, { this.saml.loginEnabled = settings.sso.saml.loginEnabled; this.saml.loginLabel = settings.sso.saml.loginLabel; } + if (settings.enterprise?.showNonProdBanner) { + useUIStore().banners.NON_PRODUCTION_LICENSE.dismissed = false; + } }, async getSettings(): Promise { const rootStore = useRootStore(); diff --git a/packages/editor-ui/src/stores/ui.store.ts b/packages/editor-ui/src/stores/ui.store.ts index c3c51cae2680c..f3fe6c6b4a85b 100644 --- a/packages/editor-ui/src/stores/ui.store.ts +++ b/packages/editor-ui/src/stores/ui.store.ts @@ -56,7 +56,7 @@ import { i18n as locale } from '@/plugins/i18n'; import { useTelemetryStore } from '@/stores/telemetry.store'; import { getStyleTokenValue } from '@/utils/htmlUtils'; import { dismissBannerPermanently } from '@/api/ui'; -import type { Banners } from 'n8n-workflow'; +import type { BannerName } from 'n8n-workflow'; export const useUIStore = defineStore(STORES.UI, { state: (): UIState => ({ @@ -176,6 +176,7 @@ export const useUIStore = defineStore(STORES.UI, { V1: { dismissed: true }, TRIAL: { dismissed: true }, TRIAL_OVER: { dismissed: true }, + NON_PRODUCTION_LICENSE: { dismissed: true }, }, bannersHeight: 0, }), @@ -333,12 +334,6 @@ export const useUIStore = defineStore(STORES.UI, { }, }, actions: { - setBanners(banners: UIState['banners']): void { - this.banners = { - ...this.banners, - ...banners, - }; - }, setMode(name: keyof Modals, mode: string): void { this.modals[name] = { ...this.modals[name], @@ -541,7 +536,7 @@ export const useUIStore = defineStore(STORES.UI, { } }, async dismissBanner( - name: Banners, + name: BannerName, type: 'temporary' | 'permanent' = 'temporary', ): Promise { if (type === 'permanent') { @@ -556,7 +551,7 @@ export const useUIStore = defineStore(STORES.UI, { this.banners[name].dismissed = true; this.banners[name].type = 'temporary'; }, - showBanner(name: Banners): void { + showBanner(name: BannerName): void { this.banners[name].dismissed = false; }, updateBannersHeight(newHeight: number): void { diff --git a/packages/workflow/src/Interfaces.ts b/packages/workflow/src/Interfaces.ts index 00fc5ecab503e..a1a403ff0595b 100644 --- a/packages/workflow/src/Interfaces.ts +++ b/packages/workflow/src/Interfaces.ts @@ -2203,4 +2203,4 @@ export interface IN8nUISettings { }; } -export type Banners = 'V1' | 'TRIAL_OVER' | 'TRIAL'; +export type BannerName = 'V1' | 'TRIAL_OVER' | 'TRIAL' | 'NON_PRODUCTION_LICENSE';