Skip to content

Commit 421cd4c

Browse files
danyelfclaude
andcommitted
feat: Add comprehensive tracking for explore actions and user interactions
This adds instrumentation to track all explore action interactions throughout the UI, capturing what action was chosen and where it was triggered from. Tracking categories: - Explore actions: Track WHAT action (row_count, profile_diff, value_diff, etc.) and WHERE it was triggered (lineage menu, node sidebar, schema view, etc.) - Form interactions: Track when users click Execute vs Cancel on action forms - Lineage selections: Track selection actions (parent/child nodes, upstream/downstream) Implementation: - All action names and sources use constants (no magic strings) - Constants defined in track.ts: EXPLORE_ACTION, EXPLORE_SOURCE, EXPLORE_FORM_EVENT, LINEAGE_SELECTION_ACTION - Type guards prevent tracking non-explore actions - Console fallback for development when Amplitude not initialized Files modified: - track.ts: Added tracking constants and functions - RunModal.tsx: Track execute/cancel on explore action forms - useValueDiffAlertDialog.tsx: Track value diff confirmation dialog - LineageViewContextMenu.tsx: Track explore actions and lineage selections - NodeView.tsx: Track actions from node sidebar - ColumnNameCell.tsx: Track actions from schema view - useMultiNodesAction.ts: Track multi-node actions from top bar 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> Signed-off-by: danyel fisher <[email protected]>
1 parent b25f087 commit 421cd4c

File tree

7 files changed

+307
-3
lines changed

7 files changed

+307
-3
lines changed

