mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
feat: Add events to enable onboarding checklist (#5536)
* feat: Add new event hooks * fix: update event * feat: Add more functionality for webhooks * ⚡ Not sending onboarding checklist event from templates page * 🔥 Removing quotes added by mistake * ⚡ Added rest of events needed for onboarding checklist * 💄 Hiding appcues checklist inside iframes * 💄 Updating appcues selector * fix: remove unnessary fix * fix: fix schedule node * refactor: bake events into segment store * refactor: rename store * refactor: use node keys * refactor: remove unnessary * chore: clean up store * refactor: add key for event * fix: allow tracking on template pages * chore: remove comment * fix: buidl * refactor: block event if in iframe * fix: fix tracking nodes * refactor: track experiments once * fix: ensure tracking works * chore: remove comment * fix: lint * fix: lint --------- Co-authored-by: Milorad Filipovic <milorad@n8n.io>
This commit is contained in:
parent
ae634407a4
commit
20c4919513
|
@ -32,6 +32,7 @@ import {
|
||||||
IAbstractEventMessage,
|
IAbstractEventMessage,
|
||||||
FeatureFlags,
|
FeatureFlags,
|
||||||
ExecutionStatus,
|
ExecutionStatus,
|
||||||
|
ITelemetryTrackProperties,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
import { SignInType } from './constants';
|
import { SignInType } from './constants';
|
||||||
import { FAKE_DOOR_FEATURES, TRIGGER_NODE_FILTER, REGULAR_NODE_FILTER } from './constants';
|
import { FAKE_DOOR_FEATURES, TRIGGER_NODE_FILTER, REGULAR_NODE_FILTER } from './constants';
|
||||||
|
@ -67,6 +68,9 @@ declare global {
|
||||||
onFeatureFlags?(callback: (keys: string[], map: FeatureFlags) => void): void;
|
onFeatureFlags?(callback: (keys: string[], map: FeatureFlags) => void): void;
|
||||||
reloadFeatureFlags?(): void;
|
reloadFeatureFlags?(): void;
|
||||||
};
|
};
|
||||||
|
analytics?: {
|
||||||
|
track(event: string, proeprties?: ITelemetryTrackProperties): void;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -89,6 +89,7 @@ import { INodeParameters, INodeProperties, INodePropertyMode, IParameterLabel }
|
||||||
import { BaseTextKey } from '@/plugins/i18n';
|
import { BaseTextKey } from '@/plugins/i18n';
|
||||||
import { mapStores } from 'pinia';
|
import { mapStores } from 'pinia';
|
||||||
import { useNDVStore } from '@/stores/ndv';
|
import { useNDVStore } from '@/stores/ndv';
|
||||||
|
import { useSegment } from '@/stores/segment';
|
||||||
import { externalHooks } from '@/mixins/externalHooks';
|
import { externalHooks } from '@/mixins/externalHooks';
|
||||||
|
|
||||||
export default mixins(showMessage, externalHooks).extend({
|
export default mixins(showMessage, externalHooks).extend({
|
||||||
|
@ -328,7 +329,8 @@ export default mixins(showMessage, externalHooks).extend({
|
||||||
success: true,
|
success: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.$externalHooks().run('parameterInputFull.mappedData');
|
const segment = useSegment();
|
||||||
|
segment.track(segment.EVENTS.MAPPED_DATA);
|
||||||
}
|
}
|
||||||
this.forceShowExpression = false;
|
this.forceShowExpression = false;
|
||||||
}, 200);
|
}, 200);
|
||||||
|
|
|
@ -33,6 +33,7 @@ import { useNodeTypesStore } from '@/stores/nodeTypes';
|
||||||
import { useCredentialsStore } from '@/stores/credentials';
|
import { useCredentialsStore } from '@/stores/credentials';
|
||||||
import { useSettingsStore } from '@/stores/settings';
|
import { useSettingsStore } from '@/stores/settings';
|
||||||
import { parse } from 'flatted';
|
import { parse } from 'flatted';
|
||||||
|
import { useSegment } from '@/stores/segment';
|
||||||
|
|
||||||
export const pushConnection = mixins(
|
export const pushConnection = mixins(
|
||||||
externalHooks,
|
externalHooks,
|
||||||
|
@ -58,6 +59,7 @@ export const pushConnection = mixins(
|
||||||
useUIStore,
|
useUIStore,
|
||||||
useWorkflowsStore,
|
useWorkflowsStore,
|
||||||
useSettingsStore,
|
useSettingsStore,
|
||||||
|
useSegment,
|
||||||
),
|
),
|
||||||
sessionId(): string {
|
sessionId(): string {
|
||||||
return this.rootStore.sessionId;
|
return this.rootStore.sessionId;
|
||||||
|
@ -515,6 +517,9 @@ export const pushConnection = mixins(
|
||||||
runDataExecutedStartData: runDataExecuted.data.startData,
|
runDataExecutedStartData: runDataExecuted.data.startData,
|
||||||
resultDataError: runDataExecuted.data.resultData.error,
|
resultDataError: runDataExecuted.data.resultData.error,
|
||||||
});
|
});
|
||||||
|
if (!runDataExecuted.data.resultData.error) {
|
||||||
|
this.segmentStore.trackSuccessfulWorkflowExecution(runDataExecuted);
|
||||||
|
}
|
||||||
} else if (receivedData.type === 'executionStarted') {
|
} else if (receivedData.type === 'executionStarted') {
|
||||||
const pushData = receivedData.data;
|
const pushData = receivedData.data;
|
||||||
|
|
||||||
|
|
|
@ -80,6 +80,11 @@ export const useNodeTypesStore = defineStore(STORES.NODE_TYPES, {
|
||||||
return !!(nodeType && nodeType.group.includes('trigger'));
|
return !!(nodeType && nodeType.group.includes('trigger'));
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
isCoreNodeType() {
|
||||||
|
return (nodeType: INodeTypeDescription) => {
|
||||||
|
return nodeType.codex?.categories?.includes('Core Nodes');
|
||||||
|
};
|
||||||
|
},
|
||||||
visibleNodeTypes(): INodeTypeDescription[] {
|
visibleNodeTypes(): INodeTypeDescription[] {
|
||||||
return this.allLatestNodeTypes.filter((nodeType: INodeTypeDescription) => !nodeType.hidden);
|
return this.allLatestNodeTypes.filter((nodeType: INodeTypeDescription) => !nodeType.hidden);
|
||||||
},
|
},
|
||||||
|
|
|
@ -2,21 +2,25 @@ import { ref, Ref, watch } from 'vue';
|
||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
import { useUsersStore } from '@/stores/users';
|
import { useUsersStore } from '@/stores/users';
|
||||||
import { useRootStore } from '@/stores/n8nRootStore';
|
import { useRootStore } from '@/stores/n8nRootStore';
|
||||||
import { useSettingsStore } from './settings';
|
import { useSettingsStore } from '@/stores/settings';
|
||||||
import { FeatureFlags } from 'n8n-workflow';
|
import { FeatureFlags } from 'n8n-workflow';
|
||||||
import { EXPERIMENTS_TO_TRACK } from '@/constants';
|
import { EXPERIMENTS_TO_TRACK, ONBOARDING_EXPERIMENT } from '@/constants';
|
||||||
import { useTelemetryStore } from './telemetry';
|
import { useTelemetryStore } from './telemetry';
|
||||||
import { runExternalHook } from '@/mixins/externalHooks';
|
import { useSegment } from './segment';
|
||||||
import { useWebhooksStore } from './webhooks';
|
import { debounce } from 'lodash-es';
|
||||||
|
|
||||||
export const usePostHogStore = defineStore('posthog', () => {
|
const EVENTS = {
|
||||||
|
IS_PART_OF_EXPERIMENT: 'User is part of experiment',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const usePostHog = defineStore('posthog', () => {
|
||||||
const usersStore = useUsersStore();
|
const usersStore = useUsersStore();
|
||||||
const settingsStore = useSettingsStore();
|
const settingsStore = useSettingsStore();
|
||||||
const telemetryStore = useTelemetryStore();
|
const telemetryStore = useTelemetryStore();
|
||||||
const rootStore = useRootStore();
|
const rootStore = useRootStore();
|
||||||
|
const segmentStore = useSegment();
|
||||||
|
|
||||||
const featureFlags: Ref<FeatureFlags | null> = ref(null);
|
const featureFlags: Ref<FeatureFlags | null> = ref(null);
|
||||||
const initialized: Ref<boolean> = ref(false);
|
|
||||||
const trackedDemoExp: Ref<FeatureFlags> = ref({});
|
const trackedDemoExp: Ref<FeatureFlags> = ref({});
|
||||||
|
|
||||||
const reset = () => {
|
const reset = () => {
|
||||||
|
@ -72,54 +76,42 @@ export const usePostHogStore = defineStore('posthog', () => {
|
||||||
debug: config.debug,
|
debug: config.debug,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (evaluatedFeatureFlags) {
|
window.posthog?.init(config.apiKey, options);
|
||||||
|
identify();
|
||||||
|
|
||||||
|
if (evaluatedFeatureFlags && Object.keys(evaluatedFeatureFlags).length) {
|
||||||
featureFlags.value = evaluatedFeatureFlags;
|
featureFlags.value = evaluatedFeatureFlags;
|
||||||
options.bootstrap = {
|
options.bootstrap = {
|
||||||
distinctId,
|
distinctId,
|
||||||
featureFlags: evaluatedFeatureFlags,
|
featureFlags: evaluatedFeatureFlags,
|
||||||
};
|
};
|
||||||
|
trackExperiments(evaluatedFeatureFlags);
|
||||||
|
} else {
|
||||||
|
// depend on client side evaluation if serverside evaluation fails
|
||||||
|
window.posthog?.onFeatureFlags?.((keys: string[], map: FeatureFlags) => {
|
||||||
|
featureFlags.value = map;
|
||||||
|
trackExperiments(map);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
window.posthog?.init(config.apiKey, options);
|
|
||||||
|
|
||||||
identify();
|
|
||||||
|
|
||||||
initialized.value = true;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const trackExperiment = (name: string) => {
|
const trackExperiments = debounce((featureFlags: FeatureFlags) => {
|
||||||
const curr = featureFlags.value;
|
EXPERIMENTS_TO_TRACK.forEach((name) => trackExperiment(featureFlags, name));
|
||||||
const prev = trackedDemoExp.value;
|
}, 2000);
|
||||||
|
|
||||||
if (!curr || curr[name] === undefined) {
|
const trackExperiment = (featureFlags: FeatureFlags, name: string) => {
|
||||||
return;
|
const variant = featureFlags[name];
|
||||||
}
|
telemetryStore.track(EVENTS.IS_PART_OF_EXPERIMENT, {
|
||||||
|
|
||||||
if (curr[name] === prev[name]) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const variant = curr[name];
|
|
||||||
telemetryStore.track('User is part of experiment', {
|
|
||||||
name,
|
name,
|
||||||
variant,
|
variant,
|
||||||
});
|
});
|
||||||
|
|
||||||
trackedDemoExp.value[name] = variant;
|
trackedDemoExp.value[name] = variant;
|
||||||
runExternalHook('posthog.featureFlagsUpdated', useWebhooksStore(), {
|
|
||||||
name,
|
|
||||||
variant,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
watch(
|
if (name === ONBOARDING_EXPERIMENT.name && variant === ONBOARDING_EXPERIMENT.variant) {
|
||||||
() => featureFlags.value,
|
segmentStore.showAppCuesChecklist();
|
||||||
() => {
|
}
|
||||||
setTimeout(() => {
|
};
|
||||||
EXPERIMENTS_TO_TRACK.forEach(trackExperiment);
|
|
||||||
}, 0);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
init,
|
init,
|
||||||
|
|
133
packages/editor-ui/src/stores/segment.ts
Normal file
133
packages/editor-ui/src/stores/segment.ts
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
import {
|
||||||
|
CODE_NODE_TYPE,
|
||||||
|
HTTP_REQUEST_NODE_TYPE,
|
||||||
|
MANUAL_TRIGGER_NODE_TYPE,
|
||||||
|
SCHEDULE_TRIGGER_NODE_TYPE,
|
||||||
|
SET_NODE_TYPE,
|
||||||
|
WEBHOOK_NODE_TYPE,
|
||||||
|
} from '@/constants';
|
||||||
|
import { ITelemetryTrackProperties } from 'n8n-workflow';
|
||||||
|
import { defineStore } from 'pinia';
|
||||||
|
import { useSettingsStore } from '@/stores/settings';
|
||||||
|
import { INodeTypeDescription, IRun } from 'n8n-workflow';
|
||||||
|
import { useWorkflowsStore } from '@/stores/workflows';
|
||||||
|
import { useNodeTypesStore } from '@/stores/nodeTypes';
|
||||||
|
|
||||||
|
const EVENTS = {
|
||||||
|
SHOW_CHECKLIST: 'Show checklist',
|
||||||
|
ADDED_MANUAL_TRIGGER: 'User added manual trigger',
|
||||||
|
ADDED_SCHEDULE_TRIGGER: 'User added schedule trigger',
|
||||||
|
ADDED_DATA_TRIGGER: 'User added data trigger',
|
||||||
|
RECEIEVED_MULTIPLE_DATA_ITEMS: 'User received multiple data items',
|
||||||
|
EXECUTED_MANUAL_TRIGGER: 'User executed manual trigger successfully',
|
||||||
|
EXECUTED_SCHEDULE_TRIGGER: 'User executed schedule trigger successfully',
|
||||||
|
EXECUTED_DATA_NODE_TRIGGER: 'User executed data node successfully',
|
||||||
|
MAPPED_DATA: 'User mapped data',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useSegment = defineStore('segment', () => {
|
||||||
|
const nodeTypesStore = useNodeTypesStore();
|
||||||
|
const workflowsStore = useWorkflowsStore();
|
||||||
|
const settingsStore = useSettingsStore();
|
||||||
|
|
||||||
|
const track = (eventName: string, properties?: ITelemetryTrackProperties) => {
|
||||||
|
if (settingsStore.telemetry.enabled) {
|
||||||
|
window.analytics?.track(eventName, properties);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const showAppCuesChecklist = () => {
|
||||||
|
const isInIframe = window.location !== window.parent.location;
|
||||||
|
if (isInIframe) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
track(EVENTS.SHOW_CHECKLIST);
|
||||||
|
};
|
||||||
|
|
||||||
|
const trackAddedTrigger = (nodeTypeName: string) => {
|
||||||
|
if (!nodeTypesStore.isTriggerNode(nodeTypeName)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nodeTypeName === MANUAL_TRIGGER_NODE_TYPE) {
|
||||||
|
track(EVENTS.ADDED_MANUAL_TRIGGER);
|
||||||
|
} else if (nodeTypeName === SCHEDULE_TRIGGER_NODE_TYPE) {
|
||||||
|
track(EVENTS.ADDED_SCHEDULE_TRIGGER);
|
||||||
|
} else {
|
||||||
|
track(EVENTS.ADDED_DATA_TRIGGER);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const trackSuccessfulWorkflowExecution = (runData: IRun) => {
|
||||||
|
const dataNodeTypes: Set<string> = new Set<string>();
|
||||||
|
const multipleOutputNodes: Set<string> = new Set<string>();
|
||||||
|
let hasManualTrigger = false;
|
||||||
|
let hasScheduleTrigger = false;
|
||||||
|
for (const nodeName of Object.keys(runData.data.resultData.runData)) {
|
||||||
|
const nodeRunData = runData.data.resultData.runData[nodeName];
|
||||||
|
const node = workflowsStore.getNodeByName(nodeName);
|
||||||
|
const nodeTypeName = node ? node.type : 'unknown';
|
||||||
|
if (nodeRunData[0].data && nodeRunData[0].data.main.some((out) => out && out?.length > 1)) {
|
||||||
|
multipleOutputNodes.add(nodeTypeName);
|
||||||
|
}
|
||||||
|
if (node && !node.disabled) {
|
||||||
|
const nodeType = nodeTypesStore.getNodeType(node.type, node.typeVersion);
|
||||||
|
if (isDataNodeType(nodeType)) {
|
||||||
|
dataNodeTypes.add(nodeTypeName);
|
||||||
|
}
|
||||||
|
if (isManualTriggerNode(nodeType)) {
|
||||||
|
hasManualTrigger = true;
|
||||||
|
}
|
||||||
|
if (isScheduleTriggerNode(nodeType)) {
|
||||||
|
hasScheduleTrigger = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (multipleOutputNodes.size > 0) {
|
||||||
|
track(EVENTS.RECEIEVED_MULTIPLE_DATA_ITEMS, {
|
||||||
|
nodeTypes: Array.from(multipleOutputNodes),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (dataNodeTypes.size > 0) {
|
||||||
|
track(EVENTS.EXECUTED_DATA_NODE_TRIGGER, {
|
||||||
|
nodeTypes: Array.from(dataNodeTypes),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (hasManualTrigger) {
|
||||||
|
track(EVENTS.EXECUTED_MANUAL_TRIGGER);
|
||||||
|
}
|
||||||
|
if (hasScheduleTrigger) {
|
||||||
|
track(EVENTS.EXECUTED_SCHEDULE_TRIGGER);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const isManualTriggerNode = (nodeType: INodeTypeDescription | null): boolean => {
|
||||||
|
return !!nodeType && nodeType.name === MANUAL_TRIGGER_NODE_TYPE;
|
||||||
|
};
|
||||||
|
|
||||||
|
const isScheduleTriggerNode = (nodeType: INodeTypeDescription | null): boolean => {
|
||||||
|
return !!nodeType && nodeType.name === SCHEDULE_TRIGGER_NODE_TYPE;
|
||||||
|
};
|
||||||
|
|
||||||
|
const isDataNodeType = (nodeType: INodeTypeDescription | null): boolean => {
|
||||||
|
if (!nodeType) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const includeCoreNodes = [
|
||||||
|
HTTP_REQUEST_NODE_TYPE,
|
||||||
|
CODE_NODE_TYPE,
|
||||||
|
SET_NODE_TYPE,
|
||||||
|
WEBHOOK_NODE_TYPE,
|
||||||
|
];
|
||||||
|
return !nodeTypesStore.isCoreNodeType(nodeType) || includeCoreNodes.includes(nodeType.name);
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
showAppCuesChecklist,
|
||||||
|
track,
|
||||||
|
trackAddedTrigger,
|
||||||
|
trackSuccessfulWorkflowExecution,
|
||||||
|
EVENTS,
|
||||||
|
};
|
||||||
|
});
|
|
@ -34,7 +34,7 @@ import { getPersonalizedNodeTypes, isAuthorized, PERMISSIONS, ROLE } from '@/uti
|
||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import { useRootStore } from './n8nRootStore';
|
import { useRootStore } from './n8nRootStore';
|
||||||
import { usePostHogStore } from './posthog';
|
import { usePostHog } from './posthog';
|
||||||
import { useSettingsStore } from './settings';
|
import { useSettingsStore } from './settings';
|
||||||
import { useUIStore } from './ui';
|
import { useUIStore } from './ui';
|
||||||
|
|
||||||
|
@ -149,7 +149,7 @@ export const useUsersStore = defineStore(STORES.USERS, {
|
||||||
this.addUsers([user]);
|
this.addUsers([user]);
|
||||||
this.currentUserId = user.id;
|
this.currentUserId = user.id;
|
||||||
|
|
||||||
usePostHogStore().init(user.featureFlags);
|
usePostHog().init(user.featureFlags);
|
||||||
},
|
},
|
||||||
async loginWithCreds(params: { email: string; password: string }): Promise<void> {
|
async loginWithCreds(params: { email: string; password: string }): Promise<void> {
|
||||||
const rootStore = useRootStore();
|
const rootStore = useRootStore();
|
||||||
|
@ -161,13 +161,13 @@ export const useUsersStore = defineStore(STORES.USERS, {
|
||||||
this.addUsers([user]);
|
this.addUsers([user]);
|
||||||
this.currentUserId = user.id;
|
this.currentUserId = user.id;
|
||||||
|
|
||||||
usePostHogStore().init(user.featureFlags);
|
usePostHog().init(user.featureFlags);
|
||||||
},
|
},
|
||||||
async logout(): Promise<void> {
|
async logout(): Promise<void> {
|
||||||
const rootStore = useRootStore();
|
const rootStore = useRootStore();
|
||||||
await logout(rootStore.getRestApiContext);
|
await logout(rootStore.getRestApiContext);
|
||||||
this.currentUserId = null;
|
this.currentUserId = null;
|
||||||
usePostHogStore().reset();
|
usePostHog().reset();
|
||||||
},
|
},
|
||||||
async preOwnerSetup() {
|
async preOwnerSetup() {
|
||||||
return preOwnerSetup(useRootStore().getRestApiContext);
|
return preOwnerSetup(useRootStore().getRestApiContext);
|
||||||
|
@ -208,7 +208,7 @@ export const useUsersStore = defineStore(STORES.USERS, {
|
||||||
this.currentUserId = user.id;
|
this.currentUserId = user.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
usePostHogStore().init(user.featureFlags);
|
usePostHog().init(user.featureFlags);
|
||||||
},
|
},
|
||||||
async sendForgotPasswordEmail(params: { email: string }): Promise<void> {
|
async sendForgotPasswordEmail(params: { email: string }): Promise<void> {
|
||||||
const rootStore = useRootStore();
|
const rootStore = useRootStore();
|
||||||
|
|
|
@ -268,6 +268,7 @@ import { nodeViewEventBus } from '@/event-bus/node-view-event-bus';
|
||||||
import { useWorkflowsStore } from '@/stores/workflows';
|
import { useWorkflowsStore } from '@/stores/workflows';
|
||||||
import { useRootStore } from '@/stores/n8nRootStore';
|
import { useRootStore } from '@/stores/n8nRootStore';
|
||||||
import { useNDVStore } from '@/stores/ndv';
|
import { useNDVStore } from '@/stores/ndv';
|
||||||
|
import { useSegment } from '@/stores/segment';
|
||||||
import { useTemplatesStore } from '@/stores/templates';
|
import { useTemplatesStore } from '@/stores/templates';
|
||||||
import { useNodeTypesStore } from '@/stores/nodeTypes';
|
import { useNodeTypesStore } from '@/stores/nodeTypes';
|
||||||
import { useCredentialsStore } from '@/stores/credentials';
|
import { useCredentialsStore } from '@/stores/credentials';
|
||||||
|
@ -304,7 +305,7 @@ import {
|
||||||
N8nPlusEndpointType,
|
N8nPlusEndpointType,
|
||||||
EVENT_PLUS_ENDPOINT_CLICK,
|
EVENT_PLUS_ENDPOINT_CLICK,
|
||||||
} from '@/plugins/endpoints/N8nPlusEndpointType';
|
} from '@/plugins/endpoints/N8nPlusEndpointType';
|
||||||
import { usePostHogStore } from '@/stores/posthog';
|
import { usePostHog } from '@/stores/posthog';
|
||||||
|
|
||||||
interface AddNodeOptions {
|
interface AddNodeOptions {
|
||||||
position?: XYPosition;
|
position?: XYPosition;
|
||||||
|
@ -1925,6 +1926,7 @@ export default mixins(
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.$externalHooks().run('nodeView.addNodeButton', { nodeTypeName });
|
this.$externalHooks().run('nodeView.addNodeButton', { nodeTypeName });
|
||||||
|
useSegment().trackAddedTrigger(nodeTypeName);
|
||||||
const trackProperties: ITelemetryTrackProperties = {
|
const trackProperties: ITelemetryTrackProperties = {
|
||||||
node_type: nodeTypeName,
|
node_type: nodeTypeName,
|
||||||
is_auto_add: isAutoAdd,
|
is_auto_add: isAutoAdd,
|
||||||
|
@ -2498,9 +2500,7 @@ export default mixins(
|
||||||
},
|
},
|
||||||
async tryToAddWelcomeSticky(): Promise<void> {
|
async tryToAddWelcomeSticky(): Promise<void> {
|
||||||
const newWorkflow = this.workflowData;
|
const newWorkflow = this.workflowData;
|
||||||
if (
|
if (usePostHog().isVariantEnabled(ASSUMPTION_EXPERIMENT.name, ASSUMPTION_EXPERIMENT.video)) {
|
||||||
usePostHogStore().isVariantEnabled(ASSUMPTION_EXPERIMENT.name, ASSUMPTION_EXPERIMENT.video)
|
|
||||||
) {
|
|
||||||
// For novice users (onboardingFlowEnabled == true)
|
// For novice users (onboardingFlowEnabled == true)
|
||||||
// Inject welcome sticky note and zoom to fit
|
// Inject welcome sticky note and zoom to fit
|
||||||
|
|
||||||
|
|
|
@ -138,7 +138,7 @@ import { useUIStore } from '@/stores/ui';
|
||||||
import { useSettingsStore } from '@/stores/settings';
|
import { useSettingsStore } from '@/stores/settings';
|
||||||
import { useUsersStore } from '@/stores/users';
|
import { useUsersStore } from '@/stores/users';
|
||||||
import { useWorkflowsStore } from '@/stores/workflows';
|
import { useWorkflowsStore } from '@/stores/workflows';
|
||||||
import { usePostHogStore } from '@/stores/posthog';
|
import { usePostHog } from '@/stores/posthog';
|
||||||
|
|
||||||
type IResourcesListLayoutInstance = Vue & { sendFiltersTelemetry: (source: string) => void };
|
type IResourcesListLayoutInstance = Vue & { sendFiltersTelemetry: (source: string) => void };
|
||||||
|
|
||||||
|
@ -185,10 +185,7 @@ export default mixins(showMessage, debounceHelper, newVersions).extend({
|
||||||
return !!this.workflowsStore.activeWorkflows.length;
|
return !!this.workflowsStore.activeWorkflows.length;
|
||||||
},
|
},
|
||||||
isDemoTest(): boolean {
|
isDemoTest(): boolean {
|
||||||
return usePostHogStore().isVariantEnabled(
|
return usePostHog().isVariantEnabled(ASSUMPTION_EXPERIMENT.name, ASSUMPTION_EXPERIMENT.demo);
|
||||||
ASSUMPTION_EXPERIMENT.name,
|
|
||||||
ASSUMPTION_EXPERIMENT.demo,
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
statusFilterOptions(): Array<{ label: string; value: string | boolean }> {
|
statusFilterOptions(): Array<{ label: string; value: string | boolean }> {
|
||||||
return [
|
return [
|
||||||
|
|
Loading…
Reference in a new issue