+
+
+
+
+
+
+
+
+
+
+
+
@@ -51,8 +65,9 @@
diff --git a/packages/editor-ui/src/components/ValueSurvey.vue b/packages/editor-ui/src/components/ValueSurvey.vue
new file mode 100644
index 0000000000..c331795ce0
--- /dev/null
+++ b/packages/editor-ui/src/components/ValueSurvey.vue
@@ -0,0 +1,283 @@
+
+
+
+
+ {{ getTitle }}
+
+
+
+
+
+
+
+ Not likely
+ Very likely
+
+
+
+
+
+
+ David from our product team will get in touch personally
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/editor-ui/src/components/WorkflowActivator.vue b/packages/editor-ui/src/components/WorkflowActivator.vue
index 0e4a99e0a7..163683ce77 100644
--- a/packages/editor-ui/src/components/WorkflowActivator.vue
+++ b/packages/editor-ui/src/components/WorkflowActivator.vue
@@ -148,6 +148,7 @@ export default mixins(
this.$emit('workflowActiveChanged', { id: this.workflowId, active: newActiveState });
this.loading = false;
+ this.$store.dispatch('settings/fetchPromptsData');
},
async displayActivationError () {
let errorMessage: string;
diff --git a/packages/editor-ui/src/constants.ts b/packages/editor-ui/src/constants.ts
index cb0deef7de..2098a46e4e 100644
--- a/packages/editor-ui/src/constants.ts
+++ b/packages/editor-ui/src/constants.ts
@@ -26,6 +26,8 @@ export const CREDENTIAL_EDIT_MODAL_KEY = 'editCredential';
export const CREDENTIAL_SELECT_MODAL_KEY = 'selectCredential';
export const CREDENTIAL_LIST_MODAL_KEY = 'credentialsList';
export const PERSONALIZATION_MODAL_KEY = 'personalization';
+export const CONTACT_PROMPT_MODAL_KEY = 'contactPrompt';
+export const VALUE_SURVEY_MODAL_KEY = 'valueSurvey';
// breakpoints
export const BREAKPOINT_SM = 768;
@@ -132,3 +134,5 @@ export const CODING_SKILL_KEY = 'codingSkill';
export const OTHER_WORK_AREA_KEY = 'otherWorkArea';
export const OTHER_COMPANY_INDUSTRY_KEY = 'otherCompanyIndustry';
+export const VALID_EMAIL_REGEX = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
+
diff --git a/packages/editor-ui/src/modules/settings.ts b/packages/editor-ui/src/modules/settings.ts
index 22311ad077..a36aabe0dc 100644
--- a/packages/editor-ui/src/modules/settings.ts
+++ b/packages/editor-ui/src/modules/settings.ts
@@ -1,19 +1,22 @@
import { ActionContext, Module } from 'vuex';
import {
+ IN8nPrompts,
IN8nUISettings,
+ IN8nValueSurveyData,
IPersonalizationSurveyAnswers,
IRootState,
ISettingsState,
} from '../Interface';
-import { getSettings, submitPersonalizationSurvey } from '../api/settings';
+import { getPromptsData, getSettings, submitValueSurvey, submitPersonalizationSurvey, submitContactInfo } from '../api/settings';
import Vue from 'vue';
import { getPersonalizedNodeTypes } from './helper';
-import { PERSONALIZATION_MODAL_KEY } from '@/constants';
+import { CONTACT_PROMPT_MODAL_KEY, PERSONALIZATION_MODAL_KEY, VALUE_SURVEY_MODAL_KEY } from '@/constants';
const module: Module
= {
namespaced: true,
state: {
settings: {} as IN8nUISettings,
+ promptsData: {} as IN8nPrompts,
},
getters: {
personalizedNodeTypes(state: ISettingsState): string[] {
@@ -24,6 +27,9 @@ const module: Module = {
return getPersonalizedNodeTypes(answers);
},
+ getPromptsData(state: ISettingsState) {
+ return state.promptsData;
+ },
},
mutations: {
setSettings(state: ISettingsState, settings: IN8nUISettings) {
@@ -35,6 +41,9 @@ const module: Module = {
shouldShow: false,
});
},
+ setPromptsData(state: ISettingsState, promptsData: IN8nPrompts) {
+ Vue.set(state, 'promptsData', promptsData);
+ },
},
actions: {
async getSettings(context: ActionContext) {
@@ -70,6 +79,40 @@ const module: Module = {
context.commit('setPersonalizationAnswers', results);
},
+ async fetchPromptsData(context: ActionContext) {
+ if (!context.rootGetters.isTelemetryEnabled) {
+ return;
+ }
+
+ try {
+ const promptsData: IN8nPrompts = await getPromptsData(context.state.settings.instanceId);
+
+ if (promptsData && promptsData.showContactPrompt) {
+ context.commit('ui/openModal', CONTACT_PROMPT_MODAL_KEY, {root: true});
+ } else if (promptsData && promptsData.showValueSurvey) {
+ context.commit('ui/openModal', VALUE_SURVEY_MODAL_KEY, {root: true});
+ }
+
+ context.commit('setPromptsData', promptsData);
+ } catch (e) {
+ return e;
+ }
+
+ },
+ async submitContactInfo(context: ActionContext, email: string) {
+ try {
+ return await submitContactInfo(context.state.settings.instanceId, email);
+ } catch (e) {
+ return e;
+ }
+ },
+ async submitValueSurvey(context: ActionContext, params: IN8nValueSurveyData) {
+ try {
+ return await submitValueSurvey(context.state.settings.instanceId, params);
+ } catch (e) {
+ return e;
+ }
+ },
},
};
diff --git a/packages/editor-ui/src/modules/ui.ts b/packages/editor-ui/src/modules/ui.ts
index 81d380ae5d..7a5a25cd10 100644
--- a/packages/editor-ui/src/modules/ui.ts
+++ b/packages/editor-ui/src/modules/ui.ts
@@ -1,4 +1,4 @@
-import { CREDENTIAL_EDIT_MODAL_KEY, DUPLICATE_MODAL_KEY, PERSONALIZATION_MODAL_KEY, TAGS_MANAGER_MODAL_KEY, VERSIONS_MODAL_KEY, WORKFLOW_OPEN_MODAL_KEY, CREDENTIAL_SELECT_MODAL_KEY, WORKFLOW_SETTINGS_MODAL_KEY, CREDENTIAL_LIST_MODAL_KEY } from '@/constants';
+import { CONTACT_PROMPT_MODAL_KEY, CREDENTIAL_EDIT_MODAL_KEY, DUPLICATE_MODAL_KEY, PERSONALIZATION_MODAL_KEY, TAGS_MANAGER_MODAL_KEY, VERSIONS_MODAL_KEY, WORKFLOW_OPEN_MODAL_KEY, CREDENTIAL_SELECT_MODAL_KEY, WORKFLOW_SETTINGS_MODAL_KEY, CREDENTIAL_LIST_MODAL_KEY, VALUE_SURVEY_MODAL_KEY } from '@/constants';
import Vue from 'vue';
import { ActionContext, Module } from 'vuex';
import {
@@ -10,6 +10,9 @@ const module: Module = {
namespaced: true,
state: {
modals: {
+ [CONTACT_PROMPT_MODAL_KEY]: {
+ open: false,
+ },
[CREDENTIAL_EDIT_MODAL_KEY]: {
open: false,
mode: '',
@@ -33,6 +36,9 @@ const module: Module = {
[WORKFLOW_OPEN_MODAL_KEY]: {
open: false,
},
+ [VALUE_SURVEY_MODAL_KEY]: {
+ open: false,
+ },
[VERSIONS_MODAL_KEY]: {
open: false,
},
diff --git a/packages/editor-ui/src/plugins/components.ts b/packages/editor-ui/src/plugins/components.ts
index b1a3750150..24856be949 100644
--- a/packages/editor-ui/src/plugins/components.ts
+++ b/packages/editor-ui/src/plugins/components.ts
@@ -55,6 +55,7 @@ import {
N8nMenuItem,
N8nSelect,
N8nSpinner,
+ N8nSquareButton,
N8nText,
N8nTooltip,
N8nOption,
@@ -75,6 +76,7 @@ Vue.use(N8nMenu);
Vue.use(N8nMenuItem);
Vue.use(N8nSelect);
Vue.use(N8nSpinner);
+Vue.component('n8n-square-button', N8nSquareButton);
Vue.component('n8n-text', N8nText);
Vue.use(N8nTooltip);
Vue.use(N8nOption);
diff --git a/packages/editor-ui/src/store.ts b/packages/editor-ui/src/store.ts
index 743d99bf7a..b95f64048b 100644
--- a/packages/editor-ui/src/store.ts
+++ b/packages/editor-ui/src/store.ts
@@ -648,6 +648,10 @@ export const store = new Vuex.Store({
return state.workflow.id === PLACEHOLDER_EMPTY_WORKFLOW_ID;
},
+ isTelemetryEnabled: (state) => {
+ return state.telemetry && state.telemetry.enabled;
+ },
+
currentWorkflowHasWebhookNode: (state: IRootState): boolean => {
return !!state.workflow.nodes.find((node: INodeUi) => !!node.webhookId);
},
diff --git a/packages/editor-ui/src/views/NodeView.vue b/packages/editor-ui/src/views/NodeView.vue
index 1cb6fb78bd..3e1476e396 100644
--- a/packages/editor-ui/src/views/NodeView.vue
+++ b/packages/editor-ui/src/views/NodeView.vue
@@ -327,6 +327,10 @@ export default mixins(
this.$store.commit('setWorkflowExecutionData', null);
this.updateNodesExecutionIssues();
},
+ async onSaveKeyboardShortcut () {
+ const saved = await this.saveCurrentWorkflow();
+ if (saved) this.$store.dispatch('settings/fetchPromptsData');
+ },
openNodeCreator (source: string) {
this.createNodeActive = true;
this.$externalHooks().run('nodeView.createNodeActiveChanged', { source, createNodeActive: this.createNodeActive });
@@ -641,7 +645,7 @@ export default mixins(
return;
}
- this.callDebounced('saveCurrentWorkflow', 1000, undefined, true);
+ this.callDebounced('onSaveKeyboardShortcut', 1000);
} else if (e.key === 'Enter') {
// Activate the last selected node
const lastSelectedNode = this.lastSelectedNode;