mirror of
https://github.com/n8n-io/n8n.git
synced 2025-01-03 17:07:29 -08:00
fix(editor): Update concurrency UI considering different types of instances (#12068)
This commit is contained in:
parent
127d864bbb
commit
fa572bbca4
|
@ -46,13 +46,12 @@ describe('ConcurrentExecutionsHeader', () => {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
it('should show tooltip on hover and call "goToUpgrade" on click', async () => {
|
it('should show tooltip on hover with Upgrade link and emit "goToUpgrade" on click when on cloud', async () => {
|
||||||
const windowOpenSpy = vi.spyOn(window, 'open').mockImplementation(() => null);
|
const { container, getByText, getByRole, queryByRole, emitted } = renderComponent({
|
||||||
|
|
||||||
const { container, getByText, getByRole, queryByRole } = renderComponent({
|
|
||||||
props: {
|
props: {
|
||||||
runningExecutionsCount: 2,
|
runningExecutionsCount: 2,
|
||||||
concurrencyCap: 5,
|
concurrencyCap: 5,
|
||||||
|
isCloudDeployment: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -68,6 +67,25 @@ describe('ConcurrentExecutionsHeader', () => {
|
||||||
|
|
||||||
await userEvent.click(getByText('Upgrade now'));
|
await userEvent.click(getByText('Upgrade now'));
|
||||||
|
|
||||||
expect(windowOpenSpy).toHaveBeenCalled();
|
expect(emitted().goToUpgrade).toHaveLength(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show tooltip on hover with Viev docs link when self-hosted', async () => {
|
||||||
|
const { container, getByText, getByRole, queryByRole } = renderComponent({
|
||||||
|
props: {
|
||||||
|
runningExecutionsCount: 2,
|
||||||
|
concurrencyCap: 5,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const tooltipTrigger = container.querySelector('svg') as SVGSVGElement;
|
||||||
|
|
||||||
|
expect(tooltipTrigger).toBeVisible();
|
||||||
|
expect(queryByRole('tooltip')).not.toBeInTheDocument();
|
||||||
|
|
||||||
|
await userEvent.hover(tooltipTrigger);
|
||||||
|
|
||||||
|
expect(getByRole('tooltip')).toBeVisible();
|
||||||
|
expect(getByText('View docs')).toBeVisible();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,15 +1,18 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, defineProps } from 'vue';
|
import { computed } from 'vue';
|
||||||
import { useI18n } from '@/composables/useI18n';
|
import { useI18n } from '@/composables/useI18n';
|
||||||
import { usePageRedirectionHelper } from '@/composables/usePageRedirectionHelper';
|
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
runningExecutionsCount: number;
|
runningExecutionsCount: number;
|
||||||
concurrencyCap: number;
|
concurrencyCap: number;
|
||||||
|
isCloudDeployment?: boolean;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
goToUpgrade: [];
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const i18n = useI18n();
|
const i18n = useI18n();
|
||||||
const pageRedirectionHelper = usePageRedirectionHelper();
|
|
||||||
|
|
||||||
const tooltipText = computed(() =>
|
const tooltipText = computed(() =>
|
||||||
i18n.baseText('executionsList.activeExecutions.tooltip', {
|
i18n.baseText('executionsList.activeExecutions.tooltip', {
|
||||||
|
@ -31,10 +34,6 @@ const headerText = computed(() => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const goToUpgrade = () => {
|
|
||||||
void pageRedirectionHelper.goToUpgrade('concurrency', 'upgrade-concurrency');
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -43,9 +42,22 @@ const goToUpgrade = () => {
|
||||||
<template #content>
|
<template #content>
|
||||||
<div :class="$style.tooltip">
|
<div :class="$style.tooltip">
|
||||||
{{ tooltipText }}
|
{{ tooltipText }}
|
||||||
<n8n-link bold size="small" :class="$style.upgrade" @click="goToUpgrade">
|
<N8nLink
|
||||||
|
v-if="props.isCloudDeployment"
|
||||||
|
bold
|
||||||
|
size="small"
|
||||||
|
:class="$style.link"
|
||||||
|
@click="emit('goToUpgrade')"
|
||||||
|
>
|
||||||
{{ i18n.baseText('generic.upgradeNow') }}
|
{{ i18n.baseText('generic.upgradeNow') }}
|
||||||
</n8n-link>
|
</N8nLink>
|
||||||
|
<N8nLink
|
||||||
|
v-else
|
||||||
|
:class="$style.link"
|
||||||
|
:href="i18n.baseText('executions.concurrency.docsLink')"
|
||||||
|
target="_blank"
|
||||||
|
>{{ i18n.baseText('generic.viewDocs') }}</N8nLink
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<font-awesome-icon icon="info-circle" class="mr-2xs" />
|
<font-awesome-icon icon="info-circle" class="mr-2xs" />
|
||||||
|
@ -54,12 +66,12 @@ const goToUpgrade = () => {
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style module scoped>
|
<style lang="scss" module>
|
||||||
.tooltip {
|
.tooltip {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
.upgrade {
|
.link {
|
||||||
margin-top: var(--spacing-xs);
|
margin-top: var(--spacing-xs);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -16,6 +16,7 @@ import { getResourcePermissions } from '@/permissions';
|
||||||
import { useSettingsStore } from '@/stores/settings.store';
|
import { useSettingsStore } from '@/stores/settings.store';
|
||||||
import ProjectHeader from '@/components/Projects/ProjectHeader.vue';
|
import ProjectHeader from '@/components/Projects/ProjectHeader.vue';
|
||||||
import ConcurrentExecutionsHeader from '@/components/executions/ConcurrentExecutionsHeader.vue';
|
import ConcurrentExecutionsHeader from '@/components/executions/ConcurrentExecutionsHeader.vue';
|
||||||
|
import { usePageRedirectionHelper } from '@/composables/usePageRedirectionHelper';
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
|
@ -40,6 +41,7 @@ const telemetry = useTelemetry();
|
||||||
const workflowsStore = useWorkflowsStore();
|
const workflowsStore = useWorkflowsStore();
|
||||||
const executionsStore = useExecutionsStore();
|
const executionsStore = useExecutionsStore();
|
||||||
const settingsStore = useSettingsStore();
|
const settingsStore = useSettingsStore();
|
||||||
|
const pageRedirectionHelper = usePageRedirectionHelper();
|
||||||
|
|
||||||
const isMounted = ref(false);
|
const isMounted = ref(false);
|
||||||
const allVisibleSelected = ref(false);
|
const allVisibleSelected = ref(false);
|
||||||
|
@ -317,6 +319,10 @@ async function onAutoRefreshToggle(value: boolean) {
|
||||||
executionsStore.stopAutoRefreshInterval();
|
executionsStore.stopAutoRefreshInterval();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const goToUpgrade = () => {
|
||||||
|
void pageRedirectionHelper.goToUpgrade('concurrency', 'upgrade-concurrency');
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -330,6 +336,8 @@ async function onAutoRefreshToggle(value: boolean) {
|
||||||
class="mr-xl"
|
class="mr-xl"
|
||||||
:running-executions-count="runningExecutionsCount"
|
:running-executions-count="runningExecutionsCount"
|
||||||
:concurrency-cap="settingsStore.concurrency"
|
:concurrency-cap="settingsStore.concurrency"
|
||||||
|
:is-cloud-deployment="settingsStore.isCloudDeployment"
|
||||||
|
@go-to-upgrade="goToUpgrade"
|
||||||
/>
|
/>
|
||||||
<N8nLoading v-if="!isMounted" :class="$style.filterLoader" variant="custom" />
|
<N8nLoading v-if="!isMounted" :class="$style.filterLoader" variant="custom" />
|
||||||
<ElCheckbox
|
<ElCheckbox
|
||||||
|
@ -400,12 +408,14 @@ async function onAutoRefreshToggle(value: boolean) {
|
||||||
:workflow-permissions="getExecutionWorkflowPermissions(execution)"
|
:workflow-permissions="getExecutionWorkflowPermissions(execution)"
|
||||||
:selected="selectedItems[execution.id] || allExistingSelected"
|
:selected="selectedItems[execution.id] || allExistingSelected"
|
||||||
:concurrency-cap="settingsStore.concurrency"
|
:concurrency-cap="settingsStore.concurrency"
|
||||||
|
:is-cloud-deployment="settingsStore.isCloudDeployment"
|
||||||
data-test-id="global-execution-list-item"
|
data-test-id="global-execution-list-item"
|
||||||
@stop="stopExecution"
|
@stop="stopExecution"
|
||||||
@delete="deleteExecution"
|
@delete="deleteExecution"
|
||||||
@select="toggleSelectExecution"
|
@select="toggleSelectExecution"
|
||||||
@retry-saved="retrySavedExecution"
|
@retry-saved="retrySavedExecution"
|
||||||
@retry-original="retryOriginalExecution"
|
@retry-original="retryOriginalExecution"
|
||||||
|
@go-to-upgrade="goToUpgrade"
|
||||||
/>
|
/>
|
||||||
</TransitionGroup>
|
</TransitionGroup>
|
||||||
</table>
|
</table>
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { describe, it, expect, vi } from 'vitest';
|
import { vi } from 'vitest';
|
||||||
import { fireEvent } from '@testing-library/vue';
|
import { fireEvent } from '@testing-library/vue';
|
||||||
import GlobalExecutionsListItem from './GlobalExecutionsListItem.vue';
|
import { WAIT_INDEFINITELY } from 'n8n-workflow';
|
||||||
|
import GlobalExecutionsListItem from '@/components/executions/global/GlobalExecutionsListItem.vue';
|
||||||
import { createComponentRenderer } from '@/__tests__/render';
|
import { createComponentRenderer } from '@/__tests__/render';
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
|
|
||||||
|
@ -15,9 +16,20 @@ vi.mock('vue-router', async () => {
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const globalExecutionsListItemQueuedTooltipRenderSpy = vi.fn();
|
||||||
|
|
||||||
const renderComponent = createComponentRenderer(GlobalExecutionsListItem, {
|
const renderComponent = createComponentRenderer(GlobalExecutionsListItem, {
|
||||||
global: {
|
global: {
|
||||||
stubs: ['font-awesome-icon', 'n8n-tooltip', 'n8n-button', 'i18n-t'],
|
//stubs: ['font-awesome-icon', 'n8n-tooltip', 'n8n-button', 'i18n-t'],
|
||||||
|
stubs: {
|
||||||
|
'font-awesome-icon': true,
|
||||||
|
'n8n-tooltip': true,
|
||||||
|
'n8n-button': true,
|
||||||
|
'i18n-t': true,
|
||||||
|
GlobalExecutionsListItemQueuedTooltip: {
|
||||||
|
render: globalExecutionsListItemQueuedTooltipRenderSpy,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -98,6 +110,7 @@ describe('GlobalExecutionsListItem', () => {
|
||||||
|
|
||||||
await fireEvent.click(getByText('TestWorkflow'));
|
await fireEvent.click(getByText('TestWorkflow'));
|
||||||
expect(window.open).toHaveBeenCalledWith('mockedRoute', '_blank');
|
expect(window.open).toHaveBeenCalledWith('mockedRoute', '_blank');
|
||||||
|
expect(globalExecutionsListItemQueuedTooltipRenderSpy).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should show formatted start date', () => {
|
it('should show formatted start date', () => {
|
||||||
|
@ -113,4 +126,50 @@ describe('GlobalExecutionsListItem', () => {
|
||||||
getByText(`1 Jan, 2022 at ${DateTime.fromJSDate(new Date(testDate)).toFormat('HH')}:00:00`),
|
getByText(`1 Jan, 2022 at ${DateTime.fromJSDate(new Date(testDate)).toFormat('HH')}:00:00`),
|
||||||
).toBeInTheDocument();
|
).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not render queued tooltip for a not indefinitely waiting execution', async () => {
|
||||||
|
renderComponent({
|
||||||
|
props: {
|
||||||
|
execution: {
|
||||||
|
status: 'waiting',
|
||||||
|
waitTill: new Date(Date.now() + 10000000).toISOString(),
|
||||||
|
id: 123,
|
||||||
|
workflowName: 'Test Workflow',
|
||||||
|
},
|
||||||
|
workflowPermissions: {},
|
||||||
|
concurrencyCap: 5,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(globalExecutionsListItemQueuedTooltipRenderSpy).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render queued tooltip for an indefinitely waiting execution', async () => {
|
||||||
|
renderComponent({
|
||||||
|
props: {
|
||||||
|
execution: {
|
||||||
|
status: 'waiting',
|
||||||
|
waitTill: WAIT_INDEFINITELY,
|
||||||
|
id: 123,
|
||||||
|
workflowName: 'Test Workflow',
|
||||||
|
},
|
||||||
|
workflowPermissions: {},
|
||||||
|
concurrencyCap: 5,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(globalExecutionsListItemQueuedTooltipRenderSpy).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render queued tooltip for a new execution', async () => {
|
||||||
|
renderComponent({
|
||||||
|
props: {
|
||||||
|
execution: { status: 'new', id: 123, workflowName: 'Test Workflow' },
|
||||||
|
workflowPermissions: {},
|
||||||
|
concurrencyCap: 5,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(globalExecutionsListItemQueuedTooltipRenderSpy).toHaveBeenCalled();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -8,6 +8,7 @@ import { i18n as locale } from '@/plugins/i18n';
|
||||||
import ExecutionsTime from '@/components/executions/ExecutionsTime.vue';
|
import ExecutionsTime from '@/components/executions/ExecutionsTime.vue';
|
||||||
import { useExecutionHelpers } from '@/composables/useExecutionHelpers';
|
import { useExecutionHelpers } from '@/composables/useExecutionHelpers';
|
||||||
import type { PermissionsRecord } from '@/permissions';
|
import type { PermissionsRecord } from '@/permissions';
|
||||||
|
import GlobalExecutionsListItemQueuedTooltip from '@/components/executions/global/GlobalExecutionsListItemQueuedTooltip.vue';
|
||||||
|
|
||||||
type Command = 'retrySaved' | 'retryOriginal' | 'delete';
|
type Command = 'retrySaved' | 'retryOriginal' | 'delete';
|
||||||
|
|
||||||
|
@ -17,6 +18,7 @@ const emit = defineEmits<{
|
||||||
retrySaved: [data: ExecutionSummary];
|
retrySaved: [data: ExecutionSummary];
|
||||||
retryOriginal: [data: ExecutionSummary];
|
retryOriginal: [data: ExecutionSummary];
|
||||||
delete: [data: ExecutionSummary];
|
delete: [data: ExecutionSummary];
|
||||||
|
goToUpgrade: [];
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
|
@ -26,6 +28,7 @@ const props = withDefaults(
|
||||||
workflowName?: string;
|
workflowName?: string;
|
||||||
workflowPermissions: PermissionsRecord['workflow'];
|
workflowPermissions: PermissionsRecord['workflow'];
|
||||||
concurrencyCap: number;
|
concurrencyCap: number;
|
||||||
|
isCloudDeployment?: boolean;
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
selected: false,
|
selected: false,
|
||||||
|
@ -84,19 +87,6 @@ const formattedStoppedAtDate = computed(() => {
|
||||||
: '';
|
: '';
|
||||||
});
|
});
|
||||||
|
|
||||||
const statusTooltipText = computed(() => {
|
|
||||||
if (isQueued.value) {
|
|
||||||
return i18n.baseText('executionsList.statusTooltipText.waitingForConcurrencyCapacity', {
|
|
||||||
interpolate: { concurrencyCap: props.concurrencyCap },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (props.execution.status === 'waiting' && isWaitTillIndefinite.value) {
|
|
||||||
return i18n.baseText('executionsList.statusTooltipText.theWorkflowIsWaitingIndefinitely');
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
});
|
|
||||||
|
|
||||||
const statusText = computed(() => {
|
const statusText = computed(() => {
|
||||||
switch (props.execution.status) {
|
switch (props.execution.status) {
|
||||||
case 'waiting':
|
case 'waiting':
|
||||||
|
@ -208,12 +198,15 @@ async function handleActionItemClick(commandData: Command) {
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</i18n-t>
|
</i18n-t>
|
||||||
<N8nTooltip v-else placement="top">
|
<GlobalExecutionsListItemQueuedTooltip
|
||||||
<template #content>
|
v-else
|
||||||
<span>{{ statusTooltipText }}</span>
|
:status="props.execution.status"
|
||||||
</template>
|
:concurrency-cap="props.concurrencyCap"
|
||||||
|
:is-cloud-deployment="props.isCloudDeployment"
|
||||||
|
@go-to-upgrade="emit('goToUpgrade')"
|
||||||
|
>
|
||||||
<span :class="$style.status">{{ statusText }}</span>
|
<span :class="$style.status">{{ statusText }}</span>
|
||||||
</N8nTooltip>
|
</GlobalExecutionsListItemQueuedTooltip>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
import userEvent from '@testing-library/user-event';
|
||||||
|
import { createComponentRenderer } from '@/__tests__/render';
|
||||||
|
import GlobalExecutionsListItemQueuedTooltip from '@/components/executions/global/GlobalExecutionsListItemQueuedTooltip.vue';
|
||||||
|
|
||||||
|
const renderComponent = createComponentRenderer(GlobalExecutionsListItemQueuedTooltip);
|
||||||
|
|
||||||
|
describe('GlobalExecutionsListItemQueuedTooltip', () => {
|
||||||
|
it('should not throw error when rendered', async () => {
|
||||||
|
expect(() =>
|
||||||
|
renderComponent({
|
||||||
|
props: {
|
||||||
|
status: 'waiting',
|
||||||
|
concurrencyCap: 0,
|
||||||
|
},
|
||||||
|
slots: {
|
||||||
|
default: 'Waiting',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show waiting indefinitely tooltip', async () => {
|
||||||
|
const { getByText } = renderComponent({
|
||||||
|
props: {
|
||||||
|
status: 'waiting',
|
||||||
|
concurrencyCap: 0,
|
||||||
|
},
|
||||||
|
slots: {
|
||||||
|
default: 'Waiting',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await userEvent.hover(getByText('Waiting'));
|
||||||
|
|
||||||
|
expect(getByText(/waiting indefinitely/)).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show queued tooltip for self-hosted', async () => {
|
||||||
|
const { getByText } = renderComponent({
|
||||||
|
props: {
|
||||||
|
status: 'new',
|
||||||
|
concurrencyCap: 0,
|
||||||
|
},
|
||||||
|
slots: {
|
||||||
|
default: 'Queued',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await userEvent.hover(getByText('Queued'));
|
||||||
|
|
||||||
|
expect(getByText(/instance is limited/)).toBeVisible();
|
||||||
|
expect(getByText('View docs')).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show queued tooltip for cloud', async () => {
|
||||||
|
const { getByText, emitted } = renderComponent({
|
||||||
|
props: {
|
||||||
|
status: 'new',
|
||||||
|
concurrencyCap: 0,
|
||||||
|
isCloudDeployment: true,
|
||||||
|
},
|
||||||
|
slots: {
|
||||||
|
default: 'Queued',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await userEvent.hover(getByText('Queued'));
|
||||||
|
|
||||||
|
expect(getByText(/plan is limited/)).toBeVisible();
|
||||||
|
expect(getByText('Upgrade now')).toBeVisible();
|
||||||
|
|
||||||
|
await userEvent.click(getByText('Upgrade now'));
|
||||||
|
|
||||||
|
expect(emitted().goToUpgrade).toHaveLength(1);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,67 @@
|
||||||
|
<script lang="ts" setup="">
|
||||||
|
import type { ExecutionStatus } from 'n8n-workflow';
|
||||||
|
import { useI18n } from '@/composables/useI18n';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
status: ExecutionStatus;
|
||||||
|
concurrencyCap: number;
|
||||||
|
isCloudDeployment?: boolean;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
goToUpgrade: [];
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const i18n = useI18n();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<N8nTooltip placement="top">
|
||||||
|
<template #content>
|
||||||
|
<i18n-t
|
||||||
|
v-if="props.status === 'waiting'"
|
||||||
|
keypath="executionsList.statusTooltipText.theWorkflowIsWaitingIndefinitely"
|
||||||
|
/>
|
||||||
|
<i18n-t
|
||||||
|
v-if="props.status === 'new'"
|
||||||
|
keypath="executionsList.statusTooltipText.waitingForConcurrencyCapacity"
|
||||||
|
>
|
||||||
|
<template #instance>
|
||||||
|
<i18n-t
|
||||||
|
v-if="props.isCloudDeployment"
|
||||||
|
keypath="executionsList.statusTooltipText.waitingForConcurrencyCapacity.cloud"
|
||||||
|
>
|
||||||
|
<template #concurrencyCap>{{ props.concurrencyCap }}</template>
|
||||||
|
<template #link>
|
||||||
|
<N8nLink bold size="small" :class="$style.link" @click="emit('goToUpgrade')">
|
||||||
|
{{ i18n.baseText('generic.upgradeNow') }}
|
||||||
|
</N8nLink>
|
||||||
|
</template>
|
||||||
|
</i18n-t>
|
||||||
|
<i18n-t
|
||||||
|
v-else
|
||||||
|
keypath="executionsList.statusTooltipText.waitingForConcurrencyCapacity.self"
|
||||||
|
>
|
||||||
|
<template #concurrencyCap>{{ props.concurrencyCap }}</template>
|
||||||
|
<template #link>
|
||||||
|
<N8nLink
|
||||||
|
:class="$style.link"
|
||||||
|
:href="i18n.baseText('executions.concurrency.docsLink')"
|
||||||
|
target="_blank"
|
||||||
|
>{{ i18n.baseText('generic.viewDocs') }}</N8nLink
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
</i18n-t>
|
||||||
|
</template>
|
||||||
|
</i18n-t>
|
||||||
|
</template>
|
||||||
|
<slot />
|
||||||
|
</N8nTooltip>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" module>
|
||||||
|
.link {
|
||||||
|
display: inline-block;
|
||||||
|
margin-top: var(--spacing-xs);
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -15,6 +15,7 @@ import { getResourcePermissions } from '@/permissions';
|
||||||
import { useI18n } from '@/composables/useI18n';
|
import { useI18n } from '@/composables/useI18n';
|
||||||
import { useSettingsStore } from '@/stores/settings.store';
|
import { useSettingsStore } from '@/stores/settings.store';
|
||||||
import ConcurrentExecutionsHeader from '@/components/executions/ConcurrentExecutionsHeader.vue';
|
import ConcurrentExecutionsHeader from '@/components/executions/ConcurrentExecutionsHeader.vue';
|
||||||
|
import { usePageRedirectionHelper } from '@/composables/usePageRedirectionHelper';
|
||||||
|
|
||||||
type AutoScrollDeps = { activeExecutionSet: boolean; cardsMounted: boolean; scroll: boolean };
|
type AutoScrollDeps = { activeExecutionSet: boolean; cardsMounted: boolean; scroll: boolean };
|
||||||
|
|
||||||
|
@ -39,6 +40,7 @@ const i18n = useI18n();
|
||||||
|
|
||||||
const executionsStore = useExecutionsStore();
|
const executionsStore = useExecutionsStore();
|
||||||
const settingsStore = useSettingsStore();
|
const settingsStore = useSettingsStore();
|
||||||
|
const pageRedirectionHelper = usePageRedirectionHelper();
|
||||||
|
|
||||||
const mountedItems = ref<string[]>([]);
|
const mountedItems = ref<string[]>([]);
|
||||||
const autoScrollDeps = ref<AutoScrollDeps>({
|
const autoScrollDeps = ref<AutoScrollDeps>({
|
||||||
|
@ -169,6 +171,10 @@ function scrollToActiveCard(): void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const goToUpgrade = () => {
|
||||||
|
void pageRedirectionHelper.goToUpgrade('concurrency', 'upgrade-concurrency');
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -186,6 +192,8 @@ function scrollToActiveCard(): void {
|
||||||
v-if="settingsStore.isConcurrencyEnabled"
|
v-if="settingsStore.isConcurrencyEnabled"
|
||||||
:running-executions-count="runningExecutionsCount"
|
:running-executions-count="runningExecutionsCount"
|
||||||
:concurrency-cap="settingsStore.concurrency"
|
:concurrency-cap="settingsStore.concurrency"
|
||||||
|
:is-cloud-deployment="settingsStore.isCloudDeployment"
|
||||||
|
@go-to-upgrade="goToUpgrade"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div :class="$style.controls">
|
<div :class="$style.controls">
|
||||||
|
|
|
@ -77,6 +77,7 @@
|
||||||
"generic.ownedByMe": "(You)",
|
"generic.ownedByMe": "(You)",
|
||||||
"generic.moreInfo": "More info",
|
"generic.moreInfo": "More info",
|
||||||
"generic.next": "Next",
|
"generic.next": "Next",
|
||||||
|
"generic.viewDocs": "View docs",
|
||||||
"about.aboutN8n": "About n8n",
|
"about.aboutN8n": "About n8n",
|
||||||
"about.close": "Close",
|
"about.close": "Close",
|
||||||
"about.license": "License",
|
"about.license": "License",
|
||||||
|
@ -662,6 +663,7 @@
|
||||||
"error.goBack": "Go back",
|
"error.goBack": "Go back",
|
||||||
"error.pageNotFound": "Oops, couldn’t find that",
|
"error.pageNotFound": "Oops, couldn’t find that",
|
||||||
"executions.ExecutionStatus": "Execution status",
|
"executions.ExecutionStatus": "Execution status",
|
||||||
|
"executions.concurrency.docsLink": "https://docs.n8n.io/hosting/scaling/concurrency-control/",
|
||||||
"executionDetails.confirmMessage.confirmButtonText": "Yes, delete",
|
"executionDetails.confirmMessage.confirmButtonText": "Yes, delete",
|
||||||
"executionDetails.confirmMessage.headline": "Delete Execution?",
|
"executionDetails.confirmMessage.headline": "Delete Execution?",
|
||||||
"executionDetails.confirmMessage.message": "Are you sure that you want to delete the current execution?",
|
"executionDetails.confirmMessage.message": "Are you sure that you want to delete the current execution?",
|
||||||
|
@ -702,7 +704,7 @@
|
||||||
"executionsLandingPage.noResults": "No executions found",
|
"executionsLandingPage.noResults": "No executions found",
|
||||||
"executionsList.activeExecutions.none": "No active executions",
|
"executionsList.activeExecutions.none": "No active executions",
|
||||||
"executionsList.activeExecutions.header": "{running}/{cap} active executions",
|
"executionsList.activeExecutions.header": "{running}/{cap} active executions",
|
||||||
"executionsList.activeExecutions.tooltip": "Current active executions: {running} out of {cap} allowed by your plan. Upgrade to increase the limit.",
|
"executionsList.activeExecutions.tooltip": "Current active executions: {running} out of {cap}. This instance is limited to {cap} concurrent production executions.",
|
||||||
"executionsList.allWorkflows": "All Workflows",
|
"executionsList.allWorkflows": "All Workflows",
|
||||||
"executionsList.anyStatus": "Any Status",
|
"executionsList.anyStatus": "Any Status",
|
||||||
"executionsList.autoRefresh": "Auto refresh",
|
"executionsList.autoRefresh": "Auto refresh",
|
||||||
|
@ -771,7 +773,9 @@
|
||||||
"executionsList.view": "View",
|
"executionsList.view": "View",
|
||||||
"executionsList.stop": "Stop",
|
"executionsList.stop": "Stop",
|
||||||
"executionsList.statusTooltipText.waitingForWebhook": "The workflow is waiting indefinitely for an incoming webhook call.",
|
"executionsList.statusTooltipText.waitingForWebhook": "The workflow is waiting indefinitely for an incoming webhook call.",
|
||||||
"executionsList.statusTooltipText.waitingForConcurrencyCapacity": "This execution will start once concurrency capacity is available. This instance is limited to {concurrencyCap} concurrent production executions.",
|
"executionsList.statusTooltipText.waitingForConcurrencyCapacity": "This execution will start once concurrency capacity is available. {instance}",
|
||||||
|
"executionsList.statusTooltipText.waitingForConcurrencyCapacity.cloud": "Your plan is limited to {concurrencyCap} concurrent production executions. {link}",
|
||||||
|
"executionsList.statusTooltipText.waitingForConcurrencyCapacity.self": "This instance is limited to {concurrencyCap} concurrent production executions. {link}",
|
||||||
"executionsList.statusTooltipText.theWorkflowIsWaitingIndefinitely": "The workflow is waiting indefinitely for an incoming webhook call.",
|
"executionsList.statusTooltipText.theWorkflowIsWaitingIndefinitely": "The workflow is waiting indefinitely for an incoming webhook call.",
|
||||||
"executionsList.debug.button.copyToEditor": "Copy to editor",
|
"executionsList.debug.button.copyToEditor": "Copy to editor",
|
||||||
"executionsList.debug.button.debugInEditor": "Debug in editor",
|
"executionsList.debug.button.debugInEditor": "Debug in editor",
|
||||||
|
|
Loading…
Reference in a new issue