feat(editor): Add cloud ExecutionsUsage and API blocking using licenses (#6159)
* Add ExecutionsUsage component * set $sidebar-expanded-width back to 200px * add days using interpolation * Rename PlanData type to CloudPlanData * Rename Metadata type to PlanMetadata * Make prop block in the update button * Use variable in line-height * Remove progressBarSection class * fix trial expiration calculation * mock expirationDate and fix issue with days left * Remove unnecesary property from class .container * inject component data via props * Check for plan data during app mounting and keep data in the store * Remove mounted hook * redirect when upgrade plan is clicked * Remove computed properties * Remove instance property as it's not needed anymore * Flatten plan object * remove console.log * Add all cloud types within its own namespace * keep redirection inside component * get computed properties back * Improve polling logic * Move cloudData to its own store * Remove commented interfaces * remove cloudPlan from user store * fix imports * update logic for userIsTrialing method * centralize userIsTrialing method * redirect to production change plan page always * Call staging or production cloud api depending on base URL * remove setting store form ExecutionUsage.vue * fix linting issue * Add trial group to PlanMetadata group * Move helpers into the store * make staging url check more specific * make cloud state nullable * fix linting issue * swap mockup date for endpoint * Make getCurrentPlan async * asas * Improvements * small improvements * chore: resolve conflicts * make sure there is data before calculating trial expiration * Fix issue with component not loading on first page load * type safety improvements * apply component ui feedback * fix linting issue * chore: clean up unnecessary change from merge conflict * feat: Block api feature using licenses, show notice page for trial cloud users (#6187) * rename planSpec to plan * Remove instance property as it's not needed anymore * Flatten plan object * remove console.log * feat: disable api using license * feat: add api page * chore: resolve conflicts * chore: resolve conflicts * feat: update and refactor a bit * fix: update endpoints * fix: update endpoints * fix: use host * feat: update copy * fix linting issues --------- Co-authored-by: ricardo <ricardoespinoza105@gmail.com> * add pluralization to days left text --------- Co-authored-by: Mutasem <mutdmour@gmail.com> Co-authored-by: Mutasem Aldmour <4711238+mutdmour@users.noreply.github.com>
This commit is contained in:
203
packages/editor-ui/src/components/ExecutionsUsage.vue
Normal file
203
packages/editor-ui/src/components/ExecutionsUsage.vue
Normal file
@@ -0,0 +1,203 @@
|
||||
<template>
|
||||
<div :class="$style.container">
|
||||
<div v-if="isTrialExpired" :class="$style.usageText">
|
||||
<n8n-text size="xsmall" color="danger">
|
||||
{{ locale.baseText('executionUsage.expired.text') }}
|
||||
</n8n-text>
|
||||
</div>
|
||||
<div v-else-if="!isTrialExpired && trialHasExecutionsLeft" :class="$style.usageText">
|
||||
<i18n path="executionUsage.currentUsage">
|
||||
<template #text>
|
||||
<n8n-text size="small" color="text-dark">
|
||||
{{ locale.baseText('executionUsage.currentUsage.text') }}
|
||||
</n8n-text>
|
||||
</template>
|
||||
<template #count>
|
||||
<n8n-text size="small" :bold="true" color="warning">
|
||||
{{
|
||||
locale.baseText('executionUsage.currentUsage.count', {
|
||||
adjustToNumber: daysLeftOnTrial,
|
||||
})
|
||||
}}
|
||||
</n8n-text>
|
||||
</template>
|
||||
</i18n>
|
||||
</div>
|
||||
<div v-else-if="!trialHasExecutionsLeft" :class="$style.usageText">
|
||||
<n8n-text size="xsmall">
|
||||
{{ locale.baseText('executionUsage.ranOutOfExecutions.text') }}
|
||||
</n8n-text>
|
||||
</div>
|
||||
<div v-if="!isTrialExpired" :class="$style.usageCounter">
|
||||
<div :class="$style.progressBarDiv">
|
||||
<progress
|
||||
:class="[
|
||||
trialHasExecutionsLeft ? $style.progressBarSuccess : $style.progressBarDanger,
|
||||
$style.progressBar,
|
||||
]"
|
||||
:value="currentExecutionsWithThreshold"
|
||||
:max="maxExecutions"
|
||||
></progress>
|
||||
</div>
|
||||
<div :class="$style.executionsCountSection">
|
||||
<n8n-text size="xsmall" :color="trialHasExecutionsLeft ? 'text-dark' : 'danger'">
|
||||
{{ currentExecutions }}/{{ maxExecutions }}
|
||||
</n8n-text>
|
||||
<n8n-text size="xsmall" :color="trialHasExecutionsLeft ? 'text-dark' : 'danger'">{{
|
||||
locale.baseText('executionUsage.label.executions')
|
||||
}}</n8n-text>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div :class="$style.upgradeButtonSection">
|
||||
<n8n-button
|
||||
:label="locale.baseText('executionUsage.button.upgrade')"
|
||||
size="xmini"
|
||||
icon="gem"
|
||||
type="success"
|
||||
:block="true"
|
||||
@click="onUpgradeClicked"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
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';
|
||||
|
||||
const PROGRESS_BAR_MINIMUM_THRESHOLD = 8;
|
||||
|
||||
const props = defineProps<{ cloudPlanData: CloudPlanAndUsageData | null }>();
|
||||
|
||||
const now = DateTime.utc();
|
||||
|
||||
const daysLeftOnTrial = computed(() => {
|
||||
const { days = 0 } = getPlanExpirationDate().diff(now, ['days']).toObject();
|
||||
return Math.ceil(days);
|
||||
});
|
||||
|
||||
const isTrialExpired = computed(() => {
|
||||
if (!props.cloudPlanData?.expirationDate) return false;
|
||||
const trialEndsAt = DateTime.fromISO(props.cloudPlanData.expirationDate);
|
||||
return now.toMillis() > trialEndsAt.toMillis();
|
||||
});
|
||||
|
||||
const getPlanExpirationDate = () => DateTime.fromISO(props?.cloudPlanData?.expirationDate ?? '');
|
||||
|
||||
const trialHasExecutionsLeft = computed(() => {
|
||||
if (!props.cloudPlanData?.usage) return 0;
|
||||
return props.cloudPlanData.usage.executions < props.cloudPlanData.monthlyExecutionsLimit;
|
||||
});
|
||||
|
||||
const currentExecutions = computed(() => {
|
||||
if (!props.cloudPlanData?.usage) return 0;
|
||||
const usedExecutions = props.cloudPlanData.usage.executions;
|
||||
const executionsQuota = props.cloudPlanData.monthlyExecutionsLimit;
|
||||
return usedExecutions > executionsQuota ? executionsQuota : usedExecutions;
|
||||
});
|
||||
|
||||
const currentExecutionsWithThreshold = computed(() => {
|
||||
if (!props.cloudPlanData?.usage) return 0;
|
||||
const usedExecutions = props.cloudPlanData.usage.executions;
|
||||
const executionsQuota = props.cloudPlanData.monthlyExecutionsLimit;
|
||||
const threshold = (PROGRESS_BAR_MINIMUM_THRESHOLD * executionsQuota) / 100;
|
||||
return usedExecutions < threshold ? threshold : usedExecutions;
|
||||
});
|
||||
|
||||
const maxExecutions = computed(() => {
|
||||
if (!props.cloudPlanData?.monthlyExecutionsLimit) return 0;
|
||||
return props.cloudPlanData.monthlyExecutionsLimit;
|
||||
});
|
||||
|
||||
const onUpgradeClicked = () => {
|
||||
location.href = CLOUD_CHANGE_PLAN_PAGE;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style module lang="scss">
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: var(--color-background-light);
|
||||
border: var(--border-base);
|
||||
border-right: 0;
|
||||
}
|
||||
|
||||
.progressBarDiv {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.progressBar {
|
||||
width: 62.4px;
|
||||
border: 0;
|
||||
height: 5px;
|
||||
border-radius: 20px;
|
||||
background-color: var(--color-foreground-base);
|
||||
}
|
||||
.progressBar::-webkit-progress-bar {
|
||||
width: 62.4px;
|
||||
border: 0;
|
||||
height: 5px;
|
||||
border-radius: 20px;
|
||||
background-color: var(--color-foreground-base);
|
||||
}
|
||||
.progressBar::-moz-progress-bar {
|
||||
width: 62.4px;
|
||||
border: 0;
|
||||
height: 5px;
|
||||
border-radius: 20px;
|
||||
background-color: var(--color-foreground-base);
|
||||
}
|
||||
|
||||
.progressBarSuccess::-moz-progress-bar {
|
||||
background: var(--color-foreground-xdark);
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.progressBarSuccess::-webkit-progress-value {
|
||||
background: var(--color-foreground-xdark);
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.progressBarDanger::-webkit-progress-value {
|
||||
background: var(--color-danger);
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.progressBarDanger::-moz-progress-bar {
|
||||
background: var(--color-danger);
|
||||
}
|
||||
|
||||
.usageText {
|
||||
margin-left: var(--spacing-s);
|
||||
margin-right: var(--spacing-s);
|
||||
margin-top: var(--spacing-xs);
|
||||
line-height: var(--spacing-xs);
|
||||
}
|
||||
|
||||
.usageCounter {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-top: var(--spacing-2xs);
|
||||
font-size: var(--font-size-3xs);
|
||||
}
|
||||
|
||||
.danger {
|
||||
color: var(--color-danger);
|
||||
}
|
||||
|
||||
.executionsCountSection {
|
||||
margin-left: var(--spacing-xs);
|
||||
}
|
||||
|
||||
.upgradeButtonSection {
|
||||
margin: var(--spacing-s);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user