feat(editor): Show banner for non-production licenses (#6943)

https://linear.app/n8n/issue/PAY-692
This commit is contained in:
Iván Ovejero 2023-08-17 14:00:17 +02:00 committed by GitHub
parent d3f01270c7
commit 413570c49d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 33 additions and 20 deletions

View file

@ -1,6 +1,6 @@
import type express from 'express'; import type express from 'express';
import type { import type {
Banners, BannerName,
IConnections, IConnections,
ICredentialDataDecryptedObject, ICredentialDataDecryptedObject,
ICredentialNodeAccess, ICredentialNodeAccess,
@ -216,7 +216,7 @@ export interface UserSetupPayload {
export declare namespace OwnerRequest { export declare namespace OwnerRequest {
type Post = AuthenticatedRequest<{}, {}, UserSetupPayload, {}>; type Post = AuthenticatedRequest<{}, {}, UserSetupPayload, {}>;
type DismissBanner = AuthenticatedRequest<{}, {}, Partial<{ bannerName: Banners }>, {}>; type DismissBanner = AuthenticatedRequest<{}, {}, Partial<{ bannerName: BannerName }>, {}>;
} }
// ---------------------------------- // ----------------------------------

View file

@ -34,7 +34,7 @@ import type {
IUserManagementSettings, IUserManagementSettings,
WorkflowSettings, WorkflowSettings,
IUserSettings, IUserSettings,
Banners, BannerName,
} from 'n8n-workflow'; } from 'n8n-workflow';
import type { SignInType } from './constants'; import type { SignInType } from './constants';
import type { import type {
@ -1074,7 +1074,7 @@ export interface UIState {
addFirstStepOnLoad: boolean; addFirstStepOnLoad: boolean;
executionSidebarAutoRefresh: boolean; executionSidebarAutoRefresh: boolean;
bannersHeight: number; bannersHeight: number;
banners: { [key in Banners]: { dismissed: boolean; type?: 'temporary' | 'permanent' } }; banners: { [key in BannerName]: { dismissed: boolean; type?: 'temporary' | 'permanent' } };
} }
export type IFakeDoor = { export type IFakeDoor = {

View file

@ -1,10 +1,10 @@
import type { IRestApiContext } from '@/Interface'; import type { IRestApiContext } from '@/Interface';
import { makeRestApiRequest } from '@/utils/apiUtils'; import { makeRestApiRequest } from '@/utils/apiUtils';
import type { Banners } from 'n8n-workflow'; import type { BannerName } from 'n8n-workflow';
export async function dismissBannerPermanently( export async function dismissBannerPermanently(
context: IRestApiContext, context: IRestApiContext,
data: { bannerName: Banners; dismissedBanners: string[] }, data: { bannerName: BannerName; dismissedBanners: string[] },
): Promise<void> { ): Promise<void> {
return makeRestApiRequest(context, 'POST', '/owner/dismiss-banner', { banner: data.bannerName }); return makeRestApiRequest(context, 'POST', '/owner/dismiss-banner', { banner: data.bannerName });
} }

View file

@ -1,15 +1,16 @@
<script setup lang="ts"> <script setup lang="ts">
import NonProductionLicenseBanner from '@/components/banners/NonProductionLicenseBanner.vue';
import TrialOverBanner from '@/components/banners/TrialOverBanner.vue'; import TrialOverBanner from '@/components/banners/TrialOverBanner.vue';
import TrialBanner from '@/components/banners/TrialBanner.vue'; import TrialBanner from '@/components/banners/TrialBanner.vue';
import V1Banner from '@/components/banners/V1Banner.vue'; import V1Banner from '@/components/banners/V1Banner.vue';
import { useUIStore } from '@/stores/ui.store'; import { useUIStore } from '@/stores/ui.store';
import { onMounted, watch } from 'vue'; import { onMounted, watch } from 'vue';
import { getBannerRowHeight } from '@/utils'; import { getBannerRowHeight } from '@/utils';
import type { Banners } from 'n8n-workflow'; import type { BannerName } from 'n8n-workflow';
const uiStore = useUIStore(); const uiStore = useUIStore();
function shouldShowBanner(bannerName: Banners) { function shouldShowBanner(bannerName: BannerName) {
return uiStore.banners[bannerName].dismissed === false; return uiStore.banners[bannerName].dismissed === false;
} }
@ -32,5 +33,6 @@ watch(uiStore.banners, async () => {
<trial-over-banner v-if="shouldShowBanner('TRIAL_OVER')" /> <trial-over-banner v-if="shouldShowBanner('TRIAL_OVER')" />
<trial-banner v-if="shouldShowBanner('TRIAL')" /> <trial-banner v-if="shouldShowBanner('TRIAL')" />
<v1-banner v-if="shouldShowBanner('V1')" /> <v1-banner v-if="shouldShowBanner('V1')" />
<non-production-license-banner v-if="shouldShowBanner('NON_PRODUCTION_LICENSE')" />
</div> </div>
</template> </template>

View file

@ -1,9 +1,9 @@
<script lang="ts" setup> <script lang="ts" setup>
import { useUIStore } from '@/stores/ui.store'; import { useUIStore } from '@/stores/ui.store';
import type { Banners } from 'n8n-workflow'; import type { BannerName } from 'n8n-workflow';
interface Props { interface Props {
name: Banners; name: BannerName;
theme?: string; theme?: string;
customIcon?: string; customIcon?: string;
dismissible?: boolean; dismissible?: boolean;

View file

@ -0,0 +1,12 @@
<script lang="ts" setup>
import BaseBanner from '@/components/banners/BaseBanner.vue';
import { i18n as locale } from '@/plugins/i18n';
</script>
<template>
<base-banner name="NON_PRODUCTION_LICENSE" :dismissible="false">
<template #mainContent>
<span>{{ locale.baseText('banners.nonProductionLicense.message') }}</span>
</template>
</base-banner>
</template>

View file

@ -115,6 +115,7 @@
"auth.signup.setupYourAccount": "Set up your account", "auth.signup.setupYourAccount": "Set up your account",
"auth.signup.setupYourAccountError": "Problem setting up your account", "auth.signup.setupYourAccountError": "Problem setting up your account",
"auth.signup.tokenValidationError": "Issue validating invite token", "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.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.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 <a target=\"_blank\" href=\"https://docs.n8n.io/1-0-migration-checklist\">migration guide</a> for more information.", "banners.v1.message": "n8n has been updated to version 1, introducing some breaking changes. Please consult the <a target=\"_blank\" href=\"https://docs.n8n.io/1-0-migration-checklist\">migration guide</a> for more information.",

View file

@ -198,6 +198,9 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, {
this.saml.loginEnabled = settings.sso.saml.loginEnabled; this.saml.loginEnabled = settings.sso.saml.loginEnabled;
this.saml.loginLabel = settings.sso.saml.loginLabel; this.saml.loginLabel = settings.sso.saml.loginLabel;
} }
if (settings.enterprise?.showNonProdBanner) {
useUIStore().banners.NON_PRODUCTION_LICENSE.dismissed = false;
}
}, },
async getSettings(): Promise<void> { async getSettings(): Promise<void> {
const rootStore = useRootStore(); const rootStore = useRootStore();

View file

@ -56,7 +56,7 @@ import { i18n as locale } from '@/plugins/i18n';
import { useTelemetryStore } from '@/stores/telemetry.store'; import { useTelemetryStore } from '@/stores/telemetry.store';
import { getStyleTokenValue } from '@/utils/htmlUtils'; import { getStyleTokenValue } from '@/utils/htmlUtils';
import { dismissBannerPermanently } from '@/api/ui'; import { dismissBannerPermanently } from '@/api/ui';
import type { Banners } from 'n8n-workflow'; import type { BannerName } from 'n8n-workflow';
export const useUIStore = defineStore(STORES.UI, { export const useUIStore = defineStore(STORES.UI, {
state: (): UIState => ({ state: (): UIState => ({
@ -176,6 +176,7 @@ export const useUIStore = defineStore(STORES.UI, {
V1: { dismissed: true }, V1: { dismissed: true },
TRIAL: { dismissed: true }, TRIAL: { dismissed: true },
TRIAL_OVER: { dismissed: true }, TRIAL_OVER: { dismissed: true },
NON_PRODUCTION_LICENSE: { dismissed: true },
}, },
bannersHeight: 0, bannersHeight: 0,
}), }),
@ -333,12 +334,6 @@ export const useUIStore = defineStore(STORES.UI, {
}, },
}, },
actions: { actions: {
setBanners(banners: UIState['banners']): void {
this.banners = {
...this.banners,
...banners,
};
},
setMode(name: keyof Modals, mode: string): void { setMode(name: keyof Modals, mode: string): void {
this.modals[name] = { this.modals[name] = {
...this.modals[name], ...this.modals[name],
@ -541,7 +536,7 @@ export const useUIStore = defineStore(STORES.UI, {
} }
}, },
async dismissBanner( async dismissBanner(
name: Banners, name: BannerName,
type: 'temporary' | 'permanent' = 'temporary', type: 'temporary' | 'permanent' = 'temporary',
): Promise<void> { ): Promise<void> {
if (type === 'permanent') { if (type === 'permanent') {
@ -556,7 +551,7 @@ export const useUIStore = defineStore(STORES.UI, {
this.banners[name].dismissed = true; this.banners[name].dismissed = true;
this.banners[name].type = 'temporary'; this.banners[name].type = 'temporary';
}, },
showBanner(name: Banners): void { showBanner(name: BannerName): void {
this.banners[name].dismissed = false; this.banners[name].dismissed = false;
}, },
updateBannersHeight(newHeight: number): void { updateBannersHeight(newHeight: number): void {

View file

@ -2203,4 +2203,4 @@ export interface IN8nUISettings {
}; };
} }
export type Banners = 'V1' | 'TRIAL_OVER' | 'TRIAL'; export type BannerName = 'V1' | 'TRIAL_OVER' | 'TRIAL' | 'NON_PRODUCTION_LICENSE';