feat(editor): Implement new banners framework (#6603)
* ⚡ Implemented new grid row - banners * ✨ Fixing node creator and executions sidebar position after layout update * 💄 Added configurable round corners to the Callout component * ⚡ Fixing mouse position detection and main tab bar position * ⚡ Implemented basic banner component structure * ⚡ Implemented banner state and dismiss logic * ⚡ Fixing grid layout. Updating banners height state dynamically * ⚡ Fix zoom to fit position, mouse position in demo mode and callout vertical alignment * ⚡ Implementing proper trial banners logic * 💄 Only showing execution usage data once the sidebar is fully expanded * ✨ Implemented permanent/temporary dismiss logic for v1 flag * ⚡ Minor refactoring of banner logic * ⚡ Updating permanent dismiss logic to work with all banners * 👕 Fixing linting errors * ✔️ Updating Callout component test snapshots * 💄 Tweaking zoom to fit position * ✔️ Updating testing endpoints to use new store data * ✅ Added banners unit tests * ✔️ Fixing failing banner tests * ✅ Added more banner tests * ⚡ Updating banners dimensions on resize, removing leftover code * ✔️ Removing store import from API file * 👕 Fixing lint errors * ⚡ Updating migration files * ⚡ Using query parameters in migrations * 👌 Addressing design review feedback * ⚡ Updating upgrade plan button click * ⚡ Updating the migrations syntax * 👌 Updating permanent banner dismiss endpoint and back-end logic * 👌 Refactoring trial banner component and ui store * 👌 Addressing more points from code review * 👌 Moving DOM logic from the store * ✔️ Updated callout component snapshots * 👌 Updating mysql migration file * ✔️ Updating e2e test canvas coordinates after setting it's position to absolute * 👌 Addressing back-end review feedback * 👌 Improving typing around banners * 👕 Fixing lint errors
This commit is contained in:
committed by
GitHub
parent
ff0759530d
commit
4240e76253
@@ -31,6 +31,7 @@ import { useUIStore } from './ui.store';
|
||||
import { useUsersStore } from './users.store';
|
||||
import { useVersionsStore } from './versions.store';
|
||||
import { makeRestApiRequest } from '@/utils';
|
||||
import { useCloudPlanStore } from './cloudPlan.store';
|
||||
|
||||
export const useSettingsStore = defineStore(STORES.SETTINGS, {
|
||||
state: (): ISettingsState => ({
|
||||
@@ -170,6 +171,9 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, {
|
||||
isDefaultAuthenticationSaml(): boolean {
|
||||
return this.userManagement.authenticationMethod === UserManagementAuthenticationMethod.Saml;
|
||||
},
|
||||
permanentlyDismissedBanners(): string[] {
|
||||
return this.settings.banners?.dismissed ?? [];
|
||||
},
|
||||
isBelowUserQuota(): boolean {
|
||||
const userStore = useUsersStore();
|
||||
return (
|
||||
@@ -219,8 +223,14 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, {
|
||||
rootStore.setN8nMetadata(settings.n8nMetadata || {});
|
||||
rootStore.setDefaultLocale(settings.defaultLocale);
|
||||
rootStore.setIsNpmAvailable(settings.isNpmAvailable);
|
||||
if (settings.banners.v1.dismissed) {
|
||||
useUIStore().setBanners({ v1: { dismissed: true, mode: 'permanent' } });
|
||||
|
||||
const isV1BannerDismissedPermanently = settings.banners.dismissed.includes('V1');
|
||||
if (
|
||||
!isV1BannerDismissedPermanently &&
|
||||
useRootStore().versionCli.startsWith('1.') &&
|
||||
!useCloudPlanStore().userIsTrialing
|
||||
) {
|
||||
useUIStore().showBanner('V1');
|
||||
}
|
||||
|
||||
useVersionsStore().setVersionNotificationSettings(settings.versionNotifications);
|
||||
|
||||
@@ -34,6 +34,7 @@ import {
|
||||
SOURCE_CONTROL_PULL_MODAL_KEY,
|
||||
} from '@/constants';
|
||||
import type {
|
||||
CloudUpdateLinkSourceType,
|
||||
CurlToJSONResponse,
|
||||
IFakeDoorLocation,
|
||||
IMenuItem,
|
||||
@@ -41,6 +42,7 @@ import type {
|
||||
IOnboardingCallPrompt,
|
||||
IUser,
|
||||
UIState,
|
||||
UTMCampaign,
|
||||
XYPosition,
|
||||
} from '@/Interface';
|
||||
import { defineStore } from 'pinia';
|
||||
@@ -53,7 +55,9 @@ import type { BaseTextKey } from '@/plugins/i18n';
|
||||
import { i18n as locale } from '@/plugins/i18n';
|
||||
import type { Modals, NewCredentialsModal } from '@/Interface';
|
||||
import { useTelemetryStore } from '@/stores/telemetry.store';
|
||||
import { dismissV1BannerPermanently } from '@/api/ui';
|
||||
import { getStyleTokenValue } from '@/utils';
|
||||
import { dismissBannerPermanently } from '@/api/ui';
|
||||
import type { Banners } from 'n8n-workflow';
|
||||
|
||||
export const useUIStore = defineStore(STORES.UI, {
|
||||
state: (): UIState => ({
|
||||
@@ -144,12 +148,6 @@ export const useUIStore = defineStore(STORES.UI, {
|
||||
},
|
||||
modalStack: [],
|
||||
sidebarMenuCollapsed: true,
|
||||
banners: {
|
||||
v1: {
|
||||
dismissed: false,
|
||||
mode: 'temporary',
|
||||
},
|
||||
},
|
||||
isPageLoading: true,
|
||||
currentView: '',
|
||||
mainPanelPosition: 0.5,
|
||||
@@ -191,6 +189,12 @@ export const useUIStore = defineStore(STORES.UI, {
|
||||
nodeViewInitialized: false,
|
||||
addFirstStepOnLoad: false,
|
||||
executionSidebarAutoRefresh: true,
|
||||
banners: {
|
||||
V1: { dismissed: true },
|
||||
TRIAL: { dismissed: true },
|
||||
TRIAL_OVER: { dismissed: true },
|
||||
},
|
||||
bannersHeight: 0,
|
||||
}),
|
||||
getters: {
|
||||
contextBasedTranslationKeys() {
|
||||
@@ -341,6 +345,9 @@ export const useUIStore = defineStore(STORES.UI, {
|
||||
return linkUrl;
|
||||
};
|
||||
},
|
||||
headerHeight() {
|
||||
return Number(getStyleTokenValue('--header-height'));
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
setBanners(banners: UIState['banners']): void {
|
||||
@@ -525,27 +532,15 @@ export const useUIStore = defineStore(STORES.UI, {
|
||||
toggleSidebarMenuCollapse(): void {
|
||||
this.sidebarMenuCollapsed = !this.sidebarMenuCollapsed;
|
||||
},
|
||||
async dismissBanner(bannerType: 'v1', mode: 'temporary' | 'permanent'): Promise<void> {
|
||||
if (mode === 'permanent') {
|
||||
await dismissV1BannerPermanently(useRootStore().getRestApiContext);
|
||||
this.banners[bannerType].dismissed = true;
|
||||
this.banners[bannerType].mode = 'permanent';
|
||||
return;
|
||||
}
|
||||
|
||||
this.banners[bannerType].dismissed = true;
|
||||
this.banners[bannerType].mode = 'temporary';
|
||||
},
|
||||
restoreBanner(bannerType: 'v1'): void {
|
||||
if (this.banners[bannerType].dismissed && this.banners[bannerType].mode === 'temporary') {
|
||||
this.banners[bannerType].dismissed = false;
|
||||
}
|
||||
},
|
||||
async getCurlToJson(curlCommand: string): Promise<CurlToJSONResponse> {
|
||||
const rootStore = useRootStore();
|
||||
return getCurlToJson(rootStore.getRestApiContext, curlCommand);
|
||||
},
|
||||
goToUpgrade(source: string, utm_campaign: string, mode: 'open' | 'redirect' = 'open'): void {
|
||||
goToUpgrade(
|
||||
source: CloudUpdateLinkSourceType,
|
||||
utm_campaign: UTMCampaign,
|
||||
mode: 'open' | 'redirect' = 'open',
|
||||
): void {
|
||||
const { usageLeft, trialDaysLeft, userIsTrialing } = useCloudPlanStore();
|
||||
const { executionsLeft, workflowsLeft } = usageLeft;
|
||||
useTelemetryStore().track('User clicked upgrade CTA', {
|
||||
@@ -562,5 +557,27 @@ export const useUIStore = defineStore(STORES.UI, {
|
||||
location.href = this.upgradeLinkUrl(source, utm_campaign);
|
||||
}
|
||||
},
|
||||
async dismissBanner(
|
||||
name: Banners,
|
||||
type: 'temporary' | 'permanent' = 'temporary',
|
||||
): Promise<void> {
|
||||
if (type === 'permanent') {
|
||||
await dismissBannerPermanently(useRootStore().getRestApiContext, {
|
||||
bannerName: name,
|
||||
dismissedBanners: useSettingsStore().permanentlyDismissedBanners,
|
||||
});
|
||||
this.banners[name].dismissed = true;
|
||||
this.banners[name].type = 'permanent';
|
||||
return;
|
||||
}
|
||||
this.banners[name].dismissed = true;
|
||||
this.banners[name].type = 'temporary';
|
||||
},
|
||||
showBanner(name: Banners): void {
|
||||
this.banners[name].dismissed = false;
|
||||
},
|
||||
updateBannersHeight(newHeight: number): void {
|
||||
this.bannersHeight = newHeight;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user