refactor(core): Remove Onboarding call prompts (no-changelog) (#9933)

This commit is contained in:
कारतोफ्फेलस्क्रिप्ट™ 2024-07-04 13:51:26 +02:00 committed by GitHub
parent 31163e1da5
commit 9e92a5774e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 3 additions and 320 deletions

View file

@ -1232,15 +1232,6 @@ export const schema = {
env: 'N8N_DEFAULT_LOCALE',
},
onboardingCallPrompt: {
enabled: {
doc: 'Whether onboarding call prompt feature is available',
format: Boolean,
default: true,
env: 'N8N_ONBOARDING_CALL_PROMPTS_ENABLED',
},
},
license: {
serverUrl: {
format: String,

View file

@ -163,7 +163,6 @@ export class FrontendService {
enabled: config.getEnv('templates.enabled'),
host: config.getEnv('templates.host'),
},
onboardingCallPromptEnabled: config.getEnv('onboardingCallPrompt.enabled'),
executionMode: config.getEnv('executions.mode'),
pushBackend: config.getEnv('push.backend'),
communityNodesEnabled: config.getEnv('nodes.communityPackages.enabled'),

View file

@ -1332,7 +1332,6 @@ export interface ISettingsState {
mfa: {
enabled: boolean;
};
onboardingCallPromptEnabled: boolean;
saveDataErrorExecution: WorkflowSettings.SaveDataExecution;
saveDataSuccessExecution: WorkflowSettings.SaveDataExecution;
saveManualExecutions: boolean;
@ -1428,16 +1427,6 @@ export interface IInviteResponse {
error?: string;
}
export interface IOnboardingCallPromptResponse {
nextPrompt: IOnboardingCallPrompt;
}
export interface IOnboardingCallPrompt {
title: string;
description: string;
toast_sequence_number: number;
}
export interface ITab {
value: string | number;
label?: string;

View file

@ -51,7 +51,6 @@ export const defaultSettings: IN8nUISettings = {
logLevel: 'info',
maxExecutionTimeout: 0,
oauthCallbackUrls: { oauth1: '', oauth2: '' },
onboardingCallPromptEnabled: false,
personalizationSurveyEnabled: false,
releaseChannel: 'stable',
posthog: {

View file

@ -65,7 +65,6 @@ export const SETTINGS_STORE_DEFAULT_STATE: ISettingsState = {
mfa: {
enabled: false,
},
onboardingCallPromptEnabled: false,
saveDataErrorExecution: 'all',
saveDataSuccessExecution: 'all',
saveDataProgressExecution: false,

View file

@ -1,40 +1,9 @@
import type { IOnboardingCallPrompt, IUser } from '@/Interface';
import { get, post } from '@/utils/apiUtils';
import { isUserGlobalOwner } from '@/utils/userUtils';
import type { IUser } from '@/Interface';
import { post } from '@/utils/apiUtils';
const N8N_API_BASE_URL = 'https://api.n8n.io/api';
const ONBOARDING_PROMPTS_ENDPOINT = '/prompts/onboarding';
const CONTACT_EMAIL_SUBMISSION_ENDPOINT = '/accounts/onboarding';
export async function fetchNextOnboardingPrompt(
instanceId: string,
currentUser: IUser,
): Promise<IOnboardingCallPrompt> {
return await get(N8N_API_BASE_URL, ONBOARDING_PROMPTS_ENDPOINT, {
instance_id: instanceId,
user_id: `${instanceId}#${currentUser.id}`,
is_owner: isUserGlobalOwner(currentUser),
survey_results: currentUser.personalizationAnswers,
});
}
export async function applyForOnboardingCall(
instanceId: string,
currentUser: IUser,
email: string,
): Promise<string> {
try {
const response = await post(N8N_API_BASE_URL, ONBOARDING_PROMPTS_ENDPOINT, {
instance_id: instanceId,
user_id: `${instanceId}#${currentUser.id}`,
email,
});
return response;
} catch (e) {
throw e;
}
}
export async function submitEmailOnSignup(
instanceId: string,
currentUser: IUser,

View file

@ -11,7 +11,6 @@ import {
DELETE_USER_MODAL_KEY,
DUPLICATE_MODAL_KEY,
INVITE_USER_MODAL_KEY,
ONBOARDING_CALL_SIGNUP_MODAL_KEY,
PERSONALIZATION_MODAL_KEY,
TAGS_MANAGER_MODAL_KEY,
NPS_SURVEY_MODAL_KEY,
@ -44,7 +43,6 @@ import InviteUsersModal from '@/components/InviteUsersModal.vue';
import CredentialsSelectModal from '@/components/CredentialsSelectModal.vue';
import DuplicateWorkflowDialog from '@/components/DuplicateWorkflowDialog.vue';
import ModalRoot from '@/components/ModalRoot.vue';
import OnboardingCallSignupModal from '@/components/OnboardingCallSignupModal.vue';
import PersonalizationModal from '@/components/PersonalizationModal.vue';
import TagsManager from '@/components/TagsManager/TagsManager.vue';
import UpdatesPanel from '@/components/UpdatesPanel.vue';
@ -152,10 +150,6 @@ import ProjectMoveResourceConfirmModal from '@/components/Projects/ProjectMoveRe
</template>
</ModalRoot>
<ModalRoot :name="ONBOARDING_CALL_SIGNUP_MODAL_KEY">
<OnboardingCallSignupModal />
</ModalRoot>
<ModalRoot :name="COMMUNITY_PACKAGE_INSTALL_MODAL_KEY">
<CommunityPackageInstallModal />
</ModalRoot>

View file

@ -1,131 +0,0 @@
<template>
<Modal
:name="ONBOARDING_CALL_SIGNUP_MODAL_KEY"
:title="$locale.baseText('onboardingCallSignupModal.title')"
:event-bus="modalBus"
:center="true"
:show-close="false"
:before-close="onModalClose"
width="460px"
>
<template #content>
<div class="pb-m">
<n8n-text>
{{ $locale.baseText('onboardingCallSignupModal.description') }}
</n8n-text>
</div>
<div @keyup.enter="onSignup">
<n8n-input
v-model="email"
:placeholder="$locale.baseText('onboardingCallSignupModal.emailInput.placeholder')"
/>
<n8n-text v-if="showError" size="small" class="mt-4xs" tag="div" color="danger">
{{ $locale.baseText('onboardingCallSignupModal.infoText.emailError') }}
</n8n-text>
</div>
</template>
<template #footer>
<div :class="$style.buttonsContainer">
<n8n-button
:label="$locale.baseText('onboardingCallSignupModal.cancelButton.label')"
:disabled="loading"
float="right"
type="outline"
@click="onCancel"
/>
<n8n-button
:disabled="email === '' || loading"
:label="$locale.baseText('onboardingCallSignupModal.signupButton.label')"
float="right"
:loading="loading"
@click="onSignup"
/>
</div>
</template>
</Modal>
</template>
<script lang="ts">
import { ONBOARDING_CALL_SIGNUP_MODAL_KEY, VALID_EMAIL_REGEX } from '@/constants';
import Modal from './Modal.vue';
import { defineComponent } from 'vue';
import { useToast } from '@/composables/useToast';
import { mapStores } from 'pinia';
import { useUIStore } from '@/stores/ui.store';
import { createEventBus } from 'n8n-design-system/utils';
export default defineComponent({
name: 'OnboardingCallSignupModal',
components: {
Modal,
},
props: ['modalName'],
setup() {
return {
toast: useToast(),
};
},
data() {
return {
email: '',
modalBus: createEventBus(),
ONBOARDING_CALL_SIGNUP_MODAL_KEY,
showError: false,
okToClose: false,
loading: false,
};
},
computed: {
...mapStores(useUIStore),
isEmailValid(): boolean {
return VALID_EMAIL_REGEX.test(String(this.email).toLowerCase());
},
},
methods: {
async onSignup() {
if (!this.isEmailValid) {
this.showError = true;
return;
}
this.showError = false;
this.loading = true;
this.okToClose = false;
try {
await this.uiStore.applyForOnboardingCall(this.email);
this.toast.showMessage({
type: 'success',
title: this.$locale.baseText('onboardingCallSignupSucess.title'),
message: this.$locale.baseText('onboardingCallSignupSucess.message'),
});
this.okToClose = true;
this.modalBus.emit('close');
} catch (e) {
this.toast.showError(
e,
this.$locale.baseText('onboardingCallSignupFailed.title'),
this.$locale.baseText('onboardingCallSignupFailed.message'),
);
this.loading = false;
this.okToClose = true;
}
},
async onCancel() {
this.okToClose = true;
this.modalBus.emit('close');
},
onModalClose() {
return this.okToClose;
},
},
});
</script>
<style lang="scss" module>
.buttonsContainer {
display: flex;
justify-content: flex-end;
column-gap: var(--spacing-xs);
}
</style>

View file

@ -90,9 +90,6 @@ import {
PERSONAL_COMPANY_TYPE,
COMPANY_INDUSTRY_EXTENDED_KEY,
OTHER_COMPANY_INDUSTRY_EXTENDED_KEY,
ONBOARDING_PROMPT_TIMEBOX,
FIRST_ONBOARDING_PROMPT_TIMEOUT,
ONBOARDING_CALL_SIGNUP_MODAL_KEY,
MARKETING_AUTOMATION_GOAL_KEY,
MARKETING_AUTOMATION_LEAD_GENERATION_GOAL,
MARKETING_AUTOMATION_CUSTOMER_COMMUNICATION,
@ -141,8 +138,7 @@ import {
} from '@/constants';
import { useToast } from '@/composables/useToast';
import Modal from '@/components/Modal.vue';
import type { IFormInputs, IPersonalizationLatestVersion, IUser } from '@/Interface';
import { getAccountAge } from '@/utils/userUtils';
import type { IFormInputs, IPersonalizationLatestVersion } from '@/Interface';
import type { GenericValue } from 'n8n-workflow';
import { useUIStore } from '@/stores/ui.store';
import { useSettingsStore } from '@/stores/settings.store';
@ -715,8 +711,6 @@ export default defineComponent({
if (Object.keys(values).length === 0) {
this.closeDialog();
}
await this.fetchOnboardingPrompt();
} catch (e) {
this.showError(e, 'Error while submitting results');
}
@ -755,40 +749,6 @@ export default defineComponent({
);
}
},
async fetchOnboardingPrompt() {
if (
this.settingsStore.onboardingCallPromptEnabled &&
getAccountAge(this.usersStore.currentUser || ({} as IUser)) <= ONBOARDING_PROMPT_TIMEBOX
) {
const onboardingResponse = await this.uiStore.getNextOnboardingPrompt();
if (!onboardingResponse) {
return;
}
const promptTimeout =
onboardingResponse.toast_sequence_number === 1 ? FIRST_ONBOARDING_PROMPT_TIMEOUT : 1000;
setTimeout(async () => {
this.showToast({
type: 'info',
title: onboardingResponse.title,
message: onboardingResponse.description,
duration: 0,
customClass: 'clickable',
closeOnClick: true,
onClick: () => {
this.$telemetry.track('user clicked onboarding toast', {
seq_num: onboardingResponse.toast_sequence_number,
title: onboardingResponse.title,
description: onboardingResponse.description,
});
this.uiStore.openModal(ONBOARDING_CALL_SIGNUP_MODAL_KEY);
},
});
}, promptTimeout);
}
},
},
});
</script>

View file

@ -54,7 +54,6 @@ export const PERSONALIZATION_MODAL_KEY = 'personalization';
export const CONTACT_PROMPT_MODAL_KEY = 'contactPrompt';
export const NPS_SURVEY_MODAL_KEY = 'npsSurvey';
export const WORKFLOW_ACTIVE_MODAL_KEY = 'activation';
export const ONBOARDING_CALL_SIGNUP_MODAL_KEY = 'onboardingCallSignup';
export const COMMUNITY_PACKAGE_INSTALL_MODAL_KEY = 'communityPackageInstall';
export const COMMUNITY_PACKAGE_CONFIRM_MODAL_KEY = 'communityPackageManageConfirm';
export const IMPORT_CURL_MODAL_KEY = 'importCurl';
@ -502,9 +501,6 @@ export const enum FAKE_DOOR_FEATURES {
SSO = 'sso',
}
export const ONBOARDING_PROMPT_TIMEBOX = 14;
export const FIRST_ONBOARDING_PROMPT_TIMEOUT = 300000;
export const TEST_PIN_DATA = [
{
name: 'First item',

View file

@ -1293,17 +1293,6 @@
"nodeWebhooks.webhookUrls": "Webhook URLs",
"nodeWebhooks.webhookUrls.formTrigger": "Form URLs",
"nodeWebhooks.webhookUrls.chatTrigger": "Chat URL",
"onboardingCallSignupModal.title": "Your onboarding session",
"onboardingCallSignupModal.description": "Pop in your email and we'll send you some scheduling options",
"onboardingCallSignupModal.emailInput.placeholder": "Your work email",
"onboardingCallSignupModal.signupButton.label": "Submit",
"onboardingCallSignupModal.cancelButton.label": "Cancel",
"onboardingCallSignupModal.infoText.emailError": "This doesn't seem to be a valid email address",
"onboardingCallSignupSucess.title": "Check your email for the final step",
"onboardingCallSignupSucess.message": "You should receive a message from us shortly",
"onboardingCallSignupFailed.title": "Something went wrong",
"onboardingCallSignupFailed.message": "Your request could not be sent",
"onboardingCallSignupModal.confirmExit.title": "Are you sure?",
"onboardingWorkflow.stickyContent": "## 👇 Get started faster \nLightning tour of the key concepts [4 min] \n\n[![n8n quickstart video](/static/quickstart_thumbnail.png#full-width)](https://www.youtube.com/watch?v=1MwSoB0gnM4)",
"openWorkflow.workflowImportError": "Could not import workflow",
"openWorkflow.workflowNotFoundError": "Could not find workflow",

View file

@ -64,7 +64,6 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, {
mfa: {
enabled: false,
},
onboardingCallPromptEnabled: false,
saveDataErrorExecution: 'all',
saveDataSuccessExecution: 'all',
saveManualExecutions: false,
@ -265,7 +264,6 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, {
this.userManagement.showSetupOnFirstLoad = !!settings.userManagement.showSetupOnFirstLoad;
}
this.api = settings.publicApi;
this.onboardingCallPromptEnabled = settings.onboardingCallPromptEnabled;
if (settings.sso?.ldap) {
this.ldap.loginEnabled = settings.sso.ldap.loginEnabled;
this.ldap.loginLabel = settings.sso.ldap.loginLabel;

View file

@ -16,7 +16,6 @@ import {
INVITE_USER_MODAL_KEY,
LOG_STREAM_MODAL_KEY,
MFA_SETUP_MODAL_KEY,
ONBOARDING_CALL_SIGNUP_MODAL_KEY,
PERSONALIZATION_MODAL_KEY,
STORES,
TAGS_MANAGER_MODAL_KEY,
@ -103,7 +102,6 @@ export const useUIStore = defineStore(STORES.UI, () => {
CONTACT_PROMPT_MODAL_KEY,
CREDENTIAL_SELECT_MODAL_KEY,
DUPLICATE_MODAL_KEY,
ONBOARDING_CALL_SIGNUP_MODAL_KEY,
PERSONALIZATION_MODAL_KEY,
INVITE_USER_MODAL_KEY,
TAGS_MANAGER_MODAL_KEY,
@ -453,24 +451,6 @@ export const useUIStore = defineStore(STORES.UI, () => {
openModal(CREDENTIAL_EDIT_MODAL_KEY);
};
const getNextOnboardingPrompt = async () => {
const instanceId = rootStore.instanceId;
const { currentUser } = userStore;
if (currentUser) {
return await onboardingApi.fetchNextOnboardingPrompt(instanceId, currentUser);
}
return null;
};
const applyForOnboardingCall = async (email: string) => {
const instanceId = rootStore.instanceId;
const { currentUser } = userStore;
if (currentUser) {
return await onboardingApi.applyForOnboardingCall(instanceId, currentUser, email);
}
return null;
};
const submitContactEmail = async (email: string, agree: boolean) => {
const instanceId = rootStore.instanceId;
const { currentUser } = userStore;
@ -668,8 +648,6 @@ export const useUIStore = defineStore(STORES.UI, () => {
openDeleteUserModal,
openExistingCredential,
openNewCredential,
getNextOnboardingPrompt,
applyForOnboardingCall,
submitContactEmail,
openCommunityPackageUninstallConfirmModal,
openCommunityPackageUpdateConfirmModal,

View file

@ -110,16 +110,6 @@ export function getPersonalizedNodeTypes(
return getPersonalizationSurveyV1(answers);
}
export function getAccountAge(currentUser: IUser): number {
if (currentUser.createdAt) {
const accountCreatedAt = new Date(currentUser.createdAt);
const today = new Date();
return Math.ceil((today.getTime() - accountCreatedAt.getTime()) / (1000 * 3600 * 24));
}
return -1;
}
function getPersonalizationSurveyV2OrLater(
answers:
| IPersonalizationSurveyAnswersV2

View file

@ -222,12 +222,9 @@ import {
import type { NotificationHandle } from 'element-plus';
import {
FIRST_ONBOARDING_PROMPT_TIMEOUT,
MAIN_HEADER_TABS,
MODAL_CANCEL,
MODAL_CONFIRM,
ONBOARDING_CALL_SIGNUP_MODAL_KEY,
ONBOARDING_PROMPT_TIMEBOX,
PLACEHOLDER_EMPTY_WORKFLOW_ID,
QUICKSTART_NOTE_NAME,
START_NODE_TYPE,
@ -332,7 +329,6 @@ import { useUsersStore } from '@/stores/users.store';
import { useWorkflowsEEStore } from '@/stores/workflows.ee.store';
import { useWorkflowsStore } from '@/stores/workflows.store';
import * as NodeViewUtils from '@/utils/nodeViewUtils';
import { getAccountAge } from '@/utils/userUtils';
import { getConnectionInfo, getNodeViewTab } from '@/utils/canvasUtils';
import {
AddConnectionCommand,
@ -891,38 +887,6 @@ export default defineComponent({
// TODO: This currently breaks since front-end hooks are still not updated to work with pinia store
void this.externalHooks.run('nodeView.mount').catch(() => {});
if (
this.currentUser?.personalizationAnswers !== null &&
this.settingsStore.onboardingCallPromptEnabled &&
this.currentUser &&
getAccountAge(this.currentUser) <= ONBOARDING_PROMPT_TIMEBOX
) {
const onboardingResponse = await this.uiStore.getNextOnboardingPrompt();
const promptTimeout =
onboardingResponse?.toast_sequence_number === 1 ? FIRST_ONBOARDING_PROMPT_TIMEOUT : 1000;
if (onboardingResponse?.title && onboardingResponse?.description) {
setTimeout(async () => {
this.showToast({
type: 'info',
title: onboardingResponse.title,
message: onboardingResponse.description,
duration: 0,
customClass: 'clickable',
closeOnClick: true,
onClick: () => {
this.$telemetry.track('user clicked onboarding toast', {
seq_num: onboardingResponse.toast_sequence_number,
title: onboardingResponse.title,
description: onboardingResponse.description,
});
this.uiStore.openModal(ONBOARDING_CALL_SIGNUP_MODAL_KEY);
},
});
}, promptTimeout);
}
}
sourceControlEventBus.on('pull', this.onSourceControlPull);
this.registerCustomAction({

View file

@ -2651,7 +2651,6 @@ export interface IN8nUISettings {
enabled: boolean;
host: string;
};
onboardingCallPromptEnabled: boolean;
missingPackages?: boolean;
executionMode: 'regular' | 'queue';
pushBackend: 'sse' | 'websocket';