mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-25 04:34:06 -08:00
fix(editor): Usage and plans page on Desktop (#5045)
This commit is contained in:
parent
16bd7610fc
commit
26e2321a71
|
@ -1148,6 +1148,8 @@
|
||||||
"settings.usageAndPlan.license.activation.error.title": "Activation failed",
|
"settings.usageAndPlan.license.activation.error.title": "Activation failed",
|
||||||
"settings.usageAndPlan.license.activation.success.title": "License activated",
|
"settings.usageAndPlan.license.activation.success.title": "License activated",
|
||||||
"settings.usageAndPlan.license.activation.success.message": "Your {name} {type} has been successfully activated.",
|
"settings.usageAndPlan.license.activation.success.message": "Your {name} {type} has been successfully activated.",
|
||||||
|
"settings.usageAndPlan.desktop.title": "Upgrade to n8n Cloud for the full experience",
|
||||||
|
"settings.usageAndPlan.desktop.description": "Cloud plans allow you to collaborate with teammates. Plus you don’t need to leave this app open all the time for your workflows to run.",
|
||||||
"showMessage.cancel": "@:_reusableBaseText.cancel",
|
"showMessage.cancel": "@:_reusableBaseText.cancel",
|
||||||
"showMessage.ok": "OK",
|
"showMessage.ok": "OK",
|
||||||
"showMessage.showDetails": "Show Details",
|
"showMessage.showDetails": "Show Details",
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { useUsersStore } from '@/stores/users';
|
||||||
|
|
||||||
export type UsageTelemetry = {
|
export type UsageTelemetry = {
|
||||||
instance_id: string;
|
instance_id: string;
|
||||||
action: 'view_plans' | 'manage_plan' | 'add_activation_key';
|
action: 'view_plans' | 'manage_plan' | 'add_activation_key' | 'desktop_view_plans';
|
||||||
plan_name_current: string;
|
plan_name_current: string;
|
||||||
usage: number;
|
usage: number;
|
||||||
quota: number;
|
quota: number;
|
||||||
|
@ -120,5 +120,6 @@ export const useUsageStore = defineStore('usage', () => {
|
||||||
usage: executionCount.value,
|
usage: executionCount.value,
|
||||||
quota: executionLimit.value,
|
quota: executionLimit.value,
|
||||||
})),
|
})),
|
||||||
|
isDesktop: computed(() => settingsStore.isDesktopDeployment),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -53,35 +53,37 @@ const onLicenseActivation = async () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
usageStore.setLoading(true);
|
if (!usageStore.isDesktop) {
|
||||||
if (route.query.key) {
|
usageStore.setLoading(true);
|
||||||
|
if (route.query.key) {
|
||||||
|
try {
|
||||||
|
await usageStore.activateLicense(route.query.key as string);
|
||||||
|
await router.replace({ query: {} });
|
||||||
|
showActivationSuccess();
|
||||||
|
usageStore.setLoading(false);
|
||||||
|
return;
|
||||||
|
} catch (error) {
|
||||||
|
showActivationError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
await usageStore.activateLicense(route.query.key as string);
|
if (!route.query.key && usageStore.canUserActivateLicense) {
|
||||||
await router.replace({ query: {} });
|
await usageStore.refreshLicenseManagementToken();
|
||||||
showActivationSuccess();
|
} else {
|
||||||
|
await usageStore.getLicenseInfo();
|
||||||
|
}
|
||||||
usageStore.setLoading(false);
|
usageStore.setLoading(false);
|
||||||
return;
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
showActivationError(error);
|
if (!error.name) {
|
||||||
|
error.name = locale.baseText('settings.usageAndPlan.error');
|
||||||
|
}
|
||||||
|
Notification.error({
|
||||||
|
title: error.name,
|
||||||
|
message: error.message,
|
||||||
|
position: 'bottom-right',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try {
|
|
||||||
if (!route.query.key && usageStore.canUserActivateLicense) {
|
|
||||||
await usageStore.refreshLicenseManagementToken();
|
|
||||||
} else {
|
|
||||||
await usageStore.getLicenseInfo();
|
|
||||||
}
|
|
||||||
usageStore.setLoading(false);
|
|
||||||
} catch (error) {
|
|
||||||
if (!error.name) {
|
|
||||||
error.name = locale.baseText('settings.usageAndPlan.error');
|
|
||||||
}
|
|
||||||
Notification.error({
|
|
||||||
title: error.name,
|
|
||||||
message: error.message,
|
|
||||||
position: 'bottom-right',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const sendUsageTelemetry = (action: UsageTelemetry['action']) => {
|
const sendUsageTelemetry = (action: UsageTelemetry['action']) => {
|
||||||
|
@ -110,99 +112,122 @@ const onDialogClosed = () => {
|
||||||
const onDialogOpened = () => {
|
const onDialogOpened = () => {
|
||||||
activationKeyInput.value?.focus();
|
activationKeyInput.value?.focus();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const openPricingPage = () => {
|
||||||
|
sendUsageTelemetry('desktop_view_plans');
|
||||||
|
window.open('https://n8n.io/pricing', '_blank');
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div v-if="!usageStore.isLoading">
|
<div>
|
||||||
<n8n-heading size="2xlarge">{{ locale.baseText('settings.usageAndPlan.title') }}</n8n-heading>
|
<n8n-heading size="2xlarge">{{ locale.baseText('settings.usageAndPlan.title') }}</n8n-heading>
|
||||||
<n8n-heading :class="$style.title" size="large">
|
<n8n-action-box
|
||||||
<i18n path="settings.usageAndPlan.description">
|
v-if="usageStore.isDesktop"
|
||||||
<template #name>{{ usageStore.planName }}</template>
|
:class="$style.actionBox"
|
||||||
<template #type>
|
:heading="locale.baseText('settings.usageAndPlan.desktop.title')"
|
||||||
<span v-if="usageStore.planId">{{ locale.baseText('settings.usageAndPlan.plan') }}</span>
|
:description="locale.baseText('settings.usageAndPlan.desktop.description')"
|
||||||
<span v-else>{{ locale.baseText('settings.usageAndPlan.edition') }}</span>
|
:buttonText="locale.baseText('settings.usageAndPlan.button.plans')"
|
||||||
</template>
|
@click="openPricingPage"
|
||||||
</i18n>
|
/>
|
||||||
</n8n-heading>
|
<div v-if="!usageStore.isDesktop && !usageStore.isLoading">
|
||||||
|
<n8n-heading :class="$style.title" size="large">
|
||||||
<div :class="$style.quota">
|
<i18n path="settings.usageAndPlan.description">
|
||||||
<n8n-text size="medium" color="text-light">
|
<template #name>{{ usageStore.planName }}</template>
|
||||||
{{ locale.baseText('settings.usageAndPlan.activeWorkflows') }}
|
<template #type>
|
||||||
</n8n-text>
|
<span v-if="usageStore.planId">{{
|
||||||
<div :class="$style.chart">
|
locale.baseText('settings.usageAndPlan.plan')
|
||||||
<span v-if="usageStore.executionLimit > 0" :class="$style.chartLine">
|
|
||||||
<span
|
|
||||||
:class="$style.chartBar"
|
|
||||||
:style="{ width: `${usageStore.executionPercentage}%` }"
|
|
||||||
></span>
|
|
||||||
</span>
|
|
||||||
<i18n :class="$style.count" path="settings.usageAndPlan.activeWorkflows.count">
|
|
||||||
<template #count>{{ usageStore.executionCount }}</template>
|
|
||||||
<template #limit>
|
|
||||||
<span v-if="usageStore.executionLimit < 0">{{
|
|
||||||
locale.baseText('settings.usageAndPlan.activeWorkflows.unlimited')
|
|
||||||
}}</span>
|
}}</span>
|
||||||
<span v-else>{{ usageStore.executionLimit }}</span>
|
<span v-else>{{ locale.baseText('settings.usageAndPlan.edition') }}</span>
|
||||||
</template>
|
</template>
|
||||||
</i18n>
|
</i18n>
|
||||||
|
</n8n-heading>
|
||||||
|
|
||||||
|
<div :class="$style.quota">
|
||||||
|
<n8n-text size="medium" color="text-light">
|
||||||
|
{{ locale.baseText('settings.usageAndPlan.activeWorkflows') }}
|
||||||
|
</n8n-text>
|
||||||
|
<div :class="$style.chart">
|
||||||
|
<span v-if="usageStore.executionLimit > 0" :class="$style.chartLine">
|
||||||
|
<span
|
||||||
|
:class="$style.chartBar"
|
||||||
|
:style="{ width: `${usageStore.executionPercentage}%` }"
|
||||||
|
></span>
|
||||||
|
</span>
|
||||||
|
<i18n :class="$style.count" path="settings.usageAndPlan.activeWorkflows.count">
|
||||||
|
<template #count>{{ usageStore.executionCount }}</template>
|
||||||
|
<template #limit>
|
||||||
|
<span v-if="usageStore.executionLimit < 0">{{
|
||||||
|
locale.baseText('settings.usageAndPlan.activeWorkflows.unlimited')
|
||||||
|
}}</span>
|
||||||
|
<span v-else>{{ usageStore.executionLimit }}</span>
|
||||||
|
</template>
|
||||||
|
</i18n>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<n8n-info-tip>{{ locale.baseText('settings.usageAndPlan.activeWorkflows.hint') }}</n8n-info-tip>
|
<n8n-info-tip>{{
|
||||||
|
locale.baseText('settings.usageAndPlan.activeWorkflows.hint')
|
||||||
|
}}</n8n-info-tip>
|
||||||
|
|
||||||
<div :class="$style.buttons">
|
<div :class="$style.buttons">
|
||||||
<n8n-button
|
<n8n-button
|
||||||
:class="$style.buttonTertiary"
|
:class="$style.buttonTertiary"
|
||||||
@click="onAddActivationKey"
|
@click="onAddActivationKey"
|
||||||
v-if="usageStore.canUserActivateLicense"
|
v-if="usageStore.canUserActivateLicense"
|
||||||
type="tertiary"
|
type="tertiary"
|
||||||
size="large"
|
size="large"
|
||||||
|
>
|
||||||
|
<strong>{{ locale.baseText('settings.usageAndPlan.button.activation') }}</strong>
|
||||||
|
</n8n-button>
|
||||||
|
<n8n-button v-if="usageStore.managementToken" @click="onManagePlan" size="large">
|
||||||
|
<a :href="managePlanUrl" target="_blank">{{
|
||||||
|
locale.baseText('settings.usageAndPlan.button.manage')
|
||||||
|
}}</a>
|
||||||
|
</n8n-button>
|
||||||
|
<n8n-button v-else @click="onViewPlans" size="large">
|
||||||
|
<a :href="viewPlansUrl" target="_blank">{{
|
||||||
|
locale.baseText('settings.usageAndPlan.button.plans')
|
||||||
|
}}</a>
|
||||||
|
</n8n-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-dialog
|
||||||
|
width="480px"
|
||||||
|
top="0"
|
||||||
|
@closed="onDialogClosed"
|
||||||
|
@opened="onDialogOpened"
|
||||||
|
:visible.sync="activationKeyModal"
|
||||||
|
:title="locale.baseText('settings.usageAndPlan.dialog.activation.title')"
|
||||||
>
|
>
|
||||||
<strong>{{ locale.baseText('settings.usageAndPlan.button.activation') }}</strong>
|
<template #default>
|
||||||
</n8n-button>
|
<n8n-input
|
||||||
<n8n-button v-if="usageStore.managementToken" @click="onManagePlan" size="large">
|
ref="activationKeyInput"
|
||||||
<a :href="managePlanUrl" target="_blank">{{
|
v-model="activationKey"
|
||||||
locale.baseText('settings.usageAndPlan.button.manage')
|
size="medium"
|
||||||
}}</a>
|
:placeholder="locale.baseText('settings.usageAndPlan.dialog.activation.label')"
|
||||||
</n8n-button>
|
/>
|
||||||
<n8n-button v-else @click="onViewPlans" size="large">
|
</template>
|
||||||
<a :href="viewPlansUrl" target="_blank">{{
|
<template #footer>
|
||||||
locale.baseText('settings.usageAndPlan.button.plans')
|
<n8n-button @click="activationKeyModal = false" size="medium" type="secondary">
|
||||||
}}</a>
|
{{ locale.baseText('settings.usageAndPlan.dialog.activation.cancel') }}
|
||||||
</n8n-button>
|
</n8n-button>
|
||||||
|
<n8n-button @click="onLicenseActivation" size="medium">
|
||||||
|
{{ locale.baseText('settings.usageAndPlan.dialog.activation.activate') }}
|
||||||
|
</n8n-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-dialog
|
|
||||||
width="480px"
|
|
||||||
top="0"
|
|
||||||
@closed="onDialogClosed"
|
|
||||||
@opened="onDialogOpened"
|
|
||||||
:visible.sync="activationKeyModal"
|
|
||||||
:title="locale.baseText('settings.usageAndPlan.dialog.activation.title')"
|
|
||||||
>
|
|
||||||
<template #default>
|
|
||||||
<n8n-input
|
|
||||||
ref="activationKeyInput"
|
|
||||||
v-model="activationKey"
|
|
||||||
size="medium"
|
|
||||||
:placeholder="locale.baseText('settings.usageAndPlan.dialog.activation.label')"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<template #footer>
|
|
||||||
<n8n-button @click="activationKeyModal = false" size="medium" type="secondary">
|
|
||||||
{{ locale.baseText('settings.usageAndPlan.dialog.activation.cancel') }}
|
|
||||||
</n8n-button>
|
|
||||||
<n8n-button @click="onLicenseActivation" size="medium">
|
|
||||||
{{ locale.baseText('settings.usageAndPlan.dialog.activation.activate') }}
|
|
||||||
</n8n-button>
|
|
||||||
</template>
|
|
||||||
</el-dialog>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
@import '@/styles/css-animation-helpers.scss';
|
@import '@/styles/css-animation-helpers.scss';
|
||||||
|
|
||||||
|
.actionBox {
|
||||||
|
margin: var(--spacing-2xl) 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
.spacedFlex {
|
.spacedFlex {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
|
Loading…
Reference in a new issue