diff --git a/apps/dashboard/src/components/workflow-editor/steps/conditions/edit-step-conditions-form.tsx b/apps/dashboard/src/components/workflow-editor/steps/conditions/edit-step-conditions-form.tsx index f2fba495e0c..72e5b27d5b0 100644 --- a/apps/dashboard/src/components/workflow-editor/steps/conditions/edit-step-conditions-form.tsx +++ b/apps/dashboard/src/components/workflow-editor/steps/conditions/edit-step-conditions-form.tsx @@ -16,6 +16,9 @@ import { useWorkflow } from '@/components/workflow-editor/workflow-provider'; import { UnsavedChangesAlertDialog } from '@/components/unsaved-changes-alert-dialog'; import { useBeforeUnload } from '@/hooks/use-before-unload'; import { EditStepConditionsLayout } from './edit-step-conditions-layout'; +import { useTelemetry } from '@/hooks/use-telemetry'; +import { TelemetryEvent } from '@/utils/telemetry'; +import { countConditions, getUniqueOperators, getUniqueFieldNamespaces } from '@/utils/conditions'; const PAYLOAD_FIELD_PREFIX = 'payload.'; const SUBSCRIBER_DATA_FIELD_PREFIX = 'subscriber.data.'; @@ -75,15 +78,17 @@ const getConditionsSchema = (fields: Array<{ value: string }>): z.ZodType { + const track = useTelemetry(); const { workflow, step, update } = useWorkflow(); + const hasConditions = !!step?.controls.values.skip; const query = useMemo( () => // prepareRuleGroup and parseJsonLogic calls are needed to generate the unique ids on the query and rules, // otherwise the lib will do it and it will result in the form being dirty - step?.controls.values.skip + hasConditions ? prepareRuleGroup(parseJsonLogic(step.controls.values.skip as RQBJsonLogic)) : prepareRuleGroup({ combinator: 'and', rules: [] }), - [step] + [hasConditions, step] ); const { fields, variables } = useMemo(() => { @@ -122,7 +127,30 @@ export const EditStepConditionsForm = () => { updateStepData.controlValues!.skip = null; } - update(updateStepInWorkflow(workflow, step.stepId, updateStepData)); + update(updateStepInWorkflow(workflow, step.stepId, updateStepData), { + onSuccess: () => { + const uniqueFieldTypes: string[] = getUniqueFieldNamespaces(skip); + const uniqueOperators: string[] = getUniqueOperators(skip); + + if (!hasConditions) { + track(TelemetryEvent.STEP_CONDITIONS_ADDED, { + stepType: step.type, + fieldTypes: uniqueFieldTypes, + operators: uniqueOperators, + }); + } else { + const oldConditionsCount = countConditions(step.controls.values.skip as RQBJsonLogic); + const newConditionsCount = countConditions(skip); + + track(TelemetryEvent.STEP_CONDITIONS_UPDATED, { + stepType: step.type, + fieldTypes: uniqueFieldTypes, + operators: uniqueOperators, + type: newConditionsCount < oldConditionsCount ? 'deletion' : 'update', + }); + } + }, + }); form.reset(values); }; diff --git a/apps/dashboard/src/hooks/use-conditions-count.ts b/apps/dashboard/src/hooks/use-conditions-count.ts index 4789f678b13..de43ed2421b 100644 --- a/apps/dashboard/src/hooks/use-conditions-count.ts +++ b/apps/dashboard/src/hooks/use-conditions-count.ts @@ -1,27 +1,8 @@ import { useMemo } from 'react'; -import { RQBJsonLogic, RuleGroupType } from 'react-querybuilder'; -import { parseJsonLogic } from 'react-querybuilder/parseJsonLogic'; +import { RQBJsonLogic } from 'react-querybuilder'; -function countRules(query: RuleGroupType): number { - let count = 0; - - for (const rule of query.rules) { - if ('rules' in rule) { - count += countRules(rule); - } else { - count += 1; - } - } - - return count; -} +import { countConditions } from '@/utils/conditions'; export const useConditionsCount = (jsonLogic?: RQBJsonLogic) => { - return useMemo(() => { - if (!jsonLogic) return 0; - - const query = parseJsonLogic(jsonLogic); - - return countRules(query); - }, [jsonLogic]); + return useMemo(() => countConditions(jsonLogic), [jsonLogic]); }; diff --git a/apps/dashboard/src/utils/conditions.ts b/apps/dashboard/src/utils/conditions.ts new file mode 100644 index 00000000000..09d88f19c8f --- /dev/null +++ b/apps/dashboard/src/utils/conditions.ts @@ -0,0 +1,77 @@ +import { RuleGroupType, RQBJsonLogic } from 'react-querybuilder'; +import { parseJsonLogic } from 'react-querybuilder/parseJsonLogic'; + +function countRules(query: RuleGroupType): number { + let count = 0; + + for (const rule of query.rules) { + if ('rules' in rule) { + count += countRules(rule); + } else { + count += 1; + } + } + + return count; +} + +export const countConditions = (jsonLogic?: RQBJsonLogic) => { + if (!jsonLogic) return 0; + + const query = parseJsonLogic(jsonLogic); + + return countRules(query); +}; + +function recursiveGetUniqueFields(query: RuleGroupType): string[] { + const fields = new Set(); + + for (const rule of query.rules) { + if ('rules' in rule) { + // recursively get fields from nested rule groups + const nestedFields = recursiveGetUniqueFields(rule); + nestedFields.forEach((field) => fields.add(field)); + } else { + // add field from individual rule + const field = rule.field.split('.').shift(); + if (field) { + fields.add(field); + } + } + } + + return Array.from(fields); +} + +export const getUniqueFieldNamespaces = (jsonLogic?: RQBJsonLogic): string[] => { + if (!jsonLogic) return []; + + const query = parseJsonLogic(jsonLogic); + + return recursiveGetUniqueFields(query); +}; + +function recursiveGetUniqueOperators(query: RuleGroupType): string[] { + const operators = new Set(); + + for (const rule of query.rules) { + if ('rules' in rule) { + // recursively get operators from nested rule groups + const nestedOperators = recursiveGetUniqueOperators(rule); + nestedOperators.forEach((operator) => operators.add(operator)); + } else { + // add operator from individual rule + operators.add(rule.operator); + } + } + + return Array.from(operators); +} + +export const getUniqueOperators = (jsonLogic?: RQBJsonLogic): string[] => { + if (!jsonLogic) return []; + + const query = parseJsonLogic(jsonLogic); + + return recursiveGetUniqueOperators(query); +}; diff --git a/apps/dashboard/src/utils/telemetry.ts b/apps/dashboard/src/utils/telemetry.ts index 07751a6fa66..4e6a6478457 100644 --- a/apps/dashboard/src/utils/telemetry.ts +++ b/apps/dashboard/src/utils/telemetry.ts @@ -55,4 +55,6 @@ export enum TelemetryEvent { SUBSCRIBERS_PAGE_VISIT = 'Subscribers page visit', SIGN_UP_PAGE_VIEWED = 'Signup page viewed', SIGN_IN_PAGE_VIEWED = 'Signin page viewed', + STEP_CONDITIONS_ADDED = 'Step conditions added', + STEP_CONDITIONS_UPDATED = 'Step conditions updated', }