feat(editor): Update to personalization survey v4 (#5474)

* feat(editor): update to personalization survey v4

* refactor: rename role other key for consistency

* feat: add reported source to survey

* test: add unit tests for personalization modal
This commit is contained in:
Alex Grozav 2023-02-15 15:05:55 +02:00 committed by GitHub
parent a3d8fac73a
commit 6265f3a27a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 667 additions and 76 deletions

View file

@ -512,7 +512,25 @@ export type IPersonalizationSurveyAnswersV3 = {
email?: string | null;
};
export type IPersonalizationLatestVersion = IPersonalizationSurveyAnswersV3;
export type IPersonalizationSurveyAnswersV4 = {
version: 'v4';
automationGoalDevops?: string[] | null;
automationGoalDevopsOther?: string | null;
companyIndustryExtended?: string[] | null;
otherCompanyIndustryExtended?: string[] | null;
companySize?: string | null;
companyType?: string | null;
automationGoalSm?: string[] | null;
automationGoalSmOther?: string | null;
usageModes?: string[] | null;
email?: string | null;
role?: string | null;
roleOther?: string | null;
reportedSource?: string | null;
reportedSourceOther?: string | null;
};
export type IPersonalizationLatestVersion = IPersonalizationSurveyAnswersV4;
export type IPersonalizationSurveyVersions =
| IPersonalizationSurveyAnswersV1

View file

@ -11,3 +11,11 @@ Vue.config.devtools = false;
// [Vue warn]: Failed to mount component: template or render function not defined.
Vue.component('vue-json-pretty', require('vue-json-pretty').default);
Vue.use((vue) => I18nPlugin(vue));
window.ResizeObserver =
window.ResizeObserver ||
vi.fn().mockImplementation(() => ({
disconnect: vi.fn(),
observe: vi.fn(),
unobserve: vi.fn(),
}));

View file

@ -0,0 +1,17 @@
export const retry = (assertion: () => any, { interval = 20, timeout = 200 } = {}) => {
return new Promise((resolve, reject) => {
const startTime = Date.now();
const tryAgain = () => {
setTimeout(() => {
try {
resolve(assertion());
} catch (err) {
Date.now() - startTime > timeout ? reject(err) : tryAgain();
}
}, interval);
};
tryAgain();
});
};

View file

@ -53,7 +53,7 @@
<script lang="ts">
import mixins from 'vue-typed-mixins';
const SURVEY_VERSION = 'v3';
const SURVEY_VERSION = 'v4';
import {
COMPANY_SIZE_100_499,
@ -78,19 +78,7 @@ import {
PHYSICAL_RETAIL_OR_SERVICES,
REAL_ESTATE_OR_CONSTRUCTION,
TELECOMS_INDUSTRY,
AUTOMATION_GOAL_KEY,
CUSTOMER_INTEGRATIONS_GOAL,
CUSTOMER_SUPPORT_GOAL,
ENGINEERING_GOAL,
FINANCE_ACCOUNTING_GOAL,
HR_GOAL,
OPERATIONS_GOAL,
PRODUCT_GOAL,
SALES_MARKETING_GOAL,
SECURITY_GOAL,
OTHER_AUTOMATION_GOAL,
NOT_SURE_YET_GOAL,
AUTOMATION_GOAL_OTHER_KEY,
COMPANY_TYPE_KEY,
EMAIL_KEY,
SAAS_COMPANY_TYPE,
@ -114,20 +102,46 @@ import {
MARKETING_AUTOMATION_DATA_SYNCHING,
MARKETING_AUTOMATION_OTHER,
OTHER_MARKETING_AUTOMATION_GOAL_KEY,
USAGE_MODE_KEY,
USAGE_MODE_MANIPULATE_FILES,
USAGE_MODE_BUILD_BE_SERVICES,
USAGE_MODE_CONNECT_TO_DB,
ROLE_KEY,
ROLE_BUSINESS_OWNER,
ROLE_CUSTOMER_SUPPORT,
ROLE_ENGINEERING,
ROLE_DATA_SCIENCE,
ROLE_DEVOPS,
ROLE_IT,
ROLE_SALES_AND_MARKETING,
ROLE_SECURITY,
ROLE_OTHER,
ROLE_OTHER_KEY,
DEVOPS_AUTOMATION_GOAL_OTHER_KEY,
DEVOPS_AUTOMATION_GOAL_KEY,
DEVOPS_AUTOMATION_OTHER,
DEVOPS_AUTOMATION_CI_CD_GOAL,
DEVOPS_AUTOMATION_CLOUD_INFRASTRUCTURE_ORCHESTRATION_GOAL,
DEVOPS_AUTOMATION_DATA_SYNCING_GOAL,
DEVOPS_INCIDENT_RESPONSE_GOAL,
DEVOPS_MONITORING_AND_ALERTING_GOAL,
DEVOPS_REPORTING_GOAL,
DEVOPS_TICKETING_SYSTEMS_INTEGRATIONS_GOAL,
AUTOMATION_BENEFICIARY_KEY,
AUTOMATION_BENEFICIARY_SELF,
AUTOMATION_BENEFICIARY_MY_TEAM,
AUTOMATION_BENEFICIARY_OTHER_TEAMS,
REPORTED_SOURCE_KEY,
REPORTED_SOURCE_GOOGLE,
REPORTED_SOURCE_TWITTER,
REPORTED_SOURCE_LINKEDIN,
REPORTED_SOURCE_YOUTUBE,
REPORTED_SOURCE_FRIEND,
REPORTED_SOURCE_PODCAST,
REPORTED_SOURCE_EVENT,
REPORTED_SOURCE_OTHER,
REPORTED_SOURCE_OTHER_KEY,
} from '../constants';
import { workflowHelpers } from '@/mixins/workflowHelpers';
import { showMessage } from '@/mixins/showMessage';
import Modal from './Modal.vue';
import {
IFormInputs,
IPersonalizationLatestVersion,
IPersonalizationSurveyAnswersV3,
IUser,
} from '@/Interface';
import { IFormInputs, IPersonalizationLatestVersion, IUser } from '@/Interface';
import Vue from 'vue';
import { getAccountAge } from '@/utils';
import { GenericValue } from 'n8n-workflow';
@ -293,56 +307,48 @@ export default mixins(showMessage, workflowHelpers).extend({
},
},
{
name: AUTOMATION_GOAL_KEY,
name: ROLE_KEY,
properties: {
type: 'select',
label: this.$locale.baseText('personalizationModal.whatAreYouLookingToAutomate'),
label: this.$locale.baseText('personalizationModal.whichRoleBestDescribesYou'),
placeholder: this.$locale.baseText('personalizationModal.select'),
options: [
{
value: CUSTOMER_INTEGRATIONS_GOAL,
label: this.$locale.baseText('personalizationModal.customerIntegrations'),
value: ROLE_BUSINESS_OWNER,
label: this.$locale.baseText('personalizationModal.businessOwner'),
},
{
value: CUSTOMER_SUPPORT_GOAL,
value: ROLE_CUSTOMER_SUPPORT,
label: this.$locale.baseText('personalizationModal.customerSupport'),
},
{
value: ENGINEERING_GOAL,
label: this.$locale.baseText('personalizationModal.engineeringOrDevops'),
value: ROLE_DATA_SCIENCE,
label: this.$locale.baseText('personalizationModal.dataScience'),
},
{
value: FINANCE_ACCOUNTING_GOAL,
label: this.$locale.baseText('personalizationModal.financeOrAccounting'),
value: ROLE_DEVOPS,
label: this.$locale.baseText('personalizationModal.devops'),
},
{
value: HR_GOAL,
label: this.$locale.baseText('personalizationModal.hr'),
value: ROLE_IT,
label: this.$locale.baseText('personalizationModal.it'),
},
{
value: OPERATIONS_GOAL,
label: this.$locale.baseText('personalizationModal.operations'),
value: ROLE_ENGINEERING,
label: this.$locale.baseText('personalizationModal.engineering'),
},
{
value: PRODUCT_GOAL,
label: this.$locale.baseText('personalizationModal.product'),
},
{
value: SALES_MARKETING_GOAL,
value: ROLE_SALES_AND_MARKETING,
label: this.$locale.baseText('personalizationModal.salesAndMarketing'),
},
{
value: SECURITY_GOAL,
value: ROLE_SECURITY,
label: this.$locale.baseText('personalizationModal.security'),
},
{
value: OTHER_AUTOMATION_GOAL,
value: ROLE_OTHER,
label: this.$locale.baseText('personalizationModal.otherPleaseSpecify'),
},
{
value: NOT_SURE_YET_GOAL,
label: this.$locale.baseText('personalizationModal.notSureYet'),
},
],
},
shouldDisplay(values): boolean {
@ -351,15 +357,82 @@ export default mixins(showMessage, workflowHelpers).extend({
},
},
{
name: AUTOMATION_GOAL_OTHER_KEY,
name: ROLE_OTHER_KEY,
properties: {
placeholder: this.$locale.baseText('personalizationModal.specifyYourRole'),
},
shouldDisplay(values): boolean {
const companyType = (values as IPersonalizationLatestVersion)[COMPANY_TYPE_KEY];
const role = (values as IPersonalizationLatestVersion)[ROLE_KEY];
return companyType !== PERSONAL_COMPANY_TYPE && role === ROLE_OTHER;
},
},
{
name: DEVOPS_AUTOMATION_GOAL_KEY,
properties: {
type: 'multi-select',
label: this.$locale.baseText('personalizationModal.whatAreYouLookingToAutomate'),
placeholder: this.$locale.baseText('personalizationModal.select'),
options: [
{
value: DEVOPS_AUTOMATION_CI_CD_GOAL,
label: this.$locale.baseText('personalizationModal.cicd'),
},
{
value: DEVOPS_AUTOMATION_CLOUD_INFRASTRUCTURE_ORCHESTRATION_GOAL,
label: this.$locale.baseText(
'personalizationModal.cloudInfrastructureOrchestration',
),
},
{
value: DEVOPS_AUTOMATION_DATA_SYNCING_GOAL,
label: this.$locale.baseText('personalizationModal.dataSynching'),
},
{
value: DEVOPS_INCIDENT_RESPONSE_GOAL,
label: this.$locale.baseText('personalizationModal.incidentResponse'),
},
{
value: DEVOPS_MONITORING_AND_ALERTING_GOAL,
label: this.$locale.baseText('personalizationModal.monitoringAndAlerting'),
},
{
value: DEVOPS_REPORTING_GOAL,
label: this.$locale.baseText('personalizationModal.reporting'),
},
{
value: DEVOPS_TICKETING_SYSTEMS_INTEGRATIONS_GOAL,
label: this.$locale.baseText('personalizationModal.ticketingSystemsIntegrations'),
},
{
value: OTHER_AUTOMATION_GOAL,
label: this.$locale.baseText('personalizationModal.other'),
},
],
},
shouldDisplay(values): boolean {
const companyType = (values as IPersonalizationLatestVersion)[COMPANY_TYPE_KEY];
const role = (values as IPersonalizationLatestVersion)[ROLE_KEY] as string;
return (
companyType !== PERSONAL_COMPANY_TYPE &&
[ROLE_DEVOPS, ROLE_ENGINEERING, ROLE_IT].includes(role)
);
},
},
{
name: DEVOPS_AUTOMATION_GOAL_OTHER_KEY,
properties: {
placeholder: this.$locale.baseText('personalizationModal.specifyYourAutomationGoal'),
},
shouldDisplay(values): boolean {
const companyType = (values as IPersonalizationLatestVersion)[COMPANY_TYPE_KEY];
const automationGoal = (values as IPersonalizationLatestVersion)[AUTOMATION_GOAL_KEY];
const goals = (values as IPersonalizationLatestVersion)[DEVOPS_AUTOMATION_GOAL_KEY];
const role = (values as IPersonalizationLatestVersion)[ROLE_KEY] as string;
return (
companyType !== PERSONAL_COMPANY_TYPE && automationGoal === OTHER_AUTOMATION_GOAL
companyType !== PERSONAL_COMPANY_TYPE &&
[ROLE_DEVOPS, ROLE_ENGINEERING, ROLE_IT].includes(role) &&
!!goals &&
goals.includes(DEVOPS_AUTOMATION_OTHER)
);
},
},
@ -401,8 +474,9 @@ export default mixins(showMessage, workflowHelpers).extend({
],
},
shouldDisplay(values): boolean {
const goal = (values as IPersonalizationLatestVersion)[AUTOMATION_GOAL_KEY];
return goal === SALES_MARKETING_GOAL;
const companyType = (values as IPersonalizationLatestVersion)[COMPANY_TYPE_KEY];
const role = (values as IPersonalizationLatestVersion)[ROLE_KEY];
return companyType !== PERSONAL_COMPANY_TYPE && role === ROLE_SALES_AND_MARKETING;
},
},
{
@ -413,31 +487,42 @@ export default mixins(showMessage, workflowHelpers).extend({
),
},
shouldDisplay(values): boolean {
const companyType = (values as IPersonalizationLatestVersion)[COMPANY_TYPE_KEY];
const goals = (values as IPersonalizationLatestVersion)[MARKETING_AUTOMATION_GOAL_KEY];
return !!goals && goals.includes(MARKETING_AUTOMATION_OTHER);
const role = (values as IPersonalizationLatestVersion)[ROLE_KEY];
return (
companyType !== PERSONAL_COMPANY_TYPE &&
role === ROLE_SALES_AND_MARKETING &&
!!goals &&
goals.includes(MARKETING_AUTOMATION_OTHER)
);
},
},
{
name: USAGE_MODE_KEY,
name: AUTOMATION_BENEFICIARY_KEY,
properties: {
type: 'multi-select',
label: this.$locale.baseText('personalizationModal.specifyUsageMode'),
type: 'select',
label: this.$locale.baseText('personalizationModal.specifyAutomationBeneficiary'),
placeholder: this.$locale.baseText('personalizationModal.select'),
options: [
{
label: this.$locale.baseText('personalizationModal.connectToInternalDB'),
value: USAGE_MODE_CONNECT_TO_DB,
label: this.$locale.baseText('personalizationModal.myself'),
value: AUTOMATION_BENEFICIARY_SELF,
},
{
label: this.$locale.baseText('personalizationModal.buildBackendServices'),
value: USAGE_MODE_BUILD_BE_SERVICES,
label: this.$locale.baseText('personalizationModal.myTeam'),
value: AUTOMATION_BENEFICIARY_MY_TEAM,
},
{
label: this.$locale.baseText('personalizationModal.manipulateFiles'),
value: USAGE_MODE_MANIPULATE_FILES,
label: this.$locale.baseText('personalizationModal.otherTeams'),
value: AUTOMATION_BENEFICIARY_OTHER_TEAMS,
},
],
},
shouldDisplay(values): boolean {
const companyType = (values as IPersonalizationLatestVersion)[COMPANY_TYPE_KEY];
return companyType !== PERSONAL_COMPANY_TYPE;
},
},
{
name: COMPANY_SIZE_KEY,
@ -477,6 +562,58 @@ export default mixins(showMessage, workflowHelpers).extend({
return companyType !== PERSONAL_COMPANY_TYPE;
},
},
{
name: REPORTED_SOURCE_KEY,
properties: {
type: 'select',
label: this.$locale.baseText('personalizationModal.howDidYouHearAboutN8n'),
placeholder: this.$locale.baseText('personalizationModal.select'),
options: [
{
label: 'Google',
value: REPORTED_SOURCE_GOOGLE,
},
{
label: 'Twitter',
value: REPORTED_SOURCE_TWITTER,
},
{
label: 'LinkedIn',
value: REPORTED_SOURCE_LINKEDIN,
},
{
label: 'YouTube',
value: REPORTED_SOURCE_YOUTUBE,
},
{
label: this.$locale.baseText('personalizationModal.friendWordOfMouth'),
value: REPORTED_SOURCE_FRIEND,
},
{
label: this.$locale.baseText('personalizationModal.podcast'),
value: REPORTED_SOURCE_PODCAST,
},
{
label: this.$locale.baseText('personalizationModal.event'),
value: REPORTED_SOURCE_EVENT,
},
{
label: this.$locale.baseText('personalizationModal.otherPleaseSpecify'),
value: REPORTED_SOURCE_OTHER,
},
],
},
},
{
name: REPORTED_SOURCE_OTHER_KEY,
properties: {
placeholder: this.$locale.baseText('personalizationModal.specifyReportedSource'),
},
shouldDisplay(values): boolean {
const reportedSource = (values as IPersonalizationLatestVersion)[REPORTED_SOURCE_KEY];
return reportedSource === REPORTED_SOURCE_OTHER;
},
},
];
return survey;
@ -502,9 +639,7 @@ export default mixins(showMessage, workflowHelpers).extend({
this.$externalHooks().run('personalizationModal.onSubmit', survey);
await this.usersStore.submitPersonalizationSurvey(
survey as IPersonalizationSurveyAnswersV3,
);
await this.usersStore.submitPersonalizationSurvey(survey as IPersonalizationLatestVersion);
if (Object.keys(values).length === 0) {
this.closeDialog();

View file

@ -0,0 +1,62 @@
import { PiniaVuePlugin } from 'pinia';
import { createLocalVue, mount } from '@vue/test-utils';
import PersonalizationModal from '@/components/PersonalizationModal.vue';
import { createTestingPinia } from '@pinia/testing';
import { PERSONALIZATION_MODAL_KEY, ROLE_SALES_AND_MARKETING } from '@/constants';
import { retry } from '@/__tests__/utils';
describe('PersonalizationModal.vue', () => {
const pinia = createTestingPinia({
initialState: {
ui: {
modals: {
[PERSONALIZATION_MODAL_KEY]: { open: true },
},
},
settings: {
settings: {
templates: {
host: '',
},
},
},
},
});
const localVue = createLocalVue();
localVue.use(PiniaVuePlugin);
it('should render correctly', async () => {
const wrapper = mount(PersonalizationModal, {
localVue,
pinia,
});
await retry(() => expect(wrapper.find('.modal-content').exists()).toBe(true));
expect(wrapper.findAll('.n8n-select').length).toEqual(5);
expect(wrapper.html()).toMatchSnapshot();
});
it('should display new option when role is "Devops", "Engineering", "IT", or "Sales and marketing"', async () => {
const wrapper = mount(PersonalizationModal, {
localVue,
pinia,
});
await retry(() => expect(wrapper.find('.modal-content').exists()).toBe(true));
for (const index of [3, 4, 5, 6]) {
wrapper.find('.n8n-select[name="role"]').trigger('click');
wrapper
.find('.n8n-select[name="role"]')
.findAll('.el-select-dropdown__item')
.at(index)
.trigger('click');
await retry(() => {
expect(wrapper.findAll('.n8n-select').length).toEqual(6);
expect(wrapper.find('.n8n-select[name^="automationGoal"]').exists()).toBe(true);
});
}
});
});

View file

@ -0,0 +1,284 @@
// Vitest Snapshot v1
exports[`PersonalizationModal.vue > should render correctly 1`] = `
"<transition-stub name=\\"dialog-fade\\" data-test-id=\\"personalization-form\\" class=\\"dialog-wrapper\\" style=\\"z-index: 2001;\\">
<div class=\\"el-dialog__wrapper\\">
<div role=\\"dialog\\" aria-modal=\\"true\\" aria-label=\\"dialog\\" class=\\"el-dialog\\" style=\\"margin-top: 15vh; width: 460px;\\">
<div class=\\"el-dialog__header\\">
<div class=\\"centerTitle\\">
<div>
<h1 class=\\"n8n-heading size-xlarge regular\\">Customize n8n to you</h1>
</div>
<div class=\\"subtitle\\">
<h3 class=\\"n8n-heading text-light size-small regular\\">These questions help us tailor n8n to you</h3>
</div>
</div>
<!---->
</div>
<div class=\\"el-dialog__body\\">
<div class=\\"modal-content\\">
<div class=\\"container\\">
<div>
<div class=\\"grid\\">
<div class=\\"\\">
<div class=\\"container\\" data-test-id=\\"companyType\\"><label for=\\"companyType\\" class=\\"inputLabel heading medium\\">
<div class=\\"title\\"><span class=\\"n8n-text size-medium bold\\"> What best describes your company? <!----></span></div>
<!---->
<!---->
<!---->
</label>
<div class=\\"\\">
<div class=\\"n8n-select container\\" name=\\"companyType\\">
<!---->
<div class=\\"el-select el-select--large\\">
<!---->
<div class=\\"el-input el-input--large el-input--suffix\\">
<!----><input type=\\"text\\" readonly=\\"readonly\\" autocomplete=\\"off\\" placeholder=\\"Select...\\" class=\\"el-input__inner\\">
<!----><span class=\\"el-input__suffix\\"><span class=\\"el-input__suffix-inner\\"><i class=\\"el-select__caret el-input__icon el-icon-arrow-up\\"></i><!----><!----><!----><!----><!----></span>
<!----></span>
<!---->
<!---->
</div>
<transition-stub name=\\"el-zoom-in-top\\">
<div class=\\"el-select-dropdown el-popper\\" style=\\"display: none;\\">
<div class=\\"el-scrollbar\\" style=\\"\\">
<div class=\\"el-select-dropdown__wrap el-scrollbar__wrap el-scrollbar__wrap--hidden-default\\">
<ul class=\\"el-scrollbar__view el-select-dropdown__list\\">
<!---->
<li class=\\"el-select-dropdown__item\\"><span>Software as a service</span></li>
<li class=\\"el-select-dropdown__item\\"><span>Ecommerce</span></li>
<li class=\\"el-select-dropdown__item\\"><span>Marketing agency / consultancy</span></li>
<li class=\\"el-select-dropdown__item\\"><span>Systems integrator / Automation agency</span></li>
<li class=\\"el-select-dropdown__item\\"><span>Education</span></li>
<li class=\\"el-select-dropdown__item\\"><span>Other</span></li>
<li class=\\"el-select-dropdown__item\\"><span>I'm not using n8n for work</span></li>
</ul>
</div>
<div class=\\"el-scrollbar__bar is-horizontal\\">
<div class=\\"el-scrollbar__thumb\\" style=\\"transform: translateX(0%); webkit-transform: translateX(0%);\\"></div>
</div>
<div class=\\"el-scrollbar__bar is-vertical\\">
<div class=\\"el-scrollbar__thumb\\" style=\\"transform: translateY(0%); webkit-transform: translateY(0%);\\"></div>
</div>
</div>
<!---->
</div>
</transition-stub>
</div>
</div>
</div>
<!---->
</div>
</div>
<div class=\\"mt-undefined\\">
<div class=\\"container\\" data-test-id=\\"role\\"><label for=\\"role\\" class=\\"inputLabel heading medium\\">
<div class=\\"title\\"><span class=\\"n8n-text size-medium bold\\"> Which role best describes you? <!----></span></div>
<!---->
<!---->
<!---->
</label>
<div class=\\"\\">
<div class=\\"n8n-select container\\" name=\\"role\\">
<!---->
<div class=\\"el-select el-select--large\\">
<!---->
<div class=\\"el-input el-input--large el-input--suffix\\">
<!----><input type=\\"text\\" readonly=\\"readonly\\" autocomplete=\\"off\\" placeholder=\\"Select...\\" class=\\"el-input__inner\\">
<!----><span class=\\"el-input__suffix\\"><span class=\\"el-input__suffix-inner\\"><i class=\\"el-select__caret el-input__icon el-icon-arrow-up\\"></i><!----><!----><!----><!----><!----></span>
<!----></span>
<!---->
<!---->
</div>
<transition-stub name=\\"el-zoom-in-top\\">
<div class=\\"el-select-dropdown el-popper\\" style=\\"display: none;\\">
<div class=\\"el-scrollbar\\" style=\\"\\">
<div class=\\"el-select-dropdown__wrap el-scrollbar__wrap el-scrollbar__wrap--hidden-default\\">
<ul class=\\"el-scrollbar__view el-select-dropdown__list\\">
<!---->
<li class=\\"el-select-dropdown__item\\"><span>Business Owner</span></li>
<li class=\\"el-select-dropdown__item\\"><span>Customer support</span></li>
<li class=\\"el-select-dropdown__item\\"><span>Data Science</span></li>
<li class=\\"el-select-dropdown__item\\"><span>Devops</span></li>
<li class=\\"el-select-dropdown__item\\"><span>IT</span></li>
<li class=\\"el-select-dropdown__item\\"><span>Engineering</span></li>
<li class=\\"el-select-dropdown__item\\"><span>Sales and Marketing</span></li>
<li class=\\"el-select-dropdown__item\\"><span>Security</span></li>
<li class=\\"el-select-dropdown__item\\"><span>Other (please specify)</span></li>
</ul>
</div>
<div class=\\"el-scrollbar__bar is-horizontal\\">
<div class=\\"el-scrollbar__thumb\\" style=\\"transform: translateX(0%); webkit-transform: translateX(0%);\\"></div>
</div>
<div class=\\"el-scrollbar__bar is-vertical\\">
<div class=\\"el-scrollbar__thumb\\" style=\\"transform: translateY(0%); webkit-transform: translateY(0%);\\"></div>
</div>
</div>
<!---->
</div>
</transition-stub>
</div>
</div>
</div>
<!---->
</div>
</div>
<div class=\\"mt-undefined\\">
<div class=\\"container\\" data-test-id=\\"automationBeneficiary\\"><label for=\\"automationBeneficiary\\" class=\\"inputLabel heading medium\\">
<div class=\\"title\\"><span class=\\"n8n-text size-medium bold\\"> Who will your automations mainly be for? <!----></span></div>
<!---->
<!---->
<!---->
</label>
<div class=\\"\\">
<div class=\\"n8n-select container\\" name=\\"automationBeneficiary\\">
<!---->
<div class=\\"el-select el-select--large\\">
<!---->
<div class=\\"el-input el-input--large el-input--suffix\\">
<!----><input type=\\"text\\" readonly=\\"readonly\\" autocomplete=\\"off\\" placeholder=\\"Select...\\" class=\\"el-input__inner\\">
<!----><span class=\\"el-input__suffix\\"><span class=\\"el-input__suffix-inner\\"><i class=\\"el-select__caret el-input__icon el-icon-arrow-up\\"></i><!----><!----><!----><!----><!----></span>
<!----></span>
<!---->
<!---->
</div>
<transition-stub name=\\"el-zoom-in-top\\">
<div class=\\"el-select-dropdown el-popper\\" style=\\"display: none;\\">
<div class=\\"el-scrollbar\\" style=\\"\\">
<div class=\\"el-select-dropdown__wrap el-scrollbar__wrap el-scrollbar__wrap--hidden-default\\">
<ul class=\\"el-scrollbar__view el-select-dropdown__list\\">
<!---->
<li class=\\"el-select-dropdown__item\\"><span>Myself</span></li>
<li class=\\"el-select-dropdown__item\\"><span>My team</span></li>
<li class=\\"el-select-dropdown__item\\"><span>Other teams</span></li>
</ul>
</div>
<div class=\\"el-scrollbar__bar is-horizontal\\">
<div class=\\"el-scrollbar__thumb\\" style=\\"transform: translateX(0%); webkit-transform: translateX(0%);\\"></div>
</div>
<div class=\\"el-scrollbar__bar is-vertical\\">
<div class=\\"el-scrollbar__thumb\\" style=\\"transform: translateY(0%); webkit-transform: translateY(0%);\\"></div>
</div>
</div>
<!---->
</div>
</transition-stub>
</div>
</div>
</div>
<!---->
</div>
</div>
<div class=\\"mt-undefined\\">
<div class=\\"container\\" data-test-id=\\"companySize\\"><label for=\\"companySize\\" class=\\"inputLabel heading medium\\">
<div class=\\"title\\"><span class=\\"n8n-text size-medium bold\\"> How big is your company? <!----></span></div>
<!---->
<!---->
<!---->
</label>
<div class=\\"\\">
<div class=\\"n8n-select container\\" name=\\"companySize\\">
<!---->
<div class=\\"el-select el-select--large\\">
<!---->
<div class=\\"el-input el-input--large el-input--suffix\\">
<!----><input type=\\"text\\" readonly=\\"readonly\\" autocomplete=\\"off\\" placeholder=\\"Select...\\" class=\\"el-input__inner\\">
<!----><span class=\\"el-input__suffix\\"><span class=\\"el-input__suffix-inner\\"><i class=\\"el-select__caret el-input__icon el-icon-arrow-up\\"></i><!----><!----><!----><!----><!----></span>
<!----></span>
<!---->
<!---->
</div>
<transition-stub name=\\"el-zoom-in-top\\">
<div class=\\"el-select-dropdown el-popper\\" style=\\"display: none;\\">
<div class=\\"el-scrollbar\\" style=\\"\\">
<div class=\\"el-select-dropdown__wrap el-scrollbar__wrap el-scrollbar__wrap--hidden-default\\">
<ul class=\\"el-scrollbar__view el-select-dropdown__list\\">
<!---->
<li class=\\"el-select-dropdown__item\\"><span>Less than 20 people</span></li>
<li class=\\"el-select-dropdown__item\\"><span>20-99 people</span></li>
<li class=\\"el-select-dropdown__item\\"><span>100-499 people</span></li>
<li class=\\"el-select-dropdown__item\\"><span>500-999 people</span></li>
<li class=\\"el-select-dropdown__item\\"><span>1000+ people</span></li>
<li class=\\"el-select-dropdown__item\\"><span>I'm not using n8n for work</span></li>
</ul>
</div>
<div class=\\"el-scrollbar__bar is-horizontal\\">
<div class=\\"el-scrollbar__thumb\\" style=\\"transform: translateX(0%); webkit-transform: translateX(0%);\\"></div>
</div>
<div class=\\"el-scrollbar__bar is-vertical\\">
<div class=\\"el-scrollbar__thumb\\" style=\\"transform: translateY(0%); webkit-transform: translateY(0%);\\"></div>
</div>
</div>
<!---->
</div>
</transition-stub>
</div>
</div>
</div>
<!---->
</div>
</div>
<div class=\\"mt-undefined\\">
<div class=\\"container\\" data-test-id=\\"reportedSource\\"><label for=\\"reportedSource\\" class=\\"inputLabel heading medium\\">
<div class=\\"title\\"><span class=\\"n8n-text size-medium bold\\"> How did you hear about n8n? <!----></span></div>
<!---->
<!---->
<!---->
</label>
<div class=\\"\\">
<div class=\\"n8n-select container\\" name=\\"reportedSource\\">
<!---->
<div class=\\"el-select el-select--large\\">
<!---->
<div class=\\"el-input el-input--large el-input--suffix\\">
<!----><input type=\\"text\\" readonly=\\"readonly\\" autocomplete=\\"off\\" placeholder=\\"Select...\\" class=\\"el-input__inner\\">
<!----><span class=\\"el-input__suffix\\"><span class=\\"el-input__suffix-inner\\"><i class=\\"el-select__caret el-input__icon el-icon-arrow-up\\"></i><!----><!----><!----><!----><!----></span>
<!----></span>
<!---->
<!---->
</div>
<transition-stub name=\\"el-zoom-in-top\\">
<div class=\\"el-select-dropdown el-popper\\" style=\\"display: none;\\">
<div class=\\"el-scrollbar\\" style=\\"\\">
<div class=\\"el-select-dropdown__wrap el-scrollbar__wrap el-scrollbar__wrap--hidden-default\\">
<ul class=\\"el-scrollbar__view el-select-dropdown__list\\">
<!---->
<li class=\\"el-select-dropdown__item\\"><span>Google</span></li>
<li class=\\"el-select-dropdown__item\\"><span>Twitter</span></li>
<li class=\\"el-select-dropdown__item\\"><span>LinkedIn</span></li>
<li class=\\"el-select-dropdown__item\\"><span>YouTube</span></li>
<li class=\\"el-select-dropdown__item\\"><span>Friend / Word of mouth</span></li>
<li class=\\"el-select-dropdown__item\\"><span>Podcast</span></li>
<li class=\\"el-select-dropdown__item\\"><span>Event</span></li>
<li class=\\"el-select-dropdown__item\\"><span>Other (please specify)</span></li>
</ul>
</div>
<div class=\\"el-scrollbar__bar is-horizontal\\">
<div class=\\"el-scrollbar__thumb\\" style=\\"transform: translateX(0%); webkit-transform: translateX(0%);\\"></div>
</div>
<div class=\\"el-scrollbar__bar is-vertical\\">
<div class=\\"el-scrollbar__thumb\\" style=\\"transform: translateY(0%); webkit-transform: translateY(0%);\\"></div>
</div>
</div>
<!---->
</div>
</transition-stub>
</div>
</div>
</div>
<!---->
</div>
</div>
</div>
</div>
</div>
</div>
<div class=\\"footer\\">
<div><button aria-disabled=\\"false\\" aria-busy=\\"false\\" aria-live=\\"polite\\" class=\\"button button primary medium float-right\\">
<!----><span>Continue</span></button></div>
</div>
</div>
<!---->
</div>
</div>
</transition-stub>"
`;

View file

@ -232,13 +232,40 @@ export const OTHER_MARKETING_AUTOMATION_GOAL_KEY = 'automationGoalSmOther';
export const CODING_SKILL_KEY = 'codingSkill';
export const AUTOMATION_BENEFICIARY_KEY = 'automationBeneficiary';
export const AUTOMATION_BENEFICIARY_SELF = 'myself';
export const AUTOMATION_BENEFICIARY_MY_TEAM = 'my-team';
export const AUTOMATION_BENEFICIARY_OTHER_TEAMS = 'other-teams';
export const USAGE_MODE_KEY = 'usageModes';
export const USAGE_MODE_CONNECT_TO_DB = 'connect-internal-db';
export const USAGE_MODE_BUILD_BE_SERVICES = 'build-be-services';
export const USAGE_MODE_MANIPULATE_FILES = 'manipulate-files';
export const REPORTED_SOURCE_KEY = 'reportedSource';
export const REPORTED_SOURCE_OTHER_KEY = 'reportedSourceOther';
export const REPORTED_SOURCE_GOOGLE = 'google';
export const REPORTED_SOURCE_TWITTER = 'twitter';
export const REPORTED_SOURCE_LINKEDIN = 'linkedin';
export const REPORTED_SOURCE_YOUTUBE = 'youtube';
export const REPORTED_SOURCE_FRIEND = 'friend';
export const REPORTED_SOURCE_PODCAST = 'podcast';
export const REPORTED_SOURCE_EVENT = 'event';
export const REPORTED_SOURCE_OTHER = 'other';
export const AUTOMATION_GOAL_KEY = 'automationGoal';
export const AUTOMATION_GOAL_OTHER_KEY = 'otherAutomationGoal';
export const DEVOPS_AUTOMATION_GOAL_KEY = 'automationGoalDevops';
export const DEVOPS_AUTOMATION_GOAL_OTHER_KEY = 'automationGoalDevopsOther';
export const DEVOPS_AUTOMATION_OTHER = 'other';
export const DEVOPS_AUTOMATION_CI_CD_GOAL = 'ci-cd';
export const DEVOPS_AUTOMATION_CLOUD_INFRASTRUCTURE_ORCHESTRATION_GOAL =
'cloud-infrastructure-orchestration';
export const DEVOPS_AUTOMATION_DATA_SYNCING_GOAL = 'data-syncing';
export const DEVOPS_INCIDENT_RESPONSE_GOAL = 'incident-response';
export const DEVOPS_MONITORING_AND_ALERTING_GOAL = 'monitoring-alerting';
export const DEVOPS_REPORTING_GOAL = 'reporting';
export const DEVOPS_TICKETING_SYSTEMS_INTEGRATIONS_GOAL = 'ticketing-systems-integrations';
export const CUSTOMER_INTEGRATIONS_GOAL = 'customer-integrations';
export const CUSTOMER_SUPPORT_GOAL = 'customer-support';
export const ENGINEERING_GOAL = 'engineering';
@ -251,6 +278,18 @@ export const SECURITY_GOAL = 'security';
export const OTHER_AUTOMATION_GOAL = 'other';
export const NOT_SURE_YET_GOAL = 'not-sure-yet';
export const ROLE_KEY = 'role';
export const ROLE_OTHER_KEY = 'roleOther';
export const ROLE_BUSINESS_OWNER = 'business-owner';
export const ROLE_CUSTOMER_SUPPORT = 'customer-support';
export const ROLE_DATA_SCIENCE = 'data-science';
export const ROLE_DEVOPS = 'devops';
export const ROLE_IT = 'it';
export const ROLE_ENGINEERING = 'engineering';
export const ROLE_SALES_AND_MARKETING = 'sales-and-marketing';
export const ROLE_SECURITY = 'security';
export const ROLE_OTHER = 'other';
/** END OF PERSONALIZATION SURVEY */
export const MODAL_CANCEL = 'cancel';

View file

@ -897,13 +897,19 @@
"parameterInputList.delete": "Delete",
"parameterInputList.deleteParameter": "Delete Parameter",
"parameterInputList.parameterOptions": "Parameter Options",
"personalizationModal.businessOwner": "Business Owner",
"personalizationModal.continue": "Continue",
"personalizationModal.cicd": "CI/CD",
"personalizationModal.cloudInfrastructureOrchestration": "Cloud infrastructure orchestration",
"personalizationModal.customerIntegrations": "Customer integrations",
"personalizationModal.customerSupport": "Customer support",
"personalizationModal.customizeN8n": "Customize n8n to you",
"personalizationModal.dataScience": "Data Science",
"personalizationModal.devops": "Devops",
"personalizationModal.digitalAgencyOrConsultant": "Marketing agency / consultancy",
"personalizationModal.eCommerce": "eCommerce",
"personalizationModal.eCommerce": "Ecommerce",
"personalizationModal.education": "Education",
"personalizationModal.engineering": "Engineering",
"personalizationModal.engineeringOrDevops": "Engineering / Devops",
"personalizationModal.errorWhileSubmittingResults": "Error while submitting results",
"personalizationModal.financeOrAccounting": "Finance / Accounting",
@ -927,6 +933,7 @@
"personalizationModal.operations": "Operations",
"personalizationModal.other": "Other",
"personalizationModal.otherPleaseSpecify": "Other (please specify)",
"personalizationModal.specifyReportedSource": "Specify how you heard about n8n",
"personalizationModal.people": "people",
"personalizationModal.physicalRetailOrServices": "Physical retail or services",
"personalizationModal.product": "Product (e.g. fast prototyping)",
@ -935,6 +942,15 @@
"personalizationModal.salesAndMarketing": "Sales and Marketing",
"personalizationModal.security": "Security",
"personalizationModal.select": "Select...",
"personalizationModal.howDidYouHearAboutN8n": "How did you hear about n8n?",
"personalizationModal.friendWordOfMouth": "Friend / Word of mouth",
"personalizationModal.podcast": "Podcast",
"personalizationModal.event": "Event",
"personalizationModal.myself": "Myself",
"personalizationModal.myTeam": "My team",
"personalizationModal.otherTeams": "Other teams",
"personalizationModal.specifyAutomationBeneficiary": "Who will your automations mainly be for?",
"personalizationModal.specifyYourRole": "Please specify your role",
"personalizationModal.specifyYourAutomationGoal": "Please specify your automation goal",
"personalizationModal.specifyYourCompanysIndustry": "Specify your company's industry",
"personalizationModal.support": "Support",
@ -942,6 +958,7 @@
"personalizationModal.telecoms": "Telecoms",
"personalizationModal.thanks": "Thanks!",
"personalizationModal.theseQuestionsHelpUs": "These questions help us tailor n8n to you",
"personalizationModal.whichRoleBestDescribesYou": "Which role best describes you?",
"personalizationModal.whatAreYouLookingToAutomate": "What are you looking to automate?",
"personalizationModal.whatBestDescribesYourCompany": "What best describes your company?",
"personalizationModal.whichIndustriesIsYourCompanyIn": "Which industries is your company in?",
@ -953,7 +970,10 @@
"personalizationModal.email": "Enter your email..",
"personalizationModal.adCampaign": "Ad campaign management",
"personalizationModal.reporting": "Reporting",
"personalizationModal.ticketingSystemsIntegrations": "Ticketing systems integrations",
"personalizationModal.dataSynching": "Data syncing",
"personalizationModal.incidentResponse": "Incident response",
"personalizationModal.monitoringAndAlerting": "Monitoring and alerting",
"personalizationModal.specifyUsageMode": "Are you looking to do any of these?",
"personalizationModal.connectToInternalDB": "Connect to my companys internal databases",
"personalizationModal.buildBackendServices": "Build backend services (endpoints)",

View file

@ -65,6 +65,7 @@ import {
IPersonalizationSurveyAnswersV1,
IPersonalizationSurveyAnswersV2,
IPersonalizationSurveyAnswersV3,
IPersonalizationSurveyAnswersV4,
IPersonalizationSurveyVersions,
IUser,
} from '@/Interface';
@ -74,9 +75,12 @@ import { ILogInStatus, IRole, IUserPermissions } from '@/Interface';
Utility functions used to handle users in n8n
*/
function isPersonalizationV2OrV3(
function isPersonalizationSurveyV2OrLater(
data: IPersonalizationSurveyVersions,
): data is IPersonalizationSurveyAnswersV2 | IPersonalizationSurveyAnswersV3 {
): data is
| IPersonalizationSurveyAnswersV2
| IPersonalizationSurveyAnswersV3
| IPersonalizationSurveyAnswersV4 {
return 'version' in data;
}
@ -179,17 +183,18 @@ export function getPersonalizedNodeTypes(
| IPersonalizationSurveyAnswersV1
| IPersonalizationSurveyAnswersV2
| IPersonalizationSurveyAnswersV3
| IPersonalizationSurveyAnswersV4
| null,
): string[] {
if (!answers) {
return [];
}
if (isPersonalizationV2OrV3(answers)) {
return getPersonalizationV2(answers);
if (isPersonalizationSurveyV2OrLater(answers)) {
return getPersonalizationSurveyV2OrLater(answers);
}
return getPersonalizationV1(answers);
return getPersonalizationSurveyV1(answers);
}
export function getAccountAge(currentUser: IUser): number {
@ -202,8 +207,11 @@ export function getAccountAge(currentUser: IUser): number {
return -1;
}
function getPersonalizationV2(
answers: IPersonalizationSurveyAnswersV2 | IPersonalizationSurveyAnswersV3,
function getPersonalizationSurveyV2OrLater(
answers:
| IPersonalizationSurveyAnswersV2
| IPersonalizationSurveyAnswersV3
| IPersonalizationSurveyAnswersV4,
) {
let nodeTypes: string[] = [];
@ -339,7 +347,7 @@ function getPersonalizationV2(
return nodeTypes;
}
function getPersonalizationV1(answers: IPersonalizationSurveyAnswersV1) {
function getPersonalizationSurveyV1(answers: IPersonalizationSurveyAnswersV1) {
const companySize = answers[COMPANY_SIZE_KEY];
const workArea = answers[WORK_AREA_KEY];