js/src/components/lineage/LineageViewContextMenu.tsx

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,13 @@ import { BiArrowFromBottom, BiArrowToBottom } from "react-icons/bi";
44
import { FaRegDotCircle } from "react-icons/fa";
55
import SetupConnectionPopover from "@/components/app/SetupConnectionPopover";
66
import { SubmitRunTrackProps } from "@/lib/api/runs";
7+
import {
8+
EXPLORE_ACTION,
9+
EXPLORE_SOURCE,
10+
LINEAGE_SELECTION_ACTION,
11+
trackExploreAction,
12+
trackLineageSelection,
13+
} from "@/lib/api/track";
714
import { formatSelectColumns } from "@/lib/formatSelect";
815
import { useLineageGraphContext } from "@/lib/hooks/LineageGraphContext";
916
import { useRecceActionContext } from "@/lib/hooks/RecceActionContext";
@@ -278,6 +285,13 @@ export const ModelNodeContextMenu = ({
278285
itemIcon: <Icon as={rowCountAndRowCountRun.icon} />,
279286
isDisabled: isQueryDisabled,
280287
action: () => {
288+
trackExploreAction({
289+
action: singleEnv
290+
? EXPLORE_ACTION.ROW_COUNT
291+
: EXPLORE_ACTION.ROW_COUNT_DIFF,
292+
source: EXPLORE_SOURCE.LINEAGE_VIEW_CONTEXT_MENU,
293+
node_count: 1,
294+
});
281295
runAction(
282296
singleEnv ? "row_count" : "row_count_diff",
283297
{ node_names: [modelNode.name] },
@@ -296,6 +310,13 @@ export const ModelNodeContextMenu = ({
296310
isDisabled: isQueryDisabled,
297311
action: () => {
298312
const columns = Array.from(getNodeColumnSet(node.id));
313+
trackExploreAction({
314+
action: singleEnv
315+
? EXPLORE_ACTION.PROFILE
316+
: EXPLORE_ACTION.PROFILE_DIFF,
317+
source: EXPLORE_SOURCE.LINEAGE_VIEW_CONTEXT_MENU,
318+
node_count: 1,
319+
});
299320
runAction(
300321
singleEnv ? "profile" : "profile_diff",
301322
{ model: modelNode.name, columns },
@@ -313,6 +334,11 @@ export const ModelNodeContextMenu = ({
313334
isDisabled: isQueryDisabled,
314335
action: () => {
315336
const columns = Array.from(getNodeColumnSet(node.id));
337+
trackExploreAction({
338+
action: EXPLORE_ACTION.VALUE_DIFF,
339+
source: EXPLORE_SOURCE.LINEAGE_VIEW_CONTEXT_MENU,
340+
node_count: 1,
341+
});
316342
runAction(
317343
"value_diff",
318344
{ model: modelNode.name, columns },
@@ -334,27 +360,39 @@ export const ModelNodeContextMenu = ({
334360
label: "Select Parent Nodes",
335361
itemIcon: <BiArrowFromBottom />,
336362
action: () => {
363+
trackLineageSelection({
364+
action: LINEAGE_SELECTION_ACTION.SELECT_PARENT_NODES,
365+
});
337366
selectParentNodes(node.id, 1);
338367
},
339368
});
340369
menuItems.push({
341370
label: "Select Child Nodes",
342371
itemIcon: <BiArrowToBottom />,
343372
action: () => {
373+
trackLineageSelection({
374+
action: LINEAGE_SELECTION_ACTION.SELECT_CHILD_NODES,
375+
});
344376
selectChildNodes(node.id, 1);
345377
},
346378
});
347379
menuItems.push({
348380
label: "Select All Upstream Nodes",
349381
itemIcon: <BiArrowFromBottom />,
350382
action: () => {
383+
trackLineageSelection({
384+
action: LINEAGE_SELECTION_ACTION.SELECT_ALL_UPSTREAM,
385+
});
351386
selectParentNodes(node.id);
352387
},
353388
});
354389
menuItems.push({
355390
label: "Select All Downstream Nodes",
356391
itemIcon: <BiArrowToBottom />,
357392
action: () => {
393+
trackLineageSelection({
394+
action: LINEAGE_SELECTION_ACTION.SELECT_ALL_DOWNSTREAM,
395+
});
358396
selectChildNodes(node.id);
359397
},
360398
});
@@ -400,6 +438,11 @@ export const ColumnNodeContextMenu = ({
400438
};
401439

402440
const handleProfileDiff = () => {
441+
trackExploreAction({
442+
action: EXPLORE_ACTION.PROFILE_DIFF,
443+
source: EXPLORE_SOURCE.LINEAGE_VIEW_CONTEXT_MENU,
444+
node_count: 1,
445+
});
403446
runAction(
404447
"profile_diff",
405448
{ model: modelNode.name, columns: [column] },
@@ -408,6 +451,11 @@ export const ColumnNodeContextMenu = ({
408451
};
409452

410453
const handleHistogramDiff = () => {
454+
trackExploreAction({
455+
action: EXPLORE_ACTION.HISTOGRAM_DIFF,
456+
source: EXPLORE_SOURCE.LINEAGE_VIEW_CONTEXT_MENU,
457+
node_count: 1,
458+
});
411459
runAction(
412460
"histogram_diff",
413461
{ model: modelNode.name, column_name: column, column_type: columnType },
@@ -416,6 +464,11 @@ export const ColumnNodeContextMenu = ({
416464
};
417465

418466
const handleTopkDiff = () => {
467+
trackExploreAction({
468+
action: EXPLORE_ACTION.TOP_K_DIFF,
469+
source: EXPLORE_SOURCE.LINEAGE_VIEW_CONTEXT_MENU,
470+
node_count: 1,
471+
});
419472
runAction(
420473
"top_k_diff",
421474
{ model: modelNode.name, column_name: column, k: 50 },
@@ -424,6 +477,11 @@ export const ColumnNodeContextMenu = ({
424477
};
425478

426479
const handleValueDiff = () => {
480+
trackExploreAction({
481+
action: EXPLORE_ACTION.VALUE_DIFF,
482+
source: EXPLORE_SOURCE.LINEAGE_VIEW_CONTEXT_MENU,
483+
node_count: 1,
484+
});
427485
runAction(
428486
"value_diff",
429487
{ model: modelNode.name, columns: [column] },

js/src/components/lineage/NodeView.tsx

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,12 @@ import SetupConnectionPopover from "@/components/app/SetupConnectionPopover";
1919
import { Tooltip } from "@/components/ui/tooltip";
2020
import { DisableTooltipMessages } from "@/constants/tooltipMessage";
2121
import { createSchemaDiffCheck } from "@/lib/api/schemacheck";
22-
import { trackPreviewChange } from "@/lib/api/track";
22+
import {
23+
EXPLORE_ACTION,
24+
EXPLORE_SOURCE,
25+
trackExploreAction,
26+
trackPreviewChange,
27+
} from "@/lib/api/track";
2328
import { formatSelectColumns } from "@/lib/formatSelect";
2429
import { useLineageGraphContext } from "@/lib/hooks/LineageGraphContext";
2530
import { useRecceActionContext } from "@/lib/hooks/RecceActionContext";
@@ -63,13 +68,23 @@ export function NodeView({ node, onCloseNode }: NodeViewProps) {
6368
const { isActionAvailable } = useLineageGraphContext();
6469

6570
const refetchRowCount = () => {
71+
trackExploreAction({
72+
action: EXPLORE_ACTION.ROW_COUNT,
73+
source: EXPLORE_SOURCE.SCHEMA_ROW_COUNT_BUTTON,
74+
node_count: 1,
75+
});
6676
runAction(
6777
"row_count",
6878
{ node_names: [node.data.name] },
6979
{ showForm: false, showLast: false },
7080
);
7181
};
7282
const refetchRowCountDiff = () => {
83+
trackExploreAction({
84+
action: EXPLORE_ACTION.ROW_COUNT_DIFF,
85+
source: EXPLORE_SOURCE.SCHEMA_ROW_COUNT_BUTTON,
86+
node_count: 1,
87+
});
7388
runAction(
7489
"row_count_diff",
7590
{ node_names: [node.data.name] },
@@ -257,6 +272,11 @@ function SingleEnvironmentMenuButton({
257272
fontSize="14px"
258273
disabled={isAddedOrRemoved}
259274
onClick={() => {
275+
trackExploreAction({
276+
action: EXPLORE_ACTION.PROFILE,
277+
source: EXPLORE_SOURCE.NODE_SIDEBAR_SINGLE_ENV,
278+
node_count: 1,
279+
});
260280
runAction(
261281
"profile",
262282
{
@@ -435,6 +455,11 @@ function ExploreChangeMenuButton({
435455
isAddedOrRemoved || featureToggles.disableDatabaseQuery
436456
}
437457
onClick={() => {
458+
trackExploreAction({
459+
action: EXPLORE_ACTION.PROFILE_DIFF,
460+
source: EXPLORE_SOURCE.NODE_SIDEBAR_MULTI_ENV,
461+
node_count: 1,
462+
});
438463
runAction(
439464
"profile_diff",
440465
{
@@ -457,6 +482,11 @@ function ExploreChangeMenuButton({
457482
isAddedOrRemoved || featureToggles.disableDatabaseQuery
458483
}
459484
onClick={() => {
485+
trackExploreAction({
486+
action: EXPLORE_ACTION.VALUE_DIFF,
487+
source: EXPLORE_SOURCE.NODE_SIDEBAR_MULTI_ENV,
488+
node_count: 1,
489+
});
460490
runAction(
461491
"value_diff",
462492
{
@@ -478,6 +508,11 @@ function ExploreChangeMenuButton({
478508
isAddedOrRemoved || featureToggles.disableDatabaseQuery
479509
}
480510
onClick={() => {
511+
trackExploreAction({
512+
action: EXPLORE_ACTION.TOP_K_DIFF,
513+
source: EXPLORE_SOURCE.NODE_SIDEBAR_MULTI_ENV,
514+
node_count: 1,
515+
});
481516
runAction(
482517
"top_k_diff",
483518
{ model: node.data.name, column_name: "", k: 50 },
@@ -497,6 +532,11 @@ function ExploreChangeMenuButton({
497532
isAddedOrRemoved || featureToggles.disableDatabaseQuery
498533
}
499534
onClick={() => {
535+
trackExploreAction({
536+
action: EXPLORE_ACTION.HISTOGRAM_DIFF,
537+
source: EXPLORE_SOURCE.NODE_SIDEBAR_MULTI_ENV,
538+
node_count: 1,
539+
});
500540
runAction(
501541
"histogram_diff",
502542
{

js/src/components/lineage/useMultiNodesAction.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ import { createLineageDiffCheck } from "@/lib/api/lineagecheck";
44
import { RowCountDiffParams, RowCountParams } from "@/lib/api/rowcount";
55
import { cancelRun, submitRun, waitRun } from "@/lib/api/runs";
66
import { createSchemaDiffCheck } from "@/lib/api/schemacheck";
7+
import {
8+
EXPLORE_ACTION,
9+
EXPLORE_SOURCE,
10+
trackExploreAction,
11+
} from "@/lib/api/track";
712
import { ValueDiffParams } from "@/lib/api/valuediff";
813
import { useRecceActionContext } from "@/lib/hooks/RecceActionContext";
914
import { ActionState } from "./LineageViewContext";
@@ -196,6 +201,12 @@ export const useMultiNodesAction = (
196201
};
197202

198203
const runRowCount = async () => {
204+
trackExploreAction({
205+
action: EXPLORE_ACTION.ROW_COUNT,
206+
source: EXPLORE_SOURCE.LINEAGE_VIEW_TOP_BAR,
207+
node_count: nodes.length,
208+
});
209+
199210
const nodeNames = [];
200211
for (const node of nodes) {
201212
if (node.data.resourceType !== "model") {
@@ -225,6 +236,12 @@ export const useMultiNodesAction = (
225236
};
226237

227238
const runRowCountDiff = async () => {
239+
trackExploreAction({
240+
action: EXPLORE_ACTION.ROW_COUNT_DIFF,
241+
source: EXPLORE_SOURCE.LINEAGE_VIEW_TOP_BAR,
242+
node_count: nodes.length,
243+
});
244+
228245
const nodeNames = [];
229246
for (const node of nodes) {
230247
if (node.data.resourceType !== "model") {
@@ -254,6 +271,12 @@ export const useMultiNodesAction = (
254271
};
255272

256273
const runValueDiff = async () => {
274+
trackExploreAction({
275+
action: EXPLORE_ACTION.VALUE_DIFF,
276+
source: EXPLORE_SOURCE.LINEAGE_VIEW_TOP_BAR,
277+
node_count: nodes.length,
278+
});
279+
257280
await submitRunsPerNodes("value_diff", (node) => {
258281
const primaryKey = node.data.data.current?.primary_key;
259282
if (!primaryKey) {

js/src/components/lineage/useValueDiffAlertDialog.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ import {
88
useDisclosure,
99
} from "@chakra-ui/react";
1010
import React, { useCallback, useRef, useState } from "react";
11+
import {
12+
EXPLORE_ACTION,
13+
EXPLORE_FORM_EVENT,
14+
trackExploreActionForm,
15+
} from "@/lib/api/track";
1116

1217
function useValueDiffAlertDialog() {
1318
const { open, onOpen, onClose } = useDisclosure();
@@ -28,11 +33,19 @@ function useValueDiffAlertDialog() {
2833
);
2934

3035
const handleConfirm = () => {
36+
trackExploreActionForm({
37+
action: EXPLORE_ACTION.VALUE_DIFF,
38+
event: EXPLORE_FORM_EVENT.EXECUTE,
39+
});
3140
resolvePromise?.(true);
3241
onClose();
3342
};
3443

3544
const handleCancel = () => {
45+
trackExploreActionForm({
46+
action: EXPLORE_ACTION.VALUE_DIFF,
47+
event: EXPLORE_FORM_EVENT.CANCEL,
48+
});
3649
resolvePromise?.(false);
3750
onClose();
3851
};

js/src/components/run/RunModal.tsx

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,14 @@ import {
1010
Popover,
1111
Portal,
1212
} from "@chakra-ui/react";
13-
import { ComponentType, useState } from "react";
13+
import { ComponentType, useRef, useState } from "react";
1414
import { IconInfo } from "@/components/icons";
1515
import { RunFormParamTypes, RunType } from "@/components/run/registry";
16+
import {
17+
EXPLORE_FORM_EVENT,
18+
isExploreAction,
19+
trackExploreActionForm,
20+
} from "@/lib/api/track";
1621
import { Run } from "@/lib/api/types";
1722
import { RunFormProps } from "./types";
1823

@@ -53,11 +58,23 @@ export const RunModal = ({
5358
const [hovered, setHovered] = useState(false);
5459
const [isReadyToExecute, setIsReadyToExecute] = useState(false);
5560
const documentationUrl = getDocumentationUrl(type);
61+
const executeClicked = useRef(false);
62+
63+
const handleClose = () => {
64+
if (!executeClicked.current && isExploreAction(type)) {
65+
trackExploreActionForm({
66+
action: type,
67+
event: EXPLORE_FORM_EVENT.CANCEL,
68+
});
69+
}
70+
executeClicked.current = false; // Reset for next open
71+
onClose();
72+
};
5673

5774
return (
5875
<Dialog.Root
5976
open={isOpen}
60-
onOpenChange={onClose}
77+
onOpenChange={handleClose}
6178
size="xl"
6279
scrollBehavior="inside"
6380
>
@@ -143,6 +160,13 @@ export const RunModal = ({
143160
disabled={!isReadyToExecute}
144161
colorPalette="blue"
145162
onClick={() => {
163+
executeClicked.current = true;
164+
if (isExploreAction(type)) {
165+
trackExploreActionForm({
166+
action: type,
167+
event: EXPLORE_FORM_EVENT.EXECUTE,
168+
});
169+
}
146170
onExecute(type, params as RunFormParamTypes);
147171
}}
148172
>

0 commit comments

Comments
 (0)