Skip to content

Commit

Permalink
feat(dashboard): step conditions analytics (#7684)
Browse files Browse the repository at this point in the history
  • Loading branch information
LetItRock authored Feb 10, 2025
1 parent 421de97 commit 70ead81
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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.';
Expand Down Expand Up @@ -75,15 +78,17 @@ const getConditionsSchema = (fields: Array<{ value: string }>): z.ZodType<FormQu
};

export const EditStepConditionsForm = () => {
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(() => {
Expand Down Expand Up @@ -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);
};

Expand Down
25 changes: 3 additions & 22 deletions apps/dashboard/src/hooks/use-conditions-count.ts
Original file line number Diff line number Diff line change
@@ -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]);
};
77 changes: 77 additions & 0 deletions apps/dashboard/src/utils/conditions.ts
Original file line number Diff line number Diff line change
@@ -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<string>();

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<string>();

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);
};
2 changes: 2 additions & 0 deletions apps/dashboard/src/utils/telemetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
}

0 comments on commit 70ead81

Please sign in to comment.