diff --git a/packages/editor-ui/src/components/ExecutionsUsage.vue b/packages/editor-ui/src/components/ExecutionsUsage.vue index 41d9d84b161d7..1d8e012bfc7b4 100644 --- a/packages/editor-ui/src/components/ExecutionsUsage.vue +++ b/packages/editor-ui/src/components/ExecutionsUsage.vue @@ -66,8 +66,8 @@ import { i18n as locale } from '@/plugins/i18n'; import { DateTime } from 'luxon'; import type { CloudPlanAndUsageData } from '@/Interface'; -import { CLOUD_CHANGE_PLAN_PAGE } from '@/constants'; import { computed } from 'vue'; +import { useUIStore } from '@/stores'; const PROGRESS_BAR_MINIMUM_THRESHOLD = 8; @@ -114,7 +114,7 @@ const maxExecutions = computed(() => { }); const onUpgradeClicked = () => { - location.href = CLOUD_CHANGE_PLAN_PAGE; + useUIStore().goToUpgrade('canvas-nav', 'upgrade-canvas-nav', 'redirect'); }; diff --git a/packages/editor-ui/src/components/MainHeader/WorkflowDetails.vue b/packages/editor-ui/src/components/MainHeader/WorkflowDetails.vue index 303489203761d..887c4d977e46a 100644 --- a/packages/editor-ui/src/components/MainHeader/WorkflowDetails.vue +++ b/packages/editor-ui/src/components/MainHeader/WorkflowDetails.vue @@ -162,6 +162,7 @@ import { getWorkflowPermissions } from '@/permissions'; import { useUsersStore } from '@/stores/users.store'; import { useUsageStore } from '@/stores/usage.store'; import { createEventBus } from 'n8n-design-system'; +import { useCloudPlanStore } from '@/stores'; const hasChanged = (prev: string[], curr: string[]) => { if (prev.length !== curr.length) { @@ -212,6 +213,7 @@ export default defineComponent({ useUsageStore, useWorkflowsStore, useUsersStore, + useCloudPlanStore, ), currentUser(): IUser | null { return this.usersStore.currentUser; diff --git a/packages/editor-ui/src/stores/cloudPlan.store.ts b/packages/editor-ui/src/stores/cloudPlan.store.ts index b034d711b0380..a90363844099b 100644 --- a/packages/editor-ui/src/stores/cloudPlan.store.ts +++ b/packages/editor-ui/src/stores/cloudPlan.store.ts @@ -70,10 +70,31 @@ export const useCloudPlanStore = defineStore('cloudPlan', () => { return usage; }; + const usageLeft = computed(() => { + if (!state.data || !state.usage) return { workflowsLeft: -1, executionsLeft: -1 }; + + return { + workflowsLeft: state.data.activeWorkflowsLimit - state.usage.activeWorkflows, + executionsLeft: state.data.monthlyExecutionsLimit - state.usage.executions, + }; + }); + + const trialDaysLeft = computed(() => { + if (!state.data?.expirationDate) return -1; + + const differenceInMs = new Date().valueOf() - new Date(state.data.expirationDate).valueOf(); + + const differenceInDays = Math.floor(differenceInMs / (1000 * 60 * 60 * 24)); + + return Math.ceil(differenceInDays); + }); + return { state, getOwnerCurrentPLan, getInstanceCurrentUsage, + usageLeft, + trialDaysLeft, userIsTrialing, currentPlanData, currentUsageData, diff --git a/packages/editor-ui/src/stores/ui.store.ts b/packages/editor-ui/src/stores/ui.store.ts index 7fe1177b6940a..45337ab88fbc6 100644 --- a/packages/editor-ui/src/stores/ui.store.ts +++ b/packages/editor-ui/src/stores/ui.store.ts @@ -49,8 +49,10 @@ import { getCurlToJson } from '@/api/curlHelper'; import { useWorkflowsStore } from './workflows.store'; import { useSettingsStore } from './settings.store'; import { useUsageStore } from './usage.store'; +import { useCloudPlanStore } from './cloudPlan.store'; import type { BaseTextKey } from '@/plugins/i18n'; import { i18n as locale } from '@/plugins/i18n'; +import { useTelemetryStore } from '@/stores/telemetry.store'; export const useUIStore = defineStore(STORES.UI, { state: (): UIState => ({ @@ -479,8 +481,22 @@ export const useUIStore = defineStore(STORES.UI, { const rootStore = useRootStore(); return getCurlToJson(rootStore.getRestApiContext, curlCommand); }, - goToUpgrade(source: string, utm_campaign: string): void { - window.open(this.upgradeLinkUrl(source, utm_campaign), '_blank'); + goToUpgrade(source: string, utm_campaign: string, mode: 'open' | 'redirect' = 'open'): void { + const { usageLeft, trialDaysLeft, userIsTrialing } = useCloudPlanStore(); + const { executionsLeft, workflowsLeft } = usageLeft; + useTelemetryStore().track('User clicked upgrade CTA', { + source, + isTrial: userIsTrialing, + deploymentType: useSettingsStore().deploymentType, + trialDaysLeft, + executionsLeft, + workflowsLeft, + }); + if (mode === 'open') { + window.open(this.upgradeLinkUrl(source, utm_campaign), '_blank'); + } else { + location.href = this.upgradeLinkUrl(source, utm_campaign); + } }, }, }); diff --git a/packages/editor-ui/src/views/SettingsApiView.vue b/packages/editor-ui/src/views/SettingsApiView.vue index 0e8b2acd7e939..ff78c47e3c782 100644 --- a/packages/editor-ui/src/views/SettingsApiView.vue +++ b/packages/editor-ui/src/views/SettingsApiView.vue @@ -90,10 +90,10 @@ import CopyInput from '@/components/CopyInput.vue'; import { mapStores } from 'pinia'; import { useSettingsStore } from '@/stores/settings.store'; import { useRootStore } from '@/stores/n8nRoot.store'; +import { useUIStore } from '@/stores/ui.store'; import { useUsersStore } from '@/stores/users.store'; +import { useCloudPlanStore } from '@/stores/cloudPlan.store'; import { DOCS_DOMAIN, MODAL_CONFIRM } from '@/constants'; -import { useCloudPlanStore } from '@/stores'; -import { CLOUD_CHANGE_PLAN_PAGE } from '@/constants'; export default defineComponent({ name: 'SettingsApiView', @@ -104,6 +104,7 @@ export default defineComponent({ return { ...useToast(), ...useMessage(), + ...useUIStore(), }; }, data() { @@ -126,7 +127,7 @@ export default defineComponent({ : `https://${DOCS_DOMAIN}/api/api-reference/`; }, computed: { - ...mapStores(useRootStore, useSettingsStore, useUsersStore, useCloudPlanStore), + ...mapStores(useRootStore, useSettingsStore, useUsersStore, useCloudPlanStore, useUIStore), currentUser(): IUser | null { return this.usersStore.currentUser; }, @@ -139,7 +140,7 @@ export default defineComponent({ }, methods: { onUpgrade() { - location.href = CLOUD_CHANGE_PLAN_PAGE; + this.uiStore.goToUpgrade('settings-n8n-api', 'upgrade-api', 'redirect'); }, async showDeleteModal() { const confirmed = await this.confirm( diff --git a/packages/editor-ui/src/views/SettingsUsersView.vue b/packages/editor-ui/src/views/SettingsUsersView.vue index 0bc2c6b1a0dc6..ba2853dce4804 100644 --- a/packages/editor-ui/src/views/SettingsUsersView.vue +++ b/packages/editor-ui/src/views/SettingsUsersView.vue @@ -153,7 +153,7 @@ export default defineComponent({ } }, goToUpgrade() { - this.uiStore.goToUpgrade('users', 'upgrade-users'); + this.uiStore.goToUpgrade('settings-users', 'upgrade-users'); }, }, }); diff --git a/packages/editor-ui/src/views/VariablesView.vue b/packages/editor-ui/src/views/VariablesView.vue index da02446222465..48cbb8e37f2d8 100644 --- a/packages/editor-ui/src/views/VariablesView.vue +++ b/packages/editor-ui/src/views/VariablesView.vue @@ -201,7 +201,7 @@ async function deleteVariable(data: EnvironmentVariable) { } function goToUpgrade() { - window.open(upgradeLinkUrl.value, '_blank'); + uiStore.goToUpgrade('variables', 'upgrade-variables'); } function displayName(resource: EnvironmentVariable) {