From 1580fcd370f1a3ad01fe207ff9db919c606d41c0 Mon Sep 17 00:00:00 2001 From: Milorad Filipovic Date: Wed, 28 Sep 2022 10:54:03 +0200 Subject: [PATCH 01/25] =?UTF-8?q?=E2=9C=A8=20Adding=20current=20workflow?= =?UTF-8?q?=20execution=20list=20to=20the=20Vuex=20store?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/editor-ui/src/Interface.ts | 1 + .../src/components/MainHeader/MainHeader.vue | 28 +++------------- packages/editor-ui/src/store.ts | 33 ++++++++++++++++++- 3 files changed, 38 insertions(+), 24 deletions(-) diff --git a/packages/editor-ui/src/Interface.ts b/packages/editor-ui/src/Interface.ts index 4a7d6730cff45..c5aecbcb78798 100644 --- a/packages/editor-ui/src/Interface.ts +++ b/packages/editor-ui/src/Interface.ts @@ -863,6 +863,7 @@ export interface IRootState { activeCredentialType: string | null; activeNode: string | null; baseUrl: string; + currentWorkflowExecutions: IExecutionsSummary[], defaultLocale: string; endpointWebhook: string; endpointWebhookTest: string; diff --git a/packages/editor-ui/src/components/MainHeader/MainHeader.vue b/packages/editor-ui/src/components/MainHeader/MainHeader.vue index 0541e69fb3762..5cc198baa0a2d 100644 --- a/packages/editor-ui/src/components/MainHeader/MainHeader.vue +++ b/packages/editor-ui/src/components/MainHeader/MainHeader.vue @@ -36,7 +36,6 @@ export default mixins( data() { return { activeHeaderTab: 'workflow', - executions: new Array(), }; }, computed: { @@ -46,7 +45,7 @@ export default mixins( tabBarItems(): ITabBarItem[] { return [ { id: MAIN_HEADER_TABS.WORKFLOW, label: 'Workflow' }, - { id: MAIN_HEADER_TABS.EXECUTIONS, label: 'Executions', notifications: this.executions.length }, + { id: MAIN_HEADER_TABS.EXECUTIONS, label: 'Executions', notifications: this.currentWorkflow ? this.executions.length : 0 }, { id: MAIN_HEADER_TABS.SETTINGS, label: 'Settings', disabled: !this.onWorkflowPage || !this.currentWorkflow }, ]; }, @@ -68,6 +67,9 @@ export default mixins( onWorkflowPage(): boolean { return this.$route.meta && this.$route.meta.nodeView; }, + executions(): IExecutionsSummary[] { + return this.$store.getters.currentWorkflowExecutions; + }, }, async mounted() { this.loadExecutions(this.currentWorkflow); @@ -81,8 +83,6 @@ export default mixins( "$route.params.name"(value) { if (value) { this.loadExecutions(value); - } else { - this.executions = []; } }, }, @@ -103,30 +103,12 @@ export default mixins( break; } }, - async loadActiveExecutions (workflowId: string): Promise { - const activeExecutions = await this.restApi().getCurrentExecutions({ workflowId } as IDataObject); - for (const activeExecution of activeExecutions) { - if (activeExecution.workflowId !== undefined && activeExecution.workflowName === undefined) { - activeExecution.workflowName = this.workflowName; - } - } - return activeExecutions; - }, - async loadFinishedExecutions (workflowId: string): Promise { - const data = await this.restApi().getPastExecutions({ workflowId } as IDataObject, 10); - return data.results; - }, async loadExecutions (workflowId: string): Promise { if (!this.currentWorkflow) { return; } - try { - const activeExecutionsPromise = this.loadActiveExecutions(workflowId); - const finishedExecutionsPromise = this.loadFinishedExecutions(workflowId); - await Promise.all([activeExecutionsPromise, finishedExecutionsPromise]).then((results) => { - this.executions = [...results[0], ...results[1]]; - }); + await this.$store.dispatch('loadCurrentWorkflowActions', workflowId); } catch (error) { this.$showError( error, diff --git a/packages/editor-ui/src/store.ts b/packages/editor-ui/src/store.ts index 1cd2f24ab1f01..9240e157cfc0a 100644 --- a/packages/editor-ui/src/store.ts +++ b/packages/editor-ui/src/store.ts @@ -1,5 +1,5 @@ import Vue from 'vue'; -import Vuex from 'vuex'; +import Vuex, { ActionContext } from 'vuex'; import { PLACEHOLDER_EMPTY_WORKFLOW_ID, @@ -34,6 +34,7 @@ import { XYPosition, IRestApiContext, IWorkflowsState, + IExecutionsSummary, } from './Interface'; import nodeTypes from './modules/nodeTypes'; @@ -49,6 +50,8 @@ import {stringSizeInBytes} from "@/components/helpers"; import {dataPinningEventBus} from "@/event-bus/data-pinning-event-bus"; import communityNodes from './modules/communityNodes'; import { isJsonKeyObject } from './utils'; +import { restApi } from './components/mixins/restApi'; +import { makeRestApiRequest } from './api/helpers'; Vue.use(Vuex); @@ -60,6 +63,7 @@ const state: IRootState = { activeCredentialType: null, // @ts-ignore baseUrl: import.meta.env.VUE_APP_URL_BASE_API ? import.meta.env.VUE_APP_URL_BASE_API : (window.BASE_PATH === '/{{BASE_PATH}}/' ? '/' : window.BASE_PATH), + currentWorkflowExecutions: [], defaultLocale: 'en', endpointWebhook: 'webhook', endpointWebhookTest: 'webhook-test', @@ -708,6 +712,30 @@ export const store = new Vuex.Store({ const updated = state.sidebarMenuItems.concat(menuItems); Vue.set(state, 'sidebarMenuItems', updated); }, + setCurrentWorkflowExecutions (state: IRootState, executions: IExecutionsSummary[]) { + state.currentWorkflowExecutions = executions; + }, + }, + actions: { + async loadCurrentWorkflowActions(context: ActionContext, workflowId: string) { + try { + const activeExecutions = await makeRestApiRequest( + context.rootGetters.getRestApiContext, + 'GET', + `/executions-current`, + { filter: { workflowId } } as IDataObject, + ); + const finishedExecutions = await makeRestApiRequest( + context.rootGetters.getRestApiContext, + 'GET', + '/executions', + { filter: { workflowId } } as IDataObject, + ); + context.commit('setCurrentWorkflowExecutions', [...activeExecutions, ...finishedExecutions.results]); + } catch (error) { + throw(error); + } + }, }, getters: { executedNode: (state): string | undefined => { @@ -993,5 +1021,8 @@ export const store = new Vuex.Store({ sidebarMenuItems: (state): IMenuItem[] => { return state.sidebarMenuItems; }, + currentWorkflowExecutions: (state: IRootState): IExecutionsSummary[] => { + return state.currentWorkflowExecutions; + }, }, }); From eab2c317892914adccc38ead8f58079b229c32b3 Mon Sep 17 00:00:00 2001 From: Milorad Filipovic Date: Wed, 28 Sep 2022 11:01:02 +0200 Subject: [PATCH 02/25] =?UTF-8?q?=E2=9C=A8=20Updating=20current=20workflow?= =?UTF-8?q?=20executions=20after=20running=20a=20workflow=20from=20the=20n?= =?UTF-8?q?ode=20view?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/editor-ui/src/components/mixins/workflowRun.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/editor-ui/src/components/mixins/workflowRun.ts b/packages/editor-ui/src/components/mixins/workflowRun.ts index 84ebfd9b32250..d367017ada6ff 100644 --- a/packages/editor-ui/src/components/mixins/workflowRun.ts +++ b/packages/editor-ui/src/components/mixins/workflowRun.ts @@ -228,9 +228,11 @@ export const workflowRun = mixins( this.$store.commit('setWorkflowExecutionData', executionData); this.updateNodesExecutionIssues(); - const runWorkflowApiResponse = await this.runWorkflowApi(startRunData); + const runWorkflowApiResponse = await this.runWorkflowApi(startRunData); - this.$externalHooks().run('workflowRun.runWorkflow', { nodeName, source }); + this.$externalHooks().run('workflowRun.runWorkflow', { nodeName, source }); + + await this.$store.dispatch('loadCurrentWorkflowActions', this.$store.getters.workflowId); return runWorkflowApiResponse; } catch (error) { From fbf9b91d44df215c20d6ab59bf6202cbe5803a61 Mon Sep 17 00:00:00 2001 From: Milorad Filipovic Date: Wed, 28 Sep 2022 13:13:48 +0200 Subject: [PATCH 03/25] =?UTF-8?q?=E2=9C=A8=20Keeping=20the=20tab=20view=20?= =?UTF-8?q?content=20alive=20when=20switching=20tabs=20in=20main=20header?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/editor-ui/src/App.vue | 4 +- .../src/components/MainHeader/MainHeader.vue | 38 +++++--- packages/editor-ui/src/constants.ts | 1 + packages/editor-ui/src/router.ts | 18 ++++ .../editor-ui/src/views/ExecutionsView.vue | 13 +++ packages/editor-ui/src/views/NodeView.vue | 95 ++++++++++--------- 6 files changed, 114 insertions(+), 55 deletions(-) create mode 100644 packages/editor-ui/src/views/ExecutionsView.vue diff --git a/packages/editor-ui/src/App.vue b/packages/editor-ui/src/App.vue index a03bbf4d37f5c..2daed0bd30097 100644 --- a/packages/editor-ui/src/App.vue +++ b/packages/editor-ui/src/App.vue @@ -16,7 +16,9 @@
- + + +
diff --git a/packages/editor-ui/src/components/MainHeader/MainHeader.vue b/packages/editor-ui/src/components/MainHeader/MainHeader.vue index 5cc198baa0a2d..5dab4ebd8a722 100644 --- a/packages/editor-ui/src/components/MainHeader/MainHeader.vue +++ b/packages/editor-ui/src/components/MainHeader/MainHeader.vue @@ -17,10 +17,9 @@ import { pushConnection } from '@/components/mixins/pushConnection'; import WorkflowDetails from '@/components/MainHeader/WorkflowDetails.vue'; import ExecutionDetails from '@/components/MainHeader/ExecutionDetails/ExecutionDetails.vue'; import TabBar from '@/components/MainHeader/TabBar.vue'; -import { EXECUTIONS_MODAL_KEY, MAIN_HEADER_TABS, STICKY_NODE_TYPE, VIEWS, WORKFLOW_SETTINGS_MODAL_KEY } from '@/constants'; -import { IExecutionsCurrentSummaryExtended, IExecutionsSummary, INodeUi, ITabBarItem } from '@/Interface'; +import { MAIN_HEADER_TABS, PLACEHOLDER_EMPTY_WORKFLOW_ID, STICKY_NODE_TYPE, VIEWS, WORKFLOW_SETTINGS_MODAL_KEY } from '@/constants'; +import { IExecutionsSummary, INodeUi, ITabBarItem } from '@/Interface'; import { workflowHelpers } from '../mixins/workflowHelpers'; -import { IDataObject } from 'n8n-workflow'; export default mixins( pushConnection, @@ -36,6 +35,7 @@ export default mixins( data() { return { activeHeaderTab: 'workflow', + workflowToReturnTo: '', }; }, computed: { @@ -83,21 +83,37 @@ export default mixins( "$route.params.name"(value) { if (value) { this.loadExecutions(value); + if (value !== PLACEHOLDER_EMPTY_WORKFLOW_ID) { + this.workflowToReturnTo = value; + } } }, }, methods: { onTabSelected(tab: string, event: MouseEvent) { - this.activeHeaderTab = tab; - switch (tab) { - case 'settings': - this.$store.dispatch('ui/openModal', WORKFLOW_SETTINGS_MODAL_KEY); - this.activeHeaderTab = 'workflow'; + case MAIN_HEADER_TABS.WORKFLOW: + if (this.workflowToReturnTo !== '' && this.workflowToReturnTo !== PLACEHOLDER_EMPTY_WORKFLOW_ID) { + this.$router.push({ + name: VIEWS.WORKFLOW, + params: { name: this.workflowToReturnTo }, + }); + } else { + this.$router.push({ name: VIEWS.NEW_WORKFLOW }); + } + this.activeHeaderTab = MAIN_HEADER_TABS.WORKFLOW; break; - case 'executions': - this.$store.dispatch('ui/openModal', EXECUTIONS_MODAL_KEY); - this.activeHeaderTab = 'workflow'; + case MAIN_HEADER_TABS.EXECUTIONS: + this.workflowToReturnTo = this.currentWorkflow; + this.$router.push({ + name: VIEWS.EXECUTIONS, + }); + // this.modalBus.$emit('closeAll'); + this.activeHeaderTab = MAIN_HEADER_TABS.EXECUTIONS; + break; + case MAIN_HEADER_TABS.SETTINGS: + this.$store.dispatch('ui/openModal', WORKFLOW_SETTINGS_MODAL_KEY); + this.activeHeaderTab = MAIN_HEADER_TABS.WORKFLOW; break; default: break; diff --git a/packages/editor-ui/src/constants.ts b/packages/editor-ui/src/constants.ts index 171cedcdaa167..b75d8d0b0ac7b 100644 --- a/packages/editor-ui/src/constants.ts +++ b/packages/editor-ui/src/constants.ts @@ -266,6 +266,7 @@ export enum VIEWS { HOMEPAGE = "Homepage", COLLECTION = "TemplatesCollectionView", EXECUTION = "ExecutionById", + EXECUTIONS = "ExecutionList", TEMPLATE = "TemplatesWorkflowView", TEMPLATES = "TemplatesSearchView", CREDENTIALS = "CredentialsView", diff --git a/packages/editor-ui/src/router.ts b/packages/editor-ui/src/router.ts index d5f8cc368cb9b..4390dd419c176 100644 --- a/packages/editor-ui/src/router.ts +++ b/packages/editor-ui/src/router.ts @@ -6,6 +6,7 @@ import ForgotMyPasswordView from './views/ForgotMyPasswordView.vue'; import MainHeader from '@/components/MainHeader/MainHeader.vue'; import MainSidebar from '@/components/MainSidebar.vue'; import NodeView from '@/views/NodeView.vue'; +import ExecutionsView from '@/views/ExecutionsView.vue'; import SettingsPersonalView from './views/SettingsPersonalView.vue'; import SettingsUsersView from './views/SettingsUsersView.vue'; import SettingsCommunityNodesView from './views/SettingsCommunityNodesView.vue'; @@ -121,6 +122,23 @@ const router = new Router({ }, }, }, + { + path: '/executions/', + name: VIEWS.EXECUTIONS, + components: { + default: ExecutionsView, + header: MainHeader, + sidebar: MainSidebar, + }, + meta: { + keepWorkflowDirty: true, + permissions: { + allow: { + loginStatus: [LOGIN_STATUS.LoggedIn], + }, + }, + }, + }, { path: '/templates/:id', name: VIEWS.TEMPLATE, diff --git a/packages/editor-ui/src/views/ExecutionsView.vue b/packages/editor-ui/src/views/ExecutionsView.vue new file mode 100644 index 0000000000000..9960016deb0e9 --- /dev/null +++ b/packages/editor-ui/src/views/ExecutionsView.vue @@ -0,0 +1,13 @@ + + + diff --git a/packages/editor-ui/src/views/NodeView.vue b/packages/editor-ui/src/views/NodeView.vue index 34d85399ba41d..74be5f4e6d594 100644 --- a/packages/editor-ui/src/views/NodeView.vue +++ b/packages/editor-ui/src/views/NodeView.vue @@ -276,7 +276,10 @@ export default mixins( }, watch: { // Listen to route changes and load the workflow accordingly - '$route': 'initView', + '$route' (to, from) { + const checkDirty = from.meta && from.meta.keepWorkflowDirty !== true; + this.initView(checkDirty); + }, activeNode () { // When a node gets set as active deactivate the create-menu this.createNodeActive = false; @@ -304,6 +307,11 @@ export default mixins( }, async beforeRouteLeave(to, from, next) { + if (to.meta && to.meta.keepWorkflowDirty === true) { + next(); + return; + } + const result = this.$store.getters.getStateIsDirty; if(result) { const confirmModal = await this.confirmModal( @@ -454,6 +462,7 @@ export default mixins( }; this.$telemetry.track('User clicked execute workflow button', telemetryPayload); this.$externalHooks().run('nodeView.onRunWorkflow', telemetryPayload); + }); this.runWorkflow(); @@ -2084,7 +2093,7 @@ export default mixins( }, 0); } }, - async initView (): Promise { + async initView (checkDirty = true): Promise { if (this.$route.params.action === 'workflowSave') { // In case the workflow got saved we do not have to run init // as only the route changed but all the needed data is already loaded @@ -2104,50 +2113,50 @@ export default mixins( const executionId = this.$route.params.id; await this.openExecution(executionId); } else { - - const result = this.$store.getters.getStateIsDirty; - if(result) { - const confirmModal = await this.confirmModal( - this.$locale.baseText('nodeView.confirmMessage.initView.message'), - this.$locale.baseText('nodeView.confirmMessage.initView.headline'), - 'warning', - this.$locale.baseText('nodeView.confirmMessage.initView.confirmButtonText'), - this.$locale.baseText('nodeView.confirmMessage.initView.cancelButtonText'), - true, - ); - - if (confirmModal === MODAL_CONFIRMED) { - const saved = await this.saveCurrentWorkflow(); - if (saved) this.$store.dispatch('settings/fetchPromptsData'); - } else if (confirmModal === MODAL_CLOSE) { - return Promise.resolve(); + if ((this.$route.meta && this.$route.meta.keepWorkflowDirty !== true) && checkDirty) { + const result = this.$store.getters.getStateIsDirty; + if(result) { + const confirmModal = await this.confirmModal( + this.$locale.baseText('generic.unsavedWork.confirmMessage.message'), + this.$locale.baseText('generic.unsavedWork.confirmMessage.headline'), + 'warning', + this.$locale.baseText('generic.unsavedWork.confirmMessage.confirmButtonText'), + this.$locale.baseText('generic.unsavedWork.confirmMessage.cancelButtonText'), + true, + ); + + if (confirmModal === MODAL_CONFIRMED) { + const saved = await this.saveCurrentWorkflow(); + if (saved) this.$store.dispatch('settings/fetchPromptsData'); + } else if (confirmModal === MODAL_CLOSE) { + return Promise.resolve(); + } } - } - - // Load a workflow - let workflowId = null as string | null; - if (this.$route.params.name) { - workflowId = this.$route.params.name; - } - if (workflowId !== null) { - const workflow = await this.restApi().getWorkflow(workflowId); - if (!workflow) { - this.$router.push({ - name: VIEWS.NEW_WORKFLOW, - }); - this.$showMessage({ - title: 'Error', - message: this.$locale.baseText('openWorkflow.workflowNotFoundError'), - type: 'error', - }); + // Load a workflow + let workflowId = null as string | null; + if (this.$route.params.name) { + workflowId = this.$route.params.name; + } + if (workflowId !== null) { + const workflow = await this.restApi().getWorkflow(workflowId); + if (!workflow) { + this.$router.push({ + name: VIEWS.NEW_WORKFLOW, + }); + this.$showMessage({ + title: 'Error', + message: this.$locale.baseText('openWorkflow.workflowNotFoundError'), + type: 'error', + }); + } else { + this.$titleSet(workflow.name, 'IDLE'); + // Open existing workflow + await this.openWorkflow(workflowId); + } } else { - this.$titleSet(workflow.name, 'IDLE'); - // Open existing workflow - await this.openWorkflow(workflowId); + // Create new workflow + await this.newWorkflow(); } - } else { - // Create new workflow - await this.newWorkflow(); } } From 2caa9347c4927d7f9dde31cbd989fa053cf80654 Mon Sep 17 00:00:00 2001 From: Milorad Filipovic Date: Wed, 28 Sep 2022 15:06:03 +0200 Subject: [PATCH 04/25] =?UTF-8?q?=E2=9C=A8=20Updating=20main=20header=20co?= =?UTF-8?q?ntrols=20to=20work=20with=20current=20workflow=20regardless=20o?= =?UTF-8?q?f=20active=20tab?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/MainHeader/MainHeader.vue | 10 ++++++---- .../components/MainHeader/WorkflowDetails.vue | 19 ++++++++----------- .../src/components/WorkflowSettings.vue | 6 +++--- .../src/components/mixins/workflowHelpers.ts | 4 ++-- packages/editor-ui/src/router.ts | 3 ++- packages/editor-ui/src/views/NodeView.vue | 6 +++--- 6 files changed, 24 insertions(+), 24 deletions(-) diff --git a/packages/editor-ui/src/components/MainHeader/MainHeader.vue b/packages/editor-ui/src/components/MainHeader/MainHeader.vue index 5dab4ebd8a722..4b9d045a32f6b 100644 --- a/packages/editor-ui/src/components/MainHeader/MainHeader.vue +++ b/packages/editor-ui/src/components/MainHeader/MainHeader.vue @@ -46,7 +46,7 @@ export default mixins( return [ { id: MAIN_HEADER_TABS.WORKFLOW, label: 'Workflow' }, { id: MAIN_HEADER_TABS.EXECUTIONS, label: 'Executions', notifications: this.currentWorkflow ? this.executions.length : 0 }, - { id: MAIN_HEADER_TABS.SETTINGS, label: 'Settings', disabled: !this.onWorkflowPage || !this.currentWorkflow }, + { id: MAIN_HEADER_TABS.SETTINGS, label: 'Settings', disabled: !this.onWorkflowPage || !this.currentWorkflow || this.currentWorkflow === PLACEHOLDER_EMPTY_WORKFLOW_ID }, ]; }, isExecutionPage (): boolean { @@ -62,10 +62,10 @@ export default mixins( return this.$store.getters.workflowName; }, currentWorkflow (): string { - return this.$route.params.name; + return this.$route.params.name || this.$store.getters.workflowId; }, onWorkflowPage(): boolean { - return this.$route.meta && this.$route.meta.nodeView; + return this.$route.meta && (this.$route.meta.nodeView || this.$route.meta.keepWorkflowAlive === true); }, executions(): IExecutionsSummary[] { return this.$store.getters.currentWorkflowExecutions; @@ -73,6 +73,9 @@ export default mixins( }, async mounted() { this.loadExecutions(this.currentWorkflow); + if (this.$route.path === `/${MAIN_HEADER_TABS.EXECUTIONS}`) { + this.activeHeaderTab = MAIN_HEADER_TABS.EXECUTIONS; + } // Initialize the push connection this.pushConnect(); }, @@ -113,7 +116,6 @@ export default mixins( break; case MAIN_HEADER_TABS.SETTINGS: this.$store.dispatch('ui/openModal', WORKFLOW_SETTINGS_MODAL_KEY); - this.activeHeaderTab = MAIN_HEADER_TABS.WORKFLOW; break; default: break; diff --git a/packages/editor-ui/src/components/MainHeader/WorkflowDetails.vue b/packages/editor-ui/src/components/MainHeader/WorkflowDetails.vue index eb1fb0af69a26..261534cf9ab35 100644 --- a/packages/editor-ui/src/components/MainHeader/WorkflowDetails.vue +++ b/packages/editor-ui/src/components/MainHeader/WorkflowDetails.vue @@ -145,29 +145,26 @@ export default mixins(workflowHelpers, titleChange).extend({ }), ...mapGetters('settings', ['areTagsEnabled']), isNewWorkflow(): boolean { - return !this.$route.params.name; + return !this.currentWorkflowId; }, isWorkflowSaving(): boolean { return this.$store.getters.isActionActive("workflowSaving"); }, currentWorkflowId(): string { - return this.$route.params.name; - }, - currentWorkflow (): string { - return this.$route.params.name; + return this.$store.getters.workflowId; }, workflowName (): string { return this.$store.getters.workflowName; }, onWorkflowPage(): boolean { - return this.$route.meta && this.$route.meta.nodeView; + return this.$route.meta && (this.$route.meta.nodeView || this.$route.meta.keepWorkflowAlive === true); }, workflowMenuItems(): Array<{}> { return [ { id: WORKFLOW_MENU_ACTIONS.DUPLICATE, label: this.$locale.baseText('menuActions.duplicate'), - disabled: !this.onWorkflowPage || !this.currentWorkflow, + disabled: !this.onWorkflowPage || !this.currentWorkflowId, }, { id: WORKFLOW_MENU_ACTIONS.DOWNLOAD, @@ -187,12 +184,12 @@ export default mixins(workflowHelpers, titleChange).extend({ { id: WORKFLOW_MENU_ACTIONS.SETTINGS, label: this.$locale.baseText('generic.settings'), - disabled: !this.onWorkflowPage || !this.currentWorkflow, + disabled: !this.onWorkflowPage || !this.currentWorkflowId, }, { id: WORKFLOW_MENU_ACTIONS.DELETE, label: this.$locale.baseText('menuActions.delete'), - disabled: !this.onWorkflowPage || !this.currentWorkflow, + disabled: !this.onWorkflowPage || !this.currentWorkflowId, customClass: this.$style.deleteItem, divided: true, }, @@ -201,7 +198,7 @@ export default mixins(workflowHelpers, titleChange).extend({ }, methods: { async onSaveButtonClick () { - const saved = await this.saveCurrentWorkflow(); + const saved = await this.saveCurrentWorkflow({ id: this.currentWorkflowId, name: this.workflowName, tags: this.currentWorkflowTagIds }); if (saved) this.$store.dispatch('settings/fetchPromptsData'); }, onTagsEditEnable() { @@ -382,7 +379,7 @@ export default mixins(workflowHelpers, titleChange).extend({ } try { - await this.restApi().deleteWorkflow(this.currentWorkflow); + await this.restApi().deleteWorkflow(this.currentWorkflowId); } catch (error) { this.$showError( error, diff --git a/packages/editor-ui/src/components/WorkflowSettings.vue b/packages/editor-ui/src/components/WorkflowSettings.vue index fda990f49c66b..f975b25d289a8 100644 --- a/packages/editor-ui/src/components/WorkflowSettings.vue +++ b/packages/editor-ui/src/components/WorkflowSettings.vue @@ -187,7 +187,7 @@ import { IWorkflowShortResponse, } from '@/Interface'; import Modal from './Modal.vue'; -import { WORKFLOW_SETTINGS_MODAL_KEY } from '../constants'; +import { PLACEHOLDER_EMPTY_WORKFLOW_ID, WORKFLOW_SETTINGS_MODAL_KEY } from '../constants'; import mixins from 'vue-typed-mixins'; @@ -243,7 +243,7 @@ export default mixins( }, async mounted () { - if (this.$route.params.name === undefined) { + if (!this.workflowId || this.workflowId === PLACEHOLDER_EMPTY_WORKFLOW_ID) { this.$showMessage({ title: 'No workflow active', message: `No workflow active to display settings of.`, @@ -518,7 +518,7 @@ export default mixins( this.isLoading = true; try { - await this.restApi().updateWorkflow(this.$route.params.name, data); + await this.restApi().updateWorkflow(this.workflowId, data); } catch (error) { this.$showError( error, diff --git a/packages/editor-ui/src/components/mixins/workflowHelpers.ts b/packages/editor-ui/src/components/mixins/workflowHelpers.ts index 8fc15819fa461..aac84332e654a 100644 --- a/packages/editor-ui/src/components/mixins/workflowHelpers.ts +++ b/packages/editor-ui/src/components/mixins/workflowHelpers.ts @@ -630,8 +630,8 @@ export const workflowHelpers = mixins( } }, - async saveCurrentWorkflow({name, tags}: {name?: string, tags?: string[]} = {}, redirect = true): Promise { - const currentWorkflow = this.$route.params.name; + async saveCurrentWorkflow({id, name, tags}: {id?: string, name?: string, tags?: string[]} = {}, redirect = true): Promise { + const currentWorkflow = id || this.$route.params.name; if (!currentWorkflow) { return this.saveAsNewWorkflow({name, tags}, redirect); } diff --git a/packages/editor-ui/src/router.ts b/packages/editor-ui/src/router.ts index 4390dd419c176..1091338f78ff1 100644 --- a/packages/editor-ui/src/router.ts +++ b/packages/editor-ui/src/router.ts @@ -131,7 +131,8 @@ const router = new Router({ sidebar: MainSidebar, }, meta: { - keepWorkflowDirty: true, + // Switching to this route with this flag will not change current WF data that's in the store + keepWorkflowAlive: true, permissions: { allow: { loginStatus: [LOGIN_STATUS.LoggedIn], diff --git a/packages/editor-ui/src/views/NodeView.vue b/packages/editor-ui/src/views/NodeView.vue index 74be5f4e6d594..434b145896738 100644 --- a/packages/editor-ui/src/views/NodeView.vue +++ b/packages/editor-ui/src/views/NodeView.vue @@ -277,7 +277,7 @@ export default mixins( watch: { // Listen to route changes and load the workflow accordingly '$route' (to, from) { - const checkDirty = from.meta && from.meta.keepWorkflowDirty !== true; + const checkDirty = from.meta && from.meta.keepWorkflowAlive !== true; this.initView(checkDirty); }, activeNode () { @@ -307,7 +307,7 @@ export default mixins( }, async beforeRouteLeave(to, from, next) { - if (to.meta && to.meta.keepWorkflowDirty === true) { + if (to.meta && to.meta.keepWorkflowAlive === true) { next(); return; } @@ -2113,7 +2113,7 @@ export default mixins( const executionId = this.$route.params.id; await this.openExecution(executionId); } else { - if ((this.$route.meta && this.$route.meta.keepWorkflowDirty !== true) && checkDirty) { + if ((this.$route.meta && this.$route.meta.keepWorkflowAlive !== true) && checkDirty) { const result = this.$store.getters.getStateIsDirty; if(result) { const confirmModal = await this.confirmModal( From d534e7c93b5055efc6ff2a35417410a47a6ff695 Mon Sep 17 00:00:00 2001 From: Milorad Filipovic Date: Wed, 28 Sep 2022 15:38:49 +0200 Subject: [PATCH 05/25] =?UTF-8?q?=F0=9F=90=9B=20Fixing=20a=20bug=20with=20?= =?UTF-8?q?previous=20WF=20executions=20still=20visible=20after=20creating?= =?UTF-8?q?=20a=20new=20WF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/MainHeader/MainHeader.vue | 2 ++ .../editor-ui/src/components/MainSidebar.vue | 3 ++ .../editor-ui/src/views/ExecutionsView.vue | 28 +++++++++++++++++-- 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/packages/editor-ui/src/components/MainHeader/MainHeader.vue b/packages/editor-ui/src/components/MainHeader/MainHeader.vue index 4b9d045a32f6b..7c5cd660d9fb8 100644 --- a/packages/editor-ui/src/components/MainHeader/MainHeader.vue +++ b/packages/editor-ui/src/components/MainHeader/MainHeader.vue @@ -89,6 +89,8 @@ export default mixins( if (value !== PLACEHOLDER_EMPTY_WORKFLOW_ID) { this.workflowToReturnTo = value; } + } else if (!this.currentWorkflow || this.currentWorkflow === PLACEHOLDER_EMPTY_WORKFLOW_ID) { + this.$store.commit('setCurrentWorkflowExecutions', []); } }, }, diff --git a/packages/editor-ui/src/components/MainSidebar.vue b/packages/editor-ui/src/components/MainSidebar.vue index 7c7bcb18de10a..f9e26be46321f 100644 --- a/packages/editor-ui/src/components/MainSidebar.vue +++ b/packages/editor-ui/src/components/MainSidebar.vue @@ -161,6 +161,7 @@ import { EXECUTIONS_MODAL_KEY, VIEWS, WORKFLOW_OPEN_MODAL_KEY, + PLACEHOLDER_EMPTY_WORKFLOW_ID, } from '@/constants'; import { userHelpers } from './mixins/userHelpers'; import { debounceHelper } from './mixins/debounce'; @@ -392,6 +393,7 @@ export default mixins( if (this.$router.currentRoute.name === VIEWS.NEW_WORKFLOW) { this.$root.$emit('newWorkflow'); } else { + this.$store.commit('setWorkflowId', PLACEHOLDER_EMPTY_WORKFLOW_ID); this.$router.push({ name: VIEWS.NEW_WORKFLOW }); } this.$showMessage({ @@ -403,6 +405,7 @@ export default mixins( } } else { if (this.$router.currentRoute.name !== VIEWS.NEW_WORKFLOW) { + this.$store.commit('setWorkflowId', PLACEHOLDER_EMPTY_WORKFLOW_ID); this.$router.push({ name: VIEWS.NEW_WORKFLOW }); } this.$showMessage({ diff --git a/packages/editor-ui/src/views/ExecutionsView.vue b/packages/editor-ui/src/views/ExecutionsView.vue index 9960016deb0e9..262e19ff25898 100644 --- a/packages/editor-ui/src/views/ExecutionsView.vue +++ b/packages/editor-ui/src/views/ExecutionsView.vue @@ -1,6 +1,14 @@ @@ -11,3 +19,19 @@ export default Vue.extend({ name: 'executions-view', }); + + From 3a4fe340e3ce70a3921150ce315b095ef26eb9b2 Mon Sep 17 00:00:00 2001 From: Milorad Filipovic Date: Wed, 28 Sep 2022 15:55:33 +0200 Subject: [PATCH 06/25] =?UTF-8?q?=E2=9A=A1=20Updating=20saved=20status=20w?= =?UTF-8?q?hen=20new=20WF=20is=20created?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../editor-ui/src/components/MainHeader/WorkflowDetails.vue | 3 ++- packages/editor-ui/src/views/NodeView.vue | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/editor-ui/src/components/MainHeader/WorkflowDetails.vue b/packages/editor-ui/src/components/MainHeader/WorkflowDetails.vue index 261534cf9ab35..42f5839c0cfa0 100644 --- a/packages/editor-ui/src/components/MainHeader/WorkflowDetails.vue +++ b/packages/editor-ui/src/components/MainHeader/WorkflowDetails.vue @@ -86,6 +86,7 @@ import { mapGetters } from "vuex"; import { DUPLICATE_MODAL_KEY, MAX_WORKFLOW_NAME_LENGTH, + PLACEHOLDER_EMPTY_WORKFLOW_ID, VIEWS, WORKFLOW_MENU_ACTIONS, WORKFLOW_SETTINGS_MODAL_KEY, } from "@/constants"; @@ -145,7 +146,7 @@ export default mixins(workflowHelpers, titleChange).extend({ }), ...mapGetters('settings', ['areTagsEnabled']), isNewWorkflow(): boolean { - return !this.currentWorkflowId; + return !this.currentWorkflowId || this.currentWorkflowId === PLACEHOLDER_EMPTY_WORKFLOW_ID; }, isWorkflowSaving(): boolean { return this.$store.getters.isActionActive("workflowSaving"); diff --git a/packages/editor-ui/src/views/NodeView.vue b/packages/editor-ui/src/views/NodeView.vue index 434b145896738..59532fa678bf4 100644 --- a/packages/editor-ui/src/views/NodeView.vue +++ b/packages/editor-ui/src/views/NodeView.vue @@ -2112,6 +2112,8 @@ export default mixins( // Load an execution const executionId = this.$route.params.id; await this.openExecution(executionId); + } else if (this.$route.name === VIEWS.NEW_WORKFLOW) { + await this.newWorkflow(); } else { if ((this.$route.meta && this.$route.meta.keepWorkflowAlive !== true) && checkDirty) { const result = this.$store.getters.getStateIsDirty; From 5e52308c3624596042a7d0d2d64486a578912bf2 Mon Sep 17 00:00:00 2001 From: Milorad Filipovic Date: Thu, 29 Sep 2022 16:52:51 +0200 Subject: [PATCH 07/25] =?UTF-8?q?=E2=9C=A8=20Implemented=20initial=20versi?= =?UTF-8?q?on=20of=20execution=20perview?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/editor-ui/src/App.vue | 14 +-- .../ExecutionsView/ExecutionsSidebar.vue | 87 ++++++++++++++++ .../src/components/MainHeader/MainHeader.vue | 35 +------ packages/editor-ui/src/plugins/icons.ts | 2 + packages/editor-ui/src/router.ts | 40 ++++---- .../editor-ui/src/views/ExecutionsView.vue | 37 ------- packages/editor-ui/src/views/NodeView.vue | 98 +++++++++++-------- 7 files changed, 174 insertions(+), 139 deletions(-) create mode 100644 packages/editor-ui/src/components/ExecutionsView/ExecutionsSidebar.vue delete mode 100644 packages/editor-ui/src/views/ExecutionsView.vue diff --git a/packages/editor-ui/src/App.vue b/packages/editor-ui/src/App.vue index 2daed0bd30097..e97f6c82a8d72 100644 --- a/packages/editor-ui/src/App.vue +++ b/packages/editor-ui/src/App.vue @@ -15,11 +15,9 @@ -
- - - -
+ + + @@ -204,12 +202,6 @@ export default mixins( grid-template-rows: fit-content($sidebar-width) 1fr; } -.content { - grid-area: content; - overflow: auto; - height: 100vh; -} - .header { grid-area: header; z-index: 999; diff --git a/packages/editor-ui/src/components/ExecutionsView/ExecutionsSidebar.vue b/packages/editor-ui/src/components/ExecutionsView/ExecutionsSidebar.vue new file mode 100644 index 0000000000000..663fcfebee164 --- /dev/null +++ b/packages/editor-ui/src/components/ExecutionsView/ExecutionsSidebar.vue @@ -0,0 +1,87 @@ + + + + + diff --git a/packages/editor-ui/src/components/MainHeader/MainHeader.vue b/packages/editor-ui/src/components/MainHeader/MainHeader.vue index 7c5cd660d9fb8..1accf390015ac 100644 --- a/packages/editor-ui/src/components/MainHeader/MainHeader.vue +++ b/packages/editor-ui/src/components/MainHeader/MainHeader.vue @@ -18,14 +18,13 @@ import WorkflowDetails from '@/components/MainHeader/WorkflowDetails.vue'; import ExecutionDetails from '@/components/MainHeader/ExecutionDetails/ExecutionDetails.vue'; import TabBar from '@/components/MainHeader/TabBar.vue'; import { MAIN_HEADER_TABS, PLACEHOLDER_EMPTY_WORKFLOW_ID, STICKY_NODE_TYPE, VIEWS, WORKFLOW_SETTINGS_MODAL_KEY } from '@/constants'; -import { IExecutionsSummary, INodeUi, ITabBarItem } from '@/Interface'; +import { INodeUi, ITabBarItem } from '@/Interface'; import { workflowHelpers } from '../mixins/workflowHelpers'; export default mixins( pushConnection, workflowHelpers, -) - .extend({ +).extend({ name: 'MainHeader', components: { WorkflowDetails, @@ -45,8 +44,7 @@ export default mixins( tabBarItems(): ITabBarItem[] { return [ { id: MAIN_HEADER_TABS.WORKFLOW, label: 'Workflow' }, - { id: MAIN_HEADER_TABS.EXECUTIONS, label: 'Executions', notifications: this.currentWorkflow ? this.executions.length : 0 }, - { id: MAIN_HEADER_TABS.SETTINGS, label: 'Settings', disabled: !this.onWorkflowPage || !this.currentWorkflow || this.currentWorkflow === PLACEHOLDER_EMPTY_WORKFLOW_ID }, + { id: MAIN_HEADER_TABS.EXECUTIONS, label: 'Executions' }, ]; }, isExecutionPage (): boolean { @@ -67,12 +65,8 @@ export default mixins( onWorkflowPage(): boolean { return this.$route.meta && (this.$route.meta.nodeView || this.$route.meta.keepWorkflowAlive === true); }, - executions(): IExecutionsSummary[] { - return this.$store.getters.currentWorkflowExecutions; - }, }, async mounted() { - this.loadExecutions(this.currentWorkflow); if (this.$route.path === `/${MAIN_HEADER_TABS.EXECUTIONS}`) { this.activeHeaderTab = MAIN_HEADER_TABS.EXECUTIONS; } @@ -85,12 +79,9 @@ export default mixins( watch: { "$route.params.name"(value) { if (value) { - this.loadExecutions(value); if (value !== PLACEHOLDER_EMPTY_WORKFLOW_ID) { this.workflowToReturnTo = value; } - } else if (!this.currentWorkflow || this.currentWorkflow === PLACEHOLDER_EMPTY_WORKFLOW_ID) { - this.$store.commit('setCurrentWorkflowExecutions', []); } }, }, @@ -110,32 +101,14 @@ export default mixins( break; case MAIN_HEADER_TABS.EXECUTIONS: this.workflowToReturnTo = this.currentWorkflow; - this.$router.push({ - name: VIEWS.EXECUTIONS, - }); + this.$router.push({ name: VIEWS.EXECUTIONS }); // this.modalBus.$emit('closeAll'); this.activeHeaderTab = MAIN_HEADER_TABS.EXECUTIONS; break; - case MAIN_HEADER_TABS.SETTINGS: - this.$store.dispatch('ui/openModal', WORKFLOW_SETTINGS_MODAL_KEY); - break; default: break; } }, - async loadExecutions (workflowId: string): Promise { - if (!this.currentWorkflow) { - return; - } - try { - await this.$store.dispatch('loadCurrentWorkflowActions', workflowId); - } catch (error) { - this.$showError( - error, - this.$locale.baseText('executionsList.showError.refreshData.title'), - ); - } - }, }, }); diff --git a/packages/editor-ui/src/plugins/icons.ts b/packages/editor-ui/src/plugins/icons.ts index a6a2cb1b984ae..91496cd4e9012 100644 --- a/packages/editor-ui/src/plugins/icons.ts +++ b/packages/editor-ui/src/plugins/icons.ts @@ -55,6 +55,7 @@ import { faGift, faGraduationCap, faGripVertical, + faHandPointLeft, faHdd, faHome, faHourglass, @@ -172,6 +173,7 @@ addIcon(faFolderOpen); addIcon(faGift); addIcon(faGlobeAmericas); addIcon(faGraduationCap); +addIcon(faHandPointLeft); addIcon(faHdd); addIcon(faHome); addIcon(faHourglass); diff --git a/packages/editor-ui/src/router.ts b/packages/editor-ui/src/router.ts index 1091338f78ff1..51032e4823928 100644 --- a/packages/editor-ui/src/router.ts +++ b/packages/editor-ui/src/router.ts @@ -6,7 +6,7 @@ import ForgotMyPasswordView from './views/ForgotMyPasswordView.vue'; import MainHeader from '@/components/MainHeader/MainHeader.vue'; import MainSidebar from '@/components/MainSidebar.vue'; import NodeView from '@/views/NodeView.vue'; -import ExecutionsView from '@/views/ExecutionsView.vue'; +import ExecutionsSidebar from '@/components/ExecutionsView/ExecutionsSidebar.vue'; import SettingsPersonalView from './views/SettingsPersonalView.vue'; import SettingsUsersView from './views/SettingsUsersView.vue'; import SettingsCommunityNodesView from './views/SettingsCommunityNodesView.vue'; @@ -22,12 +22,11 @@ import TemplatesWorkflowView from '@/views/TemplatesWorkflowView.vue'; import TemplatesSearchView from '@/views/TemplatesSearchView.vue'; import CredentialsView from '@/views/CredentialsView.vue'; import { Store } from 'vuex'; -import { IPermissions, IRootState, IWorkflowsState } from './Interface'; +import { IPermissions, IRootState } from './Interface'; import { LOGIN_STATUS, ROLE } from './modules/userHelpers'; import { RouteConfigSingleView } from 'vue-router/types/router'; import { VIEWS } from './constants'; import { store } from './store'; -import e from 'express'; Vue.use(Router); @@ -122,24 +121,6 @@ const router = new Router({ }, }, }, - { - path: '/executions/', - name: VIEWS.EXECUTIONS, - components: { - default: ExecutionsView, - header: MainHeader, - sidebar: MainSidebar, - }, - meta: { - // Switching to this route with this flag will not change current WF data that's in the store - keepWorkflowAlive: true, - permissions: { - allow: { - loginStatus: [LOGIN_STATUS.LoggedIn], - }, - }, - }, - }, { path: '/templates/:id', name: VIEWS.TEMPLATE, @@ -242,6 +223,23 @@ const router = new Router({ }, }, }, + children: [ + { + path: 'executions/:executionId?', + name: VIEWS.EXECUTIONS, + components: { + 'executionsSidebar': ExecutionsSidebar, + }, + meta: { + nodeView: true, + permissions: { + allow: { + loginStatus: [LOGIN_STATUS.LoggedIn], + }, + }, + }, + }, + ], }, { path: '/workflows/demo', diff --git a/packages/editor-ui/src/views/ExecutionsView.vue b/packages/editor-ui/src/views/ExecutionsView.vue deleted file mode 100644 index 262e19ff25898..0000000000000 --- a/packages/editor-ui/src/views/ExecutionsView.vue +++ /dev/null @@ -1,37 +0,0 @@ - - - - - diff --git a/packages/editor-ui/src/views/NodeView.vue b/packages/editor-ui/src/views/NodeView.vue index 59532fa678bf4..efc5549ee10e9 100644 --- a/packages/editor-ui/src/views/NodeView.vue +++ b/packages/editor-ui/src/views/NodeView.vue @@ -1,4 +1,6 @@ + + diff --git a/packages/editor-ui/src/views/NodeView.vue b/packages/editor-ui/src/views/NodeView.vue index 39fa5919b12eb..3ec5f536a2b94 100644 --- a/packages/editor-ui/src/views/NodeView.vue +++ b/packages/editor-ui/src/views/NodeView.vue @@ -6,6 +6,7 @@ @dragover="onDragOver" @drop="onDrop" > +
.content { + position: relative; display: flex; overflow: auto; height: 100vh; From e6957ecbb96e9c1a68c43b9b5f7606dc74213300 Mon Sep 17 00:00:00 2001 From: Milorad Filipovic Date: Fri, 30 Sep 2022 11:59:17 +0200 Subject: [PATCH 10/25] =?UTF-8?q?=E2=9C=A8=20Simplifying=20node=20view=20n?= =?UTF-8?q?avigation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/MainHeader/MainHeader.vue | 26 ++++++++++++------- packages/editor-ui/src/router.ts | 1 - packages/editor-ui/src/views/NodeView.vue | 10 ++++--- 3 files changed, 22 insertions(+), 15 deletions(-) diff --git a/packages/editor-ui/src/components/MainHeader/MainHeader.vue b/packages/editor-ui/src/components/MainHeader/MainHeader.vue index 1accf390015ac..c84b8f6be9fb6 100644 --- a/packages/editor-ui/src/components/MainHeader/MainHeader.vue +++ b/packages/editor-ui/src/components/MainHeader/MainHeader.vue @@ -4,7 +4,7 @@
- +
@@ -20,6 +20,7 @@ import TabBar from '@/components/MainHeader/TabBar.vue'; import { MAIN_HEADER_TABS, PLACEHOLDER_EMPTY_WORKFLOW_ID, STICKY_NODE_TYPE, VIEWS, WORKFLOW_SETTINGS_MODAL_KEY } from '@/constants'; import { INodeUi, ITabBarItem } from '@/Interface'; import { workflowHelpers } from '../mixins/workflowHelpers'; +import { Route } from 'vue-router'; export default mixins( pushConnection, @@ -67,9 +68,7 @@ export default mixins( }, }, async mounted() { - if (this.$route.path === `/${MAIN_HEADER_TABS.EXECUTIONS}`) { - this.activeHeaderTab = MAIN_HEADER_TABS.EXECUTIONS; - } + this.syncTabsWithRoute(this.$route); // Initialize the push connection this.pushConnect(); }, @@ -77,15 +76,22 @@ export default mixins( this.pushDisconnect(); }, watch: { - "$route.params.name"(value) { - if (value) { - if (value !== PLACEHOLDER_EMPTY_WORKFLOW_ID) { - this.workflowToReturnTo = value; - } - } + $route (to, from){ + this.syncTabsWithRoute(to); }, }, methods: { + syncTabsWithRoute(route: Route): void { + if (route.name === VIEWS.EXECUTIONS || route.name === VIEWS.EXECUTION_PREVIEW) { + this.activeHeaderTab = MAIN_HEADER_TABS.EXECUTIONS; + } else if (route.name === VIEWS.WORKFLOW) { + this.activeHeaderTab = MAIN_HEADER_TABS.WORKFLOW; + } + const workflowName = route.params.name; + if (workflowName) { + this.workflowToReturnTo = workflowName; + } + }, onTabSelected(tab: string, event: MouseEvent) { switch (tab) { case MAIN_HEADER_TABS.WORKFLOW: diff --git a/packages/editor-ui/src/router.ts b/packages/editor-ui/src/router.ts index 4bc5f14558c38..adb98968d1015 100644 --- a/packages/editor-ui/src/router.ts +++ b/packages/editor-ui/src/router.ts @@ -232,7 +232,6 @@ const router = new Router({ }, meta: { nodeView: true, - keepWorkflowAlive: true, permissions: { allow: { loginStatus: [LOGIN_STATUS.LoggedIn] }, }, diff --git a/packages/editor-ui/src/views/NodeView.vue b/packages/editor-ui/src/views/NodeView.vue index 3ec5f536a2b94..56a0f86ea36a1 100644 --- a/packages/editor-ui/src/views/NodeView.vue +++ b/packages/editor-ui/src/views/NodeView.vue @@ -278,8 +278,10 @@ export default mixins( watch: { // Listen to route changes and load the workflow accordingly '$route' (to, from) { - const checkDirty = from.meta && from.meta.keepWorkflowAlive !== true; - this.initView(checkDirty); + const workflowsChanged: boolean = + ((to.params.name && from.params.name) && (to.params.name !== from.params.name)) || + (from.params.executionId !== undefined && to.params.name !== undefined); + this.initView(workflowsChanged); }, activeNode () { // When a node gets set as active deactivate the create-menu @@ -2121,7 +2123,7 @@ export default mixins( } else if (this.$route.name === VIEWS.EXECUTION_PREVIEW) { await this.openExecution(this.$route.params.executionId); } else { - if ((this.$route.meta && this.$route.meta.keepWorkflowAlive !== true) && checkDirty) { + if (checkDirty) { // Check if there is current workflow execution that need to be opened const result = this.$store.getters.getStateIsDirty; if(result) { @@ -3160,7 +3162,7 @@ export default mixins( this.instance.ready(async () => { try { this.initNodeView(); - await this.initView(); + await this.initView(true); if (window.top) { window.top.postMessage(JSON.stringify({command: 'n8nReady',version:this.$store.getters.versionCli}), '*'); } From c71c4294e646b279676c38a6822ae72286b845c8 Mon Sep 17 00:00:00 2001 From: Milorad Filipovic Date: Fri, 30 Sep 2022 12:30:01 +0200 Subject: [PATCH 11/25] =?UTF-8?q?=E2=9C=A8=20Updating=20executions=20view?= =?UTF-8?q?=20zoom=20and=20selection=20to=20work=20with=20the=20new=20layo?= =?UTF-8?q?ut?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/ExecutionsView/ExecutionsSidebar.vue | 1 + .../src/components/MainHeader/MainHeader.vue | 2 +- .../editor-ui/src/components/mixins/mouseSelect.ts | 7 +++++-- packages/editor-ui/src/views/NodeView.vue | 12 +++++------- packages/editor-ui/src/views/canvasHelpers.ts | 8 ++++---- 5 files changed, 16 insertions(+), 14 deletions(-) diff --git a/packages/editor-ui/src/components/ExecutionsView/ExecutionsSidebar.vue b/packages/editor-ui/src/components/ExecutionsView/ExecutionsSidebar.vue index 7c011610ef37c..e702d7059250b 100644 --- a/packages/editor-ui/src/components/ExecutionsView/ExecutionsSidebar.vue +++ b/packages/editor-ui/src/components/ExecutionsView/ExecutionsSidebar.vue @@ -68,6 +68,7 @@ export default mixins(workflowHelpers).extend({ background-color: var(--color-background-xlight); border-right: var(--border-base); padding: var(--spacing-l) var(--spacing-2xs) var(--spacing-2xs) var(--spacing-l); + z-index: 1; } .executionList { diff --git a/packages/editor-ui/src/components/MainHeader/MainHeader.vue b/packages/editor-ui/src/components/MainHeader/MainHeader.vue index c84b8f6be9fb6..c7368c40738a9 100644 --- a/packages/editor-ui/src/components/MainHeader/MainHeader.vue +++ b/packages/editor-ui/src/components/MainHeader/MainHeader.vue @@ -45,7 +45,7 @@ export default mixins( tabBarItems(): ITabBarItem[] { return [ { id: MAIN_HEADER_TABS.WORKFLOW, label: 'Workflow' }, - { id: MAIN_HEADER_TABS.EXECUTIONS, label: 'Executions' }, + { id: MAIN_HEADER_TABS.EXECUTIONS, label: 'Executions', disabled: this.$route.name === VIEWS.NEW_WORKFLOW }, ]; }, isExecutionPage (): boolean { diff --git a/packages/editor-ui/src/components/mixins/mouseSelect.ts b/packages/editor-ui/src/components/mixins/mouseSelect.ts index dcee43e1179fc..cde36acedac8b 100644 --- a/packages/editor-ui/src/components/mixins/mouseSelect.ts +++ b/packages/editor-ui/src/components/mixins/mouseSelect.ts @@ -3,7 +3,7 @@ import { INodeUi, XYPosition } from '@/Interface'; import mixins from 'vue-typed-mixins'; import { deviceSupportHelpers } from '@/components/mixins/deviceSupportHelpers'; -import { getMousePosition, getRelativePosition, HEADER_HEIGHT, SIDEBAR_WIDTH, SIDEBAR_WIDTH_EXPANDED } from '@/views/canvasHelpers'; +import { getMousePosition, getRelativePosition, HEADER_HEIGHT, INNER_SIDEBAR_WIDTH, SIDEBAR_WIDTH, SIDEBAR_WIDTH_EXPANDED } from '@/views/canvasHelpers'; import { VIEWS } from '@/constants'; export const mouseSelect = mixins( @@ -49,8 +49,11 @@ export const mouseSelect = mixins( }, getMousePositionWithinNodeView (event: MouseEvent | TouchEvent): XYPosition { const [x, y] = getMousePosition(event); - const sidebarOffset = this.isDemo ? 0 : this.$store.getters['ui/sidebarMenuCollapsed'] ? SIDEBAR_WIDTH : SIDEBAR_WIDTH_EXPANDED; const headerOffset = this.isDemo ? 0 : HEADER_HEIGHT; + let sidebarOffset = this.isDemo ? 0 : this.$store.getters['ui/sidebarMenuCollapsed'] ? SIDEBAR_WIDTH : SIDEBAR_WIDTH_EXPANDED; + // If on executions tab, account for the inner sidebar + const onExecutionsTab = this.$route.name === VIEWS.EXECUTIONS || this.$route.name === VIEWS.EXECUTION_PREVIEW; + sidebarOffset += onExecutionsTab ? INNER_SIDEBAR_WIDTH : 0; // @ts-ignore return getRelativePosition(x - sidebarOffset, y - headerOffset, this.nodeViewScale, this.$store.getters.getNodeViewOffsetPosition); }, diff --git a/packages/editor-ui/src/views/NodeView.vue b/packages/editor-ui/src/views/NodeView.vue index 56a0f86ea36a1..8775fb55c6f0a 100644 --- a/packages/editor-ui/src/views/NodeView.vue +++ b/packages/editor-ui/src/views/NodeView.vue @@ -3,6 +3,7 @@
@@ -1193,7 +1194,7 @@ export default mixins( return; } - const {zoomLevel, offset} = CanvasHelpers.getZoomToFit(nodes, !this.isDemo); + const {zoomLevel, offset} = CanvasHelpers.getZoomToFit(nodes, !this.isDemo, 310); this.setZoomLevel(zoomLevel); this.$store.commit('setNodeViewOffsetPosition', {newOffset: offset}); @@ -3236,18 +3237,15 @@ export default mixins( .zoom-menu { $--zoom-menu-margin: 15; - position: fixed; + position: absolute; left: $sidebar-width + $--zoom-menu-margin; width: 210px; - bottom: 44px; + bottom: 108px; + left: 35px; line-height: 25px; color: #444; padding-right: 5px; - &.expanded { - left: $sidebar-expanded-width + $--zoom-menu-margin; - } - button { border: var(--border-base); } diff --git a/packages/editor-ui/src/views/canvasHelpers.ts b/packages/editor-ui/src/views/canvasHelpers.ts index 17c69d514de19..c8c400b5faee3 100644 --- a/packages/editor-ui/src/views/canvasHelpers.ts +++ b/packages/editor-ui/src/views/canvasHelpers.ts @@ -617,10 +617,10 @@ export const addConnectionOutputSuccess = (connection: Connection, output: {tota const getContentDimensions = (): { editorWidth: number, editorHeight: number } => { let contentWidth = window.innerWidth; let contentHeight = window.innerHeight; - const contentElement = document.getElementById('content'); + const nodeViewRoot = document.getElementById('node-view-root'); - if (contentElement) { - const contentBounds = contentElement.getBoundingClientRect(); + if (nodeViewRoot) { + const contentBounds = nodeViewRoot.getBoundingClientRect(); contentWidth = contentBounds.width; contentHeight = contentBounds.height; } @@ -630,7 +630,7 @@ const getContentDimensions = (): { editorWidth: number, editorHeight: number } = }; }; -export const getZoomToFit = (nodes: INodeUi[], addComponentPadding = true): {offset: XYPosition, zoomLevel: number} => { +export const getZoomToFit = (nodes: INodeUi[], addComponentPadding = true, additionalSidebarOffset = 0): {offset: XYPosition, zoomLevel: number} => { const {minX, minY, maxX, maxY} = getWorkflowCorners(nodes); const { editorWidth, editorHeight } = getContentDimensions(); const sidebarWidth = addComponentPadding ? SIDEBAR_WIDTH : 0; From 99b211671dcc96c131da5b24a3710dd80e462a35 Mon Sep 17 00:00:00 2001 From: Milorad Filipovic Date: Fri, 30 Sep 2022 13:37:11 +0200 Subject: [PATCH 12/25] =?UTF-8?q?=E2=9C=A8=20Using=20N8nRadioButtons=20com?= =?UTF-8?q?ponent=20for=20main=20header=20tabs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../N8nRadioButtons/RadioButton.vue | 1 + .../N8nRadioButtons/RadioButtons.vue | 10 +-- packages/editor-ui/src/Interface.ts | 3 +- .../src/components/MainHeader/MainHeader.vue | 6 +- .../src/components/MainHeader/TabBar.vue | 64 ++----------------- packages/editor-ui/src/views/NodeView.vue | 1 + packages/editor-ui/src/views/canvasHelpers.ts | 1 + 7 files changed, 17 insertions(+), 69 deletions(-) diff --git a/packages/design-system/src/components/N8nRadioButtons/RadioButton.vue b/packages/design-system/src/components/N8nRadioButtons/RadioButton.vue index 31e9b5c472af5..9ae70d99a91dd 100644 --- a/packages/design-system/src/components/N8nRadioButtons/RadioButton.vue +++ b/packages/design-system/src/components/N8nRadioButtons/RadioButton.vue @@ -65,6 +65,7 @@ export default Vue.extend({ color: var(--color-text-base); transition: background-color 0.2s ease; cursor: pointer; + user-select: none; } .disabled { diff --git a/packages/design-system/src/components/N8nRadioButtons/RadioButtons.vue b/packages/design-system/src/components/N8nRadioButtons/RadioButtons.vue index 4b07c67662fb4..4a1d794b58d06 100644 --- a/packages/design-system/src/components/N8nRadioButtons/RadioButtons.vue +++ b/packages/design-system/src/components/N8nRadioButtons/RadioButtons.vue @@ -6,8 +6,8 @@ v-bind="option" :active="value === option.value" :size="size" - :disabled="disabled" - @click="(e) => onClick(option.value, e)" + :disabled="disabled || option.disabled" + @click="(e) => onClick(option, e)" />
@@ -36,11 +36,11 @@ export default Vue.extend({ RadioButton, }, methods: { - onClick(value: unknown) { - if (this.disabled) { + onClick(option: {label: string, value: string, disabled?: boolean}) { + if (this.disabled || option.disabled) { return; } - this.$emit('input', value); + this.$emit('input', option.value); }, }, }); diff --git a/packages/editor-ui/src/Interface.ts b/packages/editor-ui/src/Interface.ts index c5aecbcb78798..087bad3df2072 100644 --- a/packages/editor-ui/src/Interface.ts +++ b/packages/editor-ui/src/Interface.ts @@ -1087,9 +1087,8 @@ export interface ITab { } export interface ITabBarItem { - id: string, + value: string, label: string, - notifications?: number, disabled?: boolean, }; diff --git a/packages/editor-ui/src/components/MainHeader/MainHeader.vue b/packages/editor-ui/src/components/MainHeader/MainHeader.vue index c7368c40738a9..a7c107f822c50 100644 --- a/packages/editor-ui/src/components/MainHeader/MainHeader.vue +++ b/packages/editor-ui/src/components/MainHeader/MainHeader.vue @@ -44,8 +44,8 @@ export default mixins( ]), tabBarItems(): ITabBarItem[] { return [ - { id: MAIN_HEADER_TABS.WORKFLOW, label: 'Workflow' }, - { id: MAIN_HEADER_TABS.EXECUTIONS, label: 'Executions', disabled: this.$route.name === VIEWS.NEW_WORKFLOW }, + { value: MAIN_HEADER_TABS.WORKFLOW, label: 'Workflow' }, + { value: MAIN_HEADER_TABS.EXECUTIONS, label: 'Executions', disabled: this.$route.name === VIEWS.NEW_WORKFLOW }, ]; }, isExecutionPage (): boolean { @@ -84,7 +84,7 @@ export default mixins( syncTabsWithRoute(route: Route): void { if (route.name === VIEWS.EXECUTIONS || route.name === VIEWS.EXECUTION_PREVIEW) { this.activeHeaderTab = MAIN_HEADER_TABS.EXECUTIONS; - } else if (route.name === VIEWS.WORKFLOW) { + } else if (route.name === VIEWS.WORKFLOW || route.name === VIEWS.NEW_WORKFLOW) { this.activeHeaderTab = MAIN_HEADER_TABS.WORKFLOW; } const workflowName = route.params.name; diff --git a/packages/editor-ui/src/components/MainHeader/TabBar.vue b/packages/editor-ui/src/components/MainHeader/TabBar.vue index a14dd5eaa1699..5bf56b6313606 100644 --- a/packages/editor-ui/src/components/MainHeader/TabBar.vue +++ b/packages/editor-ui/src/components/MainHeader/TabBar.vue @@ -1,29 +1,10 @@ @@ -71,44 +52,9 @@ export default Vue.extend({ border-radius: var(--border-radius-base) } -.tabButton { - background: none; - border: none; - color: var(--color-text-base); - font-weight: 600 !important; - - & + & { - margin-left: var(--spacing-2xs); - } - - &.active, &:active { - background-color: var(--color-background-xlight); - color: var(--text-color-dark); - - &:disabled { background: none; } - } - - &:focus { outline: none; } -} - -.notificationCount { - background-color: var(--color-success); - border-color: var(--color-success ); - & > span { font-size: var(--font-size-3xs); } -} - @media screen and (max-width: 430px) { .container { flex-direction: column; } - - .tabButton { - margin-left: 0 !important; - - & + & { - margin-top: var(--spacing-2xs); - } - } } - diff --git a/packages/editor-ui/src/views/NodeView.vue b/packages/editor-ui/src/views/NodeView.vue index 8775fb55c6f0a..a7b31e447ade1 100644 --- a/packages/editor-ui/src/views/NodeView.vue +++ b/packages/editor-ui/src/views/NodeView.vue @@ -280,6 +280,7 @@ export default mixins( // Listen to route changes and load the workflow accordingly '$route' (to, from) { const workflowsChanged: boolean = + (from.name === VIEWS.NEW_WORKFLOW || to.name === VIEWS.NEW_WORKFLOW) || ((to.params.name && from.params.name) && (to.params.name !== from.params.name)) || (from.params.executionId !== undefined && to.params.name !== undefined); this.initView(workflowsChanged); diff --git a/packages/editor-ui/src/views/canvasHelpers.ts b/packages/editor-ui/src/views/canvasHelpers.ts index c8c400b5faee3..4ec5aeee304a2 100644 --- a/packages/editor-ui/src/views/canvasHelpers.ts +++ b/packages/editor-ui/src/views/canvasHelpers.ts @@ -32,6 +32,7 @@ export const DEFAULT_START_POSITION_X = 175; export const DEFAULT_START_POSITION_Y = 235; export const HEADER_HEIGHT = 65; export const SIDEBAR_WIDTH = 65; +export const INNER_SIDEBAR_WIDTH = 310; export const SIDEBAR_WIDTH_EXPANDED = 200; export const MAX_X_TO_PUSH_DOWNSTREAM_NODES = 300; export const PUSH_NODES_OFFSET = NODE_SIZE * 2 + GRID_SIZE; From e531e037637bd6bcc596a1a4162660c800526bfa Mon Sep 17 00:00:00 2001 From: Milorad Filipovic Date: Fri, 30 Sep 2022 14:45:49 +0200 Subject: [PATCH 13/25] =?UTF-8?q?=F0=9F=92=84=20Implementing=20executions?= =?UTF-8?q?=20page=20states.=20Minor=20refactoring.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExecutionsView/ExecutionsLandingPage.vue | 24 +++++- .../ExecutionsView/ExecutionsSidebar.vue | 78 +++++++++++++++---- .../src/plugins/i18n/locales/en.json | 4 + packages/editor-ui/src/views/NodeView.vue | 2 +- 4 files changed, 88 insertions(+), 20 deletions(-) diff --git a/packages/editor-ui/src/components/ExecutionsView/ExecutionsLandingPage.vue b/packages/editor-ui/src/components/ExecutionsView/ExecutionsLandingPage.vue index 8cbabf60472e9..02bd455b04b8f 100644 --- a/packages/editor-ui/src/components/ExecutionsView/ExecutionsLandingPage.vue +++ b/packages/editor-ui/src/components/ExecutionsView/ExecutionsLandingPage.vue @@ -1,21 +1,38 @@ @@ -32,7 +49,6 @@ export default Vue.extend({ display: flex; flex-direction: column; align-items: center; - justify-content: center; } .messageContainer { @@ -40,7 +56,7 @@ export default Vue.extend({ flex-direction: column; align-items: center; text-align: center; - max-width: 200px; + margin-top: 20%; color: var(--color-text-base); } diff --git a/packages/editor-ui/src/components/ExecutionsView/ExecutionsSidebar.vue b/packages/editor-ui/src/components/ExecutionsView/ExecutionsSidebar.vue index e702d7059250b..94ae79678e5f9 100644 --- a/packages/editor-ui/src/components/ExecutionsView/ExecutionsSidebar.vue +++ b/packages/editor-ui/src/components/ExecutionsView/ExecutionsSidebar.vue @@ -1,10 +1,18 @@