mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
feat(editor): Show banner for non-production licenses (#6943)
https://linear.app/n8n/issue/PAY-692
This commit is contained in:
parent
d3f01270c7
commit
413570c49d
|
@ -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 }>, {}>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------
|
// ----------------------------------
|
||||||
|
|
|
@ -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 = {
|
||||||
|
|
|
@ -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 });
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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>
|
|
@ -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.",
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -2203,4 +2203,4 @@ export interface IN8nUISettings {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Banners = 'V1' | 'TRIAL_OVER' | 'TRIAL';
|
export type BannerName = 'V1' | 'TRIAL_OVER' | 'TRIAL' | 'NON_PRODUCTION_LICENSE';
|
||||||
|
|
Loading…
Reference in a new issue