diff --git a/packages/frontend/editor-ui/src/components/FocusPanel.vue b/packages/frontend/editor-ui/src/components/FocusPanel.vue index cdbe50f1ce..fff230df15 100644 --- a/packages/frontend/editor-ui/src/components/FocusPanel.vue +++ b/packages/frontend/editor-ui/src/components/FocusPanel.vue @@ -28,6 +28,7 @@ import { useDebounce } from '@/composables/useDebounce'; import { htmlEditorEventBus } from '@/event-bus'; import { hasFocusOnInput, isFocusableEl } from '@/utils/typesUtils'; import type { ResizeData, TargetNodeParameterContext } from '@/Interface'; +import { useTelemetry } from '@/composables/useTelemetry'; import { useThrottleFn } from '@vueuse/core'; import { useStyles } from '@/composables/useStyles'; import { useExecutionData } from '@/composables/useExecutionData'; @@ -53,6 +54,7 @@ const nodeHelpers = useNodeHelpers(); const focusPanelStore = useFocusPanelStore(); const workflowsStore = useWorkflowsStore(); const nodeTypesStore = useNodeTypesStore(); +const telemetry = useTelemetry(); const nodeSettingsParameters = useNodeSettingsParameters(); const environmentsStore = useEnvironmentsStore(); const deviceSupport = useDeviceSupport(); @@ -262,6 +264,22 @@ function optionSelected(command: string) { } } +function closeFocusPanel() { + telemetry.track('User closed focus panel', { + source: 'closeIcon', + parameters: focusPanelStore.focusedNodeParametersInTelemetryFormat, + }); + + focusPanelStore.closeFocusPanel(); +} + +function onExecute() { + telemetry.track( + 'User executed node from focus panel', + focusPanelStore.focusedNodeParametersInTelemetryFormat[0], + ); +} + const valueChangedDebounced = debounce(valueChanged, { debounceTime: 0 }); // Wait for editor to mount before focusing @@ -343,13 +361,14 @@ const onResizeThrottle = useThrottleFn(onResize, 10); :square="true" :hide-label="true" telemetry-source="focus" - > + @execute="onExecute" + /> diff --git a/packages/frontend/editor-ui/src/components/Node/NodeCreation.vue b/packages/frontend/editor-ui/src/components/Node/NodeCreation.vue index 6bd0ef7b16..2e5da6354f 100644 --- a/packages/frontend/editor-ui/src/components/Node/NodeCreation.vue +++ b/packages/frontend/editor-ui/src/components/Node/NodeCreation.vue @@ -21,6 +21,7 @@ import { useActions } from './NodeCreator/composables/useActions'; import KeyboardShortcutTooltip from '@/components/KeyboardShortcutTooltip.vue'; import AssistantIcon from '@n8n/design-system/components/AskAssistantIcon/AssistantIcon.vue'; import { useI18n } from '@n8n/i18n'; +import { useTelemetry } from '@/composables/useTelemetry'; import { useAssistantStore } from '@/stores/assistant.store'; type Props = { @@ -46,6 +47,7 @@ const uiStore = useUIStore(); const focusPanelStore = useFocusPanelStore(); const posthogStore = usePostHog(); const i18n = useI18n(); +const telemetry = useTelemetry(); const assistantStore = useAssistantStore(); const { getAddedNodesAndConnections } = useActions(); @@ -86,6 +88,15 @@ function nodeTypeSelected(value: NodeTypeSelectedPayload[]) { closeNodeCreator(true); } +function toggleFocusPanel() { + focusPanelStore.toggleFocusPanel(); + + telemetry.track(`User ${focusPanelStore.focusPanelActive ? 'opened' : 'closed'} focus panel`, { + source: 'canvasButton', + parameters: focusPanelStore.focusedNodeParametersInTelemetryFormat, + }); +} + function onAskAssistantButtonClick() { if (!assistantStore.chatWindowOpen) assistantStore.trackUserOpenedAssistant({ @@ -132,12 +143,7 @@ function onAskAssistantButtonClick() { :shortcut="{ keys: ['f'], shiftKey: true }" placement="left" > - + diff --git a/packages/frontend/editor-ui/src/components/ParameterInput.vue b/packages/frontend/editor-ui/src/components/ParameterInput.vue index ad269933d3..8987eb8712 100644 --- a/packages/frontend/editor-ui/src/components/ParameterInput.vue +++ b/packages/frontend/editor-ui/src/components/ParameterInput.vue @@ -78,6 +78,7 @@ import { isCredentialOnlyNodeType } from '@/utils/credentialOnlyNodes'; import { hasFocusOnInput, isBlurrableEl, isFocusableEl, isSelectableEl } from '@/utils/typesUtils'; import { completeExpressionSyntax, shouldConvertToExpression } from '@/utils/expressions'; import CssEditor from './CssEditor/CssEditor.vue'; +import { useFocusPanelStore } from '@/stores/focusPanel.store'; type Picker = { $emit: (arg0: string, arg1: Date) => void }; @@ -141,6 +142,7 @@ const workflowsStore = useWorkflowsStore(); const settingsStore = useSettingsStore(); const nodeTypesStore = useNodeTypesStore(); const uiStore = useUIStore(); +const focusPanelStore = useFocusPanelStore(); // ESLint: false positive // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents @@ -1000,6 +1002,10 @@ async function optionSelected(command: string) { case 'focus': nodeSettingsParameters.handleFocus(node.value, props.path, props.parameter); + telemetry.track('User opened focus panel', { + source: 'parameterButton', + parameters: focusPanelStore.focusedNodeParametersInTelemetryFormat, + }); return; } diff --git a/packages/frontend/editor-ui/src/stores/focusPanel.store.ts b/packages/frontend/editor-ui/src/stores/focusPanel.store.ts index 3ea132970c..d73caa2fe4 100644 --- a/packages/frontend/editor-ui/src/stores/focusPanel.store.ts +++ b/packages/frontend/editor-ui/src/stores/focusPanel.store.ts @@ -152,6 +152,16 @@ export const useFocusPanelStore = defineStore(STORES.FOCUS_PANEL, () => { return 'value' in p && 'node' in p; } + const focusedNodeParametersInTelemetryFormat = computed< + Array<{ parameterPath: string; nodeType: string; nodeId: string }> + >(() => + focusedNodeParameters.value.map((x) => ({ + parameterPath: x.parameterPath, + nodeType: isRichParameter(x) ? x.node.type : 'unresolved', + nodeId: x.nodeId, + })), + ); + // Ensure lastFocusTimestamp is set on initial load if panel is already active (e.g. after reload) watchOnce( () => currentFocusPanelData.value, @@ -165,6 +175,7 @@ export const useFocusPanelStore = defineStore(STORES.FOCUS_PANEL, () => { return { focusPanelActive, focusedNodeParameters, + focusedNodeParametersInTelemetryFormat, lastFocusTimestamp, focusPanelWidth, openWithFocusedNodeParameter, diff --git a/packages/frontend/editor-ui/src/views/NodeView.vue b/packages/frontend/editor-ui/src/views/NodeView.vue index 7ddd7b8f9f..47e6425250 100644 --- a/packages/frontend/editor-ui/src/views/NodeView.vue +++ b/packages/frontend/editor-ui/src/views/NodeView.vue @@ -1233,6 +1233,11 @@ function onToggleFocusPanel() { } focusPanelStore.toggleFocusPanel(); + telemetry.track(`User ${focusPanelStore.focusPanelActive ? 'opened' : 'closed'} focus panel`, { + source: 'canvasKeyboardShortcut', + parameters: focusPanelStore.focusedNodeParametersInTelemetryFormat, + parameterCount: focusPanelStore.focusedNodeParametersInTelemetryFormat.length, + }); } function closeNodeCreator() {