mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 01:56:46 +00:00
feat(editor): Add stop current execution button in new canvas (no-changelog) (#9968)
This commit is contained in:
@@ -0,0 +1,33 @@
|
|||||||
|
import { createComponentRenderer } from '@/__tests__/render';
|
||||||
|
import CanvasExecuteWorkflowButton from './CanvasExecuteWorkflowButton.vue';
|
||||||
|
|
||||||
|
const renderComponent = createComponentRenderer(CanvasExecuteWorkflowButton);
|
||||||
|
|
||||||
|
describe('CanvasExecuteWorkflowButton', () => {
|
||||||
|
it('should render correctly', () => {
|
||||||
|
const wrapper = renderComponent();
|
||||||
|
|
||||||
|
expect(wrapper.html()).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render different label when executing', () => {
|
||||||
|
const wrapper = renderComponent({
|
||||||
|
props: {
|
||||||
|
executing: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(wrapper.getAllByText('Executing workflow')).toHaveLength(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render different label when executing and waiting for webhook', () => {
|
||||||
|
const wrapper = renderComponent({
|
||||||
|
props: {
|
||||||
|
executing: true,
|
||||||
|
waitingForWebhook: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(wrapper.getAllByText('Waiting for trigger event')).toHaveLength(2);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -2,31 +2,36 @@
|
|||||||
import KeyboardShortcutTooltip from '@/components/KeyboardShortcutTooltip.vue';
|
import KeyboardShortcutTooltip from '@/components/KeyboardShortcutTooltip.vue';
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
import { useI18n } from '@/composables/useI18n';
|
import { useI18n } from '@/composables/useI18n';
|
||||||
import { useUIStore } from '@/stores/ui.store';
|
|
||||||
|
|
||||||
defineEmits<{
|
defineEmits<{
|
||||||
click: [event: MouseEvent];
|
click: [event: MouseEvent];
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const uiStore = useUIStore();
|
const props = defineProps<{
|
||||||
const locale = useI18n();
|
waitingForWebhook: boolean;
|
||||||
|
executing: boolean;
|
||||||
|
}>();
|
||||||
|
|
||||||
const workflowRunning = computed(() => uiStore.isActionActive['workflowRunning']);
|
const i18n = useI18n();
|
||||||
|
|
||||||
const runButtonText = computed(() => {
|
const label = computed(() => {
|
||||||
if (!workflowRunning.value) {
|
if (!props.executing) {
|
||||||
return locale.baseText('nodeView.runButtonText.executeWorkflow');
|
return i18n.baseText('nodeView.runButtonText.executeWorkflow');
|
||||||
}
|
}
|
||||||
|
|
||||||
return locale.baseText('nodeView.runButtonText.executingWorkflow');
|
if (props.waitingForWebhook) {
|
||||||
|
return i18n.baseText('nodeView.runButtonText.waitingForTriggerEvent');
|
||||||
|
}
|
||||||
|
|
||||||
|
return i18n.baseText('nodeView.runButtonText.executingWorkflow');
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<KeyboardShortcutTooltip :label="runButtonText" :shortcut="{ metaKey: true, keys: ['↵'] }">
|
<KeyboardShortcutTooltip :label="label" :shortcut="{ metaKey: true, keys: ['↵'] }">
|
||||||
<N8nButton
|
<N8nButton
|
||||||
:loading="workflowRunning"
|
:loading="executing"
|
||||||
:label="runButtonText"
|
:label="label"
|
||||||
size="large"
|
size="large"
|
||||||
icon="flask"
|
icon="flask"
|
||||||
type="primary"
|
type="primary"
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import { createComponentRenderer } from '@/__tests__/render';
|
||||||
|
import CanvasStopCurrentExecutionButton from './CanvasStopCurrentExecutionButton.vue';
|
||||||
|
|
||||||
|
const renderComponent = createComponentRenderer(CanvasStopCurrentExecutionButton);
|
||||||
|
|
||||||
|
describe('CanvasStopCurrentExecutionButton', () => {
|
||||||
|
it('should render correctly', () => {
|
||||||
|
const wrapper = renderComponent();
|
||||||
|
|
||||||
|
expect(wrapper.html()).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render different title when loading', () => {
|
||||||
|
const wrapper = renderComponent({
|
||||||
|
props: {
|
||||||
|
stopping: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(wrapper.getByTitle('Stopping current execution')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { useI18n } from '@/composables/useI18n';
|
||||||
|
import { computed } from 'vue';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
stopping?: boolean;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const i18n = useI18n();
|
||||||
|
|
||||||
|
const title = computed(() =>
|
||||||
|
props.stopping
|
||||||
|
? i18n.baseText('nodeView.stoppingCurrentExecution')
|
||||||
|
: i18n.baseText('nodeView.stopCurrentExecution'),
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<n8n-icon-button
|
||||||
|
icon="stop"
|
||||||
|
size="large"
|
||||||
|
class="stop-execution"
|
||||||
|
type="secondary"
|
||||||
|
:title="title"
|
||||||
|
:loading="stopping"
|
||||||
|
data-test-id="stop-execution-button"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import { createComponentRenderer } from '@/__tests__/render';
|
||||||
|
import CanvasStopWaitingForWebhookButton from './CanvasStopWaitingForWebhookButton.vue';
|
||||||
|
|
||||||
|
const renderComponent = createComponentRenderer(CanvasStopWaitingForWebhookButton);
|
||||||
|
|
||||||
|
describe('CanvasStopCurrentExecutionButton', () => {
|
||||||
|
it('should render correctly', () => {
|
||||||
|
const wrapper = renderComponent();
|
||||||
|
|
||||||
|
expect(wrapper.html()).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { useI18n } from '@/composables/useI18n';
|
||||||
|
|
||||||
|
const i18n = useI18n();
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<n8n-icon-button
|
||||||
|
class="stop-execution"
|
||||||
|
icon="stop"
|
||||||
|
size="large"
|
||||||
|
:title="i18n.baseText('nodeView.stopWaitingForWebhookCall')"
|
||||||
|
type="secondary"
|
||||||
|
data-test-id="stop-execution-waiting-for-webhook-button"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||||
|
|
||||||
|
exports[`CanvasExecuteWorkflowButton > should render correctly 1`] = `
|
||||||
|
"<button class="button button primary large withIcon el-tooltip__trigger el-tooltip__trigger" aria-live="polite" data-test-id="execute-workflow-button"><span class="icon"><span class="n8n-text compact size-large regular n8n-icon n8n-icon"><svg class="svg-inline--fa fa-flask fa-w-14 large" aria-hidden="true" focusable="false" data-prefix="fas" data-icon="flask" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path class="" fill="currentColor" d="M437.2 403.5L320 215V64h8c13.3 0 24-10.7 24-24V24c0-13.3-10.7-24-24-24H120c-13.3 0-24 10.7-24 24v16c0 13.3 10.7 24 24 24h8v151L10.8 403.5C-18.5 450.6 15.3 512 70.9 512h306.2c55.7 0 89.4-61.5 60.1-108.5zM137.9 320l48.2-77.6c3.7-5.2 5.8-11.6 5.8-18.4V64h64v160c0 6.9 2.2 13.2 5.8 18.4l48.2 77.6h-172z"></path></svg></span></span><span>Test workflow</span></button>
|
||||||
|
<!--teleport start-->
|
||||||
|
<!--teleport end-->"
|
||||||
|
`;
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||||
|
|
||||||
|
exports[`CanvasStopCurrentExecutionButton > should render correctly 1`] = `
|
||||||
|
"<button class="button button secondary large withIcon square stop-execution stop-execution" aria-live="polite" title="Stop current execution" data-test-id="stop-execution-button"><span class="icon"><span class="n8n-text compact size-large regular n8n-icon n8n-icon"><svg class="svg-inline--fa fa-stop fa-w-14 large" aria-hidden="true" focusable="false" data-prefix="fas" data-icon="stop" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path class="" fill="currentColor" d="M400 32H48C21.5 32 0 53.5 0 80v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V80c0-26.5-21.5-48-48-48z"></path></svg></span></span>
|
||||||
|
<!--v-if-->
|
||||||
|
</button>"
|
||||||
|
`;
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||||
|
|
||||||
|
exports[`CanvasStopCurrentExecutionButton > should render correctly 1`] = `
|
||||||
|
"<button class="button button secondary large withIcon square stop-execution stop-execution" aria-live="polite" title="Stop waiting for webhook call" data-test-id="stop-execution-waiting-for-webhook-button"><span class="icon"><span class="n8n-text compact size-large regular n8n-icon n8n-icon"><svg class="svg-inline--fa fa-stop fa-w-14 large" aria-hidden="true" focusable="false" data-prefix="fas" data-icon="stop" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path class="" fill="currentColor" d="M400 32H48C21.5 32 0 53.5 0 80v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V80c0-26.5-21.5-48-48-48z"></path></svg></span></span>
|
||||||
|
<!--v-if-->
|
||||||
|
</button>"
|
||||||
|
`;
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { createPinia, setActivePinia } from 'pinia';
|
import { createPinia, setActivePinia } from 'pinia';
|
||||||
import type { Connection } from '@vue-flow/core';
|
import type { Connection } from '@vue-flow/core';
|
||||||
import type { IConnection } from 'n8n-workflow';
|
import type { IConnection, Workflow } from 'n8n-workflow';
|
||||||
import { NodeConnectionType } from 'n8n-workflow';
|
import { NodeConnectionType } from 'n8n-workflow';
|
||||||
import { useCanvasOperations } from '@/composables/useCanvasOperations';
|
import { useCanvasOperations } from '@/composables/useCanvasOperations';
|
||||||
import type { CanvasElement } from '@/types';
|
import type { CanvasElement } from '@/types';
|
||||||
@@ -65,7 +65,7 @@ describe('useCanvasOperations', () => {
|
|||||||
usedCredentials: [],
|
usedCredentials: [],
|
||||||
});
|
});
|
||||||
workflowsStore.workflowsById[workflowId] = workflow;
|
workflowsStore.workflowsById[workflowId] = workflow;
|
||||||
await workflowHelpers.initState(workflow, true);
|
await workflowHelpers.initState(workflow);
|
||||||
|
|
||||||
canvasOperations = useCanvasOperations({ router, lastClickPosition });
|
canvasOperations = useCanvasOperations({ router, lastClickPosition });
|
||||||
});
|
});
|
||||||
@@ -248,8 +248,8 @@ describe('useCanvasOperations', () => {
|
|||||||
it('should add nodes at current position when position is not specified', async () => {
|
it('should add nodes at current position when position is not specified', async () => {
|
||||||
const nodeTypeName = 'type';
|
const nodeTypeName = 'type';
|
||||||
const nodes = [
|
const nodes = [
|
||||||
mockNode({ name: 'Node 1', type: nodeTypeName, position: [40, 40] }),
|
mockNode({ name: 'Node 1', type: nodeTypeName, position: [120, 120] }),
|
||||||
mockNode({ name: 'Node 2', type: nodeTypeName, position: [100, 240] }),
|
mockNode({ name: 'Node 2', type: nodeTypeName, position: [180, 320] }),
|
||||||
];
|
];
|
||||||
const workflowStoreAddNodeSpy = vi.spyOn(workflowsStore, 'addNode');
|
const workflowStoreAddNodeSpy = vi.spyOn(workflowsStore, 'addNode');
|
||||||
|
|
||||||
@@ -292,9 +292,16 @@ describe('useCanvasOperations', () => {
|
|||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
canvasOperations.editableWorkflowObject.value.getParentNodesByDepth = vi
|
vi.spyOn(workflowsStore, 'getCurrentWorkflow').mockImplementation(() =>
|
||||||
.fn()
|
mock<Workflow>({
|
||||||
.mockReturnValue(nodes.map((node) => node.name));
|
getParentNodesByDepth: () =>
|
||||||
|
nodes.map((node) => ({
|
||||||
|
name: node.name,
|
||||||
|
depth: 0,
|
||||||
|
indicies: [],
|
||||||
|
})),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
await canvasOperations.addNodes(nodes, {});
|
await canvasOperations.addNodes(nodes, {});
|
||||||
|
|
||||||
|
|||||||
@@ -11,13 +11,19 @@ import type {
|
|||||||
INodeUpdatePropertiesInformation,
|
INodeUpdatePropertiesInformation,
|
||||||
XYPosition,
|
XYPosition,
|
||||||
} from '@/Interface';
|
} from '@/Interface';
|
||||||
import { QUICKSTART_NOTE_NAME, STICKY_NODE_TYPE } from '@/constants';
|
import {
|
||||||
|
FORM_TRIGGER_NODE_TYPE,
|
||||||
|
QUICKSTART_NOTE_NAME,
|
||||||
|
STICKY_NODE_TYPE,
|
||||||
|
WEBHOOK_NODE_TYPE,
|
||||||
|
} from '@/constants';
|
||||||
import { useWorkflowsStore } from '@/stores/workflows.store';
|
import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||||
import { useHistoryStore } from '@/stores/history.store';
|
import { useHistoryStore } from '@/stores/history.store';
|
||||||
import { useUIStore } from '@/stores/ui.store';
|
import { useUIStore } from '@/stores/ui.store';
|
||||||
import { useTelemetry } from '@/composables/useTelemetry';
|
import { useTelemetry } from '@/composables/useTelemetry';
|
||||||
import { useExternalHooks } from '@/composables/useExternalHooks';
|
import { useExternalHooks } from '@/composables/useExternalHooks';
|
||||||
import {
|
import {
|
||||||
|
AddNodeCommand,
|
||||||
MoveNodeCommand,
|
MoveNodeCommand,
|
||||||
RemoveConnectionCommand,
|
RemoveConnectionCommand,
|
||||||
RemoveNodeCommand,
|
RemoveNodeCommand,
|
||||||
@@ -53,10 +59,8 @@ import type { useRouter } from 'vue-router';
|
|||||||
import { useCanvasStore } from '@/stores/canvas.store';
|
import { useCanvasStore } from '@/stores/canvas.store';
|
||||||
import { useNodeHelpers } from '@/composables/useNodeHelpers';
|
import { useNodeHelpers } from '@/composables/useNodeHelpers';
|
||||||
|
|
||||||
type AddNodeData = {
|
type AddNodeData = Partial<INodeUi> & {
|
||||||
name?: string;
|
|
||||||
type: string;
|
type: string;
|
||||||
position?: XYPosition;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type AddNodeOptions = {
|
type AddNodeOptions = {
|
||||||
@@ -266,13 +270,12 @@ export function useCanvasOperations({
|
|||||||
) {
|
) {
|
||||||
let currentPosition = position;
|
let currentPosition = position;
|
||||||
let lastAddedNode: INodeUi | undefined;
|
let lastAddedNode: INodeUi | undefined;
|
||||||
for (const { type, name, position: nodePosition, isAutoAdd, openDetail } of nodes) {
|
for (const { isAutoAdd, openDetail, ...nodeData } of nodes) {
|
||||||
try {
|
try {
|
||||||
await createNode(
|
await createNode(
|
||||||
{
|
{
|
||||||
name,
|
...nodeData,
|
||||||
type,
|
position: nodeData.position ?? currentPosition,
|
||||||
position: nodePosition ?? currentPosition,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
dragAndDrop,
|
dragAndDrop,
|
||||||
@@ -328,14 +331,16 @@ export function useCanvasOperations({
|
|||||||
|
|
||||||
workflowsStore.addNode(newNodeData);
|
workflowsStore.addNode(newNodeData);
|
||||||
|
|
||||||
// @TODO Figure out why this is needed and if we can do better...
|
nodeHelpers.matchCredentials(newNodeData);
|
||||||
// this.matchCredentials(node);
|
|
||||||
|
|
||||||
const lastSelectedNode = uiStore.getLastSelectedNode;
|
const lastSelectedNode = uiStore.getLastSelectedNode;
|
||||||
const lastSelectedNodeOutputIndex = uiStore.lastSelectedNodeOutputIndex;
|
const lastSelectedNodeOutputIndex = uiStore.lastSelectedNodeOutputIndex;
|
||||||
const lastSelectedNodeEndpointUuid = uiStore.lastSelectedNodeEndpointUuid;
|
const lastSelectedNodeEndpointUuid = uiStore.lastSelectedNodeEndpointUuid;
|
||||||
|
|
||||||
historyStore.startRecordingUndo();
|
historyStore.startRecordingUndo();
|
||||||
|
if (options.trackHistory) {
|
||||||
|
historyStore.pushCommandToUndo(new AddNodeCommand(newNodeData));
|
||||||
|
}
|
||||||
|
|
||||||
const outputIndex = lastSelectedNodeOutputIndex ?? 0;
|
const outputIndex = lastSelectedNodeOutputIndex ?? 0;
|
||||||
const targetEndpoint = lastSelectedNodeEndpointUuid ?? '';
|
const targetEndpoint = lastSelectedNodeEndpointUuid ?? '';
|
||||||
@@ -399,12 +404,14 @@ export function useCanvasOperations({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const newNodeData: INodeUi = {
|
const newNodeData: INodeUi = {
|
||||||
id: uuid(),
|
...node,
|
||||||
|
id: node.id ?? uuid(),
|
||||||
name: node.name ?? (nodeTypeDescription.defaults.name as string),
|
name: node.name ?? (nodeTypeDescription.defaults.name as string),
|
||||||
type: nodeTypeDescription.name,
|
type: nodeTypeDescription.name,
|
||||||
typeVersion: nodeVersion,
|
typeVersion: nodeVersion,
|
||||||
position: node.position ?? [0, 0],
|
position: node.position ?? [0, 0],
|
||||||
parameters: {},
|
disabled: node.disabled ?? false,
|
||||||
|
parameters: node.parameters ?? {},
|
||||||
};
|
};
|
||||||
|
|
||||||
await loadNodeTypesProperties([{ name: newNodeData.type, version: newNodeData.typeVersion }]);
|
await loadNodeTypesProperties([{ name: newNodeData.type, version: newNodeData.typeVersion }]);
|
||||||
@@ -664,6 +671,14 @@ export function useCanvasOperations({
|
|||||||
newNodeData.webhookId = uuid();
|
newNodeData.webhookId = uuid();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if it's a webhook and the path is empty set the UUID as the default path
|
||||||
|
if (
|
||||||
|
[WEBHOOK_NODE_TYPE, FORM_TRIGGER_NODE_TYPE].includes(newNodeData.type) &&
|
||||||
|
newNodeData.parameters.path === ''
|
||||||
|
) {
|
||||||
|
newNodeData.parameters.path = newNodeData.webhookId as string;
|
||||||
|
}
|
||||||
|
|
||||||
workflowsStore.setNodePristine(newNodeData.name, true);
|
workflowsStore.setNodePristine(newNodeData.name, true);
|
||||||
uiStore.stateIsDirty = true;
|
uiStore.stateIsDirty = true;
|
||||||
|
|
||||||
|
|||||||
@@ -1253,6 +1253,7 @@ export function useNodeHelpers() {
|
|||||||
updateNodesCredentialsIssues,
|
updateNodesCredentialsIssues,
|
||||||
getNodeInputData,
|
getNodeInputData,
|
||||||
setSuccessOutput,
|
setSuccessOutput,
|
||||||
|
matchCredentials,
|
||||||
isInsertingNodes,
|
isInsertingNodes,
|
||||||
credentialsUpdated,
|
credentialsUpdated,
|
||||||
isProductionExecutionPreview,
|
isProductionExecutionPreview,
|
||||||
|
|||||||
@@ -435,10 +435,20 @@ export function useRunWorkflow(useRunWorkflowOpts: { router: ReturnType<typeof u
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function stopWaitingForWebhook() {
|
||||||
|
try {
|
||||||
|
await workflowsStore.removeTestWebhook(workflowsStore.workflowId);
|
||||||
|
} catch (error) {
|
||||||
|
toast.showError(error, i18n.baseText('nodeView.showError.stopWaitingForWebhook.title'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
consolidateRunDataAndStartNodes,
|
consolidateRunDataAndStartNodes,
|
||||||
runWorkflow,
|
runWorkflow,
|
||||||
runWorkflowApi,
|
runWorkflowApi,
|
||||||
stopCurrentExecution,
|
stopCurrentExecution,
|
||||||
|
stopWaitingForWebhook,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1050,12 +1050,8 @@ export function useWorkflowHelpers(options: { router: ReturnType<typeof useRoute
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function initState(workflowData: IWorkflowDb, set = false): Promise<void> {
|
async function initState(workflowData: IWorkflowDb): Promise<void> {
|
||||||
workflowsStore.addWorkflow(workflowData);
|
workflowsStore.addWorkflow(workflowData);
|
||||||
if (set) {
|
|
||||||
workflowsStore.setWorkflow(workflowData);
|
|
||||||
}
|
|
||||||
|
|
||||||
workflowsStore.setActive(workflowData.active || false);
|
workflowsStore.setActive(workflowData.active || false);
|
||||||
workflowsStore.setWorkflowId(workflowData.id);
|
workflowsStore.setWorkflowId(workflowData.id);
|
||||||
workflowsStore.setWorkflowName({
|
workflowsStore.setWorkflowName({
|
||||||
|
|||||||
@@ -73,8 +73,10 @@ import { useUsersStore } from '@/stores/users.store';
|
|||||||
import { sourceControlEventBus } from '@/event-bus/source-control';
|
import { sourceControlEventBus } from '@/event-bus/source-control';
|
||||||
import { useTagsStore } from '@/stores/tags.store';
|
import { useTagsStore } from '@/stores/tags.store';
|
||||||
import { usePushConnectionStore } from '@/stores/pushConnection.store';
|
import { usePushConnectionStore } from '@/stores/pushConnection.store';
|
||||||
import { getNodeViewTab } from '@/utils/canvasUtils';
|
|
||||||
import { useNDVStore } from '@/stores/ndv.store';
|
import { useNDVStore } from '@/stores/ndv.store';
|
||||||
|
import { getNodeViewTab } from '@/utils/canvasUtils';
|
||||||
|
import CanvasStopCurrentExecutionButton from '@/components/canvas/elements/buttons/CanvasStopCurrentExecutionButton.vue';
|
||||||
|
import CanvasStopWaitingForWebhookButton from '@/components/canvas/elements/buttons/CanvasStopWaitingForWebhookButton.vue';
|
||||||
|
|
||||||
const NodeCreation = defineAsyncComponent(
|
const NodeCreation = defineAsyncComponent(
|
||||||
async () => await import('@/components/Node/NodeCreation.vue'),
|
async () => await import('@/components/Node/NodeCreation.vue'),
|
||||||
@@ -120,7 +122,7 @@ const ndvStore = useNDVStore();
|
|||||||
|
|
||||||
const lastClickPosition = ref<XYPosition>([450, 450]);
|
const lastClickPosition = ref<XYPosition>([450, 450]);
|
||||||
|
|
||||||
const { runWorkflow } = useRunWorkflow({ router });
|
const { runWorkflow, stopCurrentExecution, stopWaitingForWebhook } = useRunWorkflow({ router });
|
||||||
const {
|
const {
|
||||||
updateNodePosition,
|
updateNodePosition,
|
||||||
renameNode,
|
renameNode,
|
||||||
@@ -147,7 +149,6 @@ const readOnlyNotification = ref<null | { visible: boolean }>(null);
|
|||||||
|
|
||||||
const isProductionExecutionPreview = ref(false);
|
const isProductionExecutionPreview = ref(false);
|
||||||
const isExecutionPreview = ref(false);
|
const isExecutionPreview = ref(false);
|
||||||
const isExecutionWaitingForWebhook = ref(false);
|
|
||||||
|
|
||||||
const canOpenNDV = ref(true);
|
const canOpenNDV = ref(true);
|
||||||
const hideNodeIssues = ref(false);
|
const hideNodeIssues = ref(false);
|
||||||
@@ -348,7 +349,9 @@ async function openWorkflow(data: IWorkflowDb) {
|
|||||||
|
|
||||||
resetWorkspace();
|
resetWorkspace();
|
||||||
|
|
||||||
await workflowHelpers.initState(data, true);
|
await workflowHelpers.initState(data);
|
||||||
|
await addNodes(data.nodes);
|
||||||
|
workflowsStore.setConnections(data.connections);
|
||||||
|
|
||||||
if (data.sharedWithProjects) {
|
if (data.sharedWithProjects) {
|
||||||
workflowsEEStore.setWorkflowSharedWith({
|
workflowsEEStore.setWorkflowSharedWith({
|
||||||
@@ -536,6 +539,18 @@ function onToggleNodeCreator(options: ToggleNodeCreatorOptions) {
|
|||||||
* Executions
|
* Executions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
const isStoppingExecution = ref(false);
|
||||||
|
|
||||||
|
const isWorkflowRunning = computed(() => uiStore.isActionActive.workflowRunning);
|
||||||
|
const isExecutionWaitingForWebhook = computed(() => workflowsStore.executionWaitingForWebhook);
|
||||||
|
|
||||||
|
const isStopExecutionButtonVisible = computed(
|
||||||
|
() => isWorkflowRunning.value && !isExecutionWaitingForWebhook.value,
|
||||||
|
);
|
||||||
|
const isStopWaitingForWebhookButtonVisible = computed(
|
||||||
|
() => isWorkflowRunning.value && isExecutionWaitingForWebhook.value,
|
||||||
|
);
|
||||||
|
|
||||||
async function onRunWorkflow() {
|
async function onRunWorkflow() {
|
||||||
trackRunWorkflow();
|
trackRunWorkflow();
|
||||||
|
|
||||||
@@ -583,8 +598,18 @@ async function openExecution(_executionId: string) {
|
|||||||
// @TODO
|
// @TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function onStopExecution() {
|
||||||
|
isStoppingExecution.value = true;
|
||||||
|
await stopCurrentExecution();
|
||||||
|
isStoppingExecution.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onStopWaitingForWebhook() {
|
||||||
|
await stopWaitingForWebhook();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Keboard
|
* Keyboard
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function addKeyboardEventBindings() {
|
function addKeyboardEventBindings() {
|
||||||
@@ -938,7 +963,20 @@ onBeforeUnmount(() => {
|
|||||||
@click:pane="onClickPane"
|
@click:pane="onClickPane"
|
||||||
>
|
>
|
||||||
<div :class="$style.executionButtons">
|
<div :class="$style.executionButtons">
|
||||||
<CanvasExecuteWorkflowButton @click="onRunWorkflow" />
|
<CanvasExecuteWorkflowButton
|
||||||
|
:waiting-for-webhook="isExecutionWaitingForWebhook"
|
||||||
|
:executing="isWorkflowRunning"
|
||||||
|
@click="onRunWorkflow"
|
||||||
|
/>
|
||||||
|
<CanvasStopCurrentExecutionButton
|
||||||
|
v-if="isStopExecutionButtonVisible"
|
||||||
|
:stopping="isStoppingExecution"
|
||||||
|
@click="onStopExecution"
|
||||||
|
/>
|
||||||
|
<CanvasStopWaitingForWebhookButton
|
||||||
|
v-if="isStopWaitingForWebhookButtonVisible"
|
||||||
|
@click="onStopWaitingForWebhook"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Suspense>
|
<Suspense>
|
||||||
<NodeCreation
|
<NodeCreation
|
||||||
@@ -956,12 +994,12 @@ onBeforeUnmount(() => {
|
|||||||
:is-production-execution-preview="isProductionExecutionPreview"
|
:is-production-execution-preview="isProductionExecutionPreview"
|
||||||
:renaming="false"
|
:renaming="false"
|
||||||
@value-changed="onRenameNode"
|
@value-changed="onRenameNode"
|
||||||
|
@stop-execution="onStopExecution"
|
||||||
@switch-selected-node="onSwitchActiveNode"
|
@switch-selected-node="onSwitchActiveNode"
|
||||||
@open-connection-node-creator="onOpenConnectionNodeCreator"
|
@open-connection-node-creator="onOpenConnectionNodeCreator"
|
||||||
/>
|
/>
|
||||||
<!--
|
<!--
|
||||||
:renaming="renamingActive"
|
:renaming="renamingActive"
|
||||||
@stop-execution="stopExecution"
|
|
||||||
@save-keyboard-shortcut="onSaveKeyboardShortcut"
|
@save-keyboard-shortcut="onSaveKeyboardShortcut"
|
||||||
-->
|
-->
|
||||||
</Suspense>
|
</Suspense>
|
||||||
|
|||||||
Reference in New Issue
Block a user