Skip to content

Commit d8f666c

Browse files
atrakhConvex, Inc.
authored and
Convex, Inc.
committed
dashboard: instrument analytics for filters ui (#36228)
GitOrigin-RevId: 2320ae65f989caabad7174b4d7060a043b217342
1 parent c625e39 commit d8f666c

File tree

2 files changed

+96
-6
lines changed

2 files changed

+96
-6
lines changed

npm-packages/dashboard-common/src/features/data/components/DataFilters/DataFilters.tsx

+86-6
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@ export function DataFilters({
104104
const numRowsWeKnowOf = hasFilters ? numRowsLoaded : numRows;
105105

106106
const { enableIndexFilters } = useContext(DeploymentInfoContext);
107+
const { useLogDeploymentEvent } = useContext(DeploymentInfoContext);
108+
const log = useLogDeploymentEvent();
107109

108110
return (
109111
<form
@@ -115,6 +117,13 @@ export function DataFilters({
115117
if (hasInvalidFilters) {
116118
return;
117119
}
120+
log("apply filters", {
121+
hasIndexFilters:
122+
(shownFilters.index?.clauses || []).filter((c) => c.enabled)
123+
.length > 0,
124+
hasOtherFilters:
125+
shownFilters.clauses.filter((c) => c.enabled !== false).length > 0,
126+
});
118127
onChangeFilters(
119128
draftFilters || {
120129
clauses: [],
@@ -249,6 +258,16 @@ export function DataFilters({
249258
if (hasInvalidFilters) {
250259
return;
251260
}
261+
log("apply filters", {
262+
hasIndexFilters:
263+
(shownFilters.index?.clauses || []).filter(
264+
(c) => c.enabled,
265+
).length > 0,
266+
hasOtherFilters:
267+
shownFilters.clauses.filter(
268+
(c) => c.enabled !== false,
269+
).length > 0,
270+
});
252271
onChangeFilters(shownFilters);
253272
}}
254273
onError={(...args) => onError("filter", ...args)}
@@ -273,7 +292,10 @@ export function DataFilters({
273292
size="xs"
274293
className="text-xs"
275294
icon={<PlusIcon />}
276-
onClick={() => onAddFilter(shownFilters.clauses.length)}
295+
onClick={() => {
296+
onAddFilter(shownFilters.clauses.length);
297+
log("add filter");
298+
}}
277299
>
278300
Add filter
279301
</Button>
@@ -443,6 +465,8 @@ function useDataFilters({
443465
setDraftFilters(next: FilterExpression): void;
444466
activeSchema: SchemaJson | null;
445467
}) {
468+
const { useLogDeploymentEvent } = useContext(DeploymentInfoContext);
469+
const log = useLogDeploymentEvent();
446470
const [invalidFilters, { set: setInvalidFilters }] = useMap();
447471

448472
const isDirty = !isEqual(filters, draftFilters);
@@ -473,6 +497,7 @@ function useDataFilters({
473497
const onChangeFilter = useCallback(
474498
(filter: FilterState, idx: number) => {
475499
const newFilters = cloneDeep(shownFilters);
500+
const oldFilter = newFilters.clauses[idx];
476501

477502
// Convert the FilterState to a Filter
478503
let newFilter: Filter;
@@ -495,10 +520,33 @@ function useDataFilters({
495520
};
496521
}
497522

523+
// Log filter changes
524+
if (oldFilter) {
525+
if (oldFilter.enabled !== filter.enabled) {
526+
log("filter toggle", {
527+
enabled: filter.enabled,
528+
filterType: "regular",
529+
filterIndex: idx,
530+
});
531+
} else if (oldFilter.op !== filter.op) {
532+
log("filter operator change", {
533+
oldOperator: oldFilter.op,
534+
newOperator: filter.op,
535+
filterType: "regular",
536+
filterIndex: idx,
537+
});
538+
} else if (oldFilter.field !== filter.field) {
539+
log("filter field change", {
540+
filterType: "regular",
541+
filterIndex: idx,
542+
});
543+
}
544+
}
545+
498546
newFilters.clauses[idx] = newFilter;
499547
setDraftFilters(newFilters);
500548
},
501-
[shownFilters, setDraftFilters],
549+
[shownFilters, setDraftFilters, log],
502550
);
503551

504552
const onChangeIndexFilter = useCallback(
@@ -507,13 +555,37 @@ function useDataFilters({
507555
if (!newFilters.index) {
508556
throw new Error("Index not found");
509557
}
558+
const oldFilter = newFilters.index.clauses[idx];
559+
560+
// Log index filter changes
561+
if (oldFilter) {
562+
if (oldFilter.enabled !== filter.enabled) {
563+
log("filter toggle", {
564+
enabled: filter.enabled,
565+
filterType: "index",
566+
filterIndex: idx,
567+
});
568+
} else if (oldFilter.type !== filter.type) {
569+
log("index filter type change", {
570+
oldType: oldFilter.type,
571+
newType: filter.type,
572+
filterIndex: idx,
573+
});
574+
}
575+
}
576+
510577
newFilters.index.clauses[idx] = filter;
511578
setDraftFilters(newFilters);
512579
},
513-
[shownFilters, setDraftFilters],
580+
[shownFilters, setDraftFilters, log],
514581
);
582+
515583
const onDeleteFilter = useCallback(
516584
(idx: number) => {
585+
log("filter delete", {
586+
filterType: "regular",
587+
filterIndex: idx,
588+
});
517589
setInvalidFilters(idx, undefined);
518590
const newFilters = {
519591
...shownFilters,
@@ -525,11 +597,15 @@ function useDataFilters({
525597
} as FilterExpression;
526598
setDraftFilters(newFilters);
527599
},
528-
[shownFilters, setDraftFilters, setInvalidFilters],
600+
[shownFilters, setDraftFilters, setInvalidFilters, log],
529601
);
530602

531603
const onAddFilter = useCallback(
532604
(idx: number) => {
605+
log("filter add", {
606+
filterType: "regular",
607+
filterIndex: idx,
608+
});
533609
const newFilters = {
534610
...shownFilters,
535611
clauses: [
@@ -541,7 +617,7 @@ function useDataFilters({
541617
} as FilterExpression;
542618
setDraftFilters(newFilters);
543619
},
544-
[shownFilters, setDraftFilters],
620+
[shownFilters, setDraftFilters, log],
545621
);
546622

547623
const onError = useCallback(
@@ -574,6 +650,10 @@ function useDataFilters({
574650

575651
const onChangeOrder = useCallback(
576652
(newOrder: "asc" | "desc") => {
653+
log("filter order change", {
654+
oldOrder: shownFilters.order,
655+
newOrder,
656+
});
577657
const newFilters = {
578658
...shownFilters,
579659
clauses: shownFilters.clauses.map((filter, idx) => ({
@@ -585,7 +665,7 @@ function useDataFilters({
585665
setDraftFilters(newFilters);
586666
onChangeFilters(newFilters);
587667
},
588-
[shownFilters, setDraftFilters, onChangeFilters, invalidFilters],
668+
[shownFilters, setDraftFilters, onChangeFilters, invalidFilters, log],
589669
);
590670

591671
return {

npm-packages/dashboard-common/src/features/data/components/DataFilters/IndexFilters.tsx

+10
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import React, { useContext } from "react";
12
import { ArrowsUpDownIcon, FingerPrintIcon } from "@heroicons/react/24/outline";
23
import { InfoCircledIcon } from "@radix-ui/react-icons";
34
import { GenericDocument } from "convex/server";
@@ -12,6 +13,7 @@ import { Tooltip } from "@common/elements/Tooltip";
1213
import { Popover } from "@common/elements/Popover";
1314
import { SchemaJson } from "@common/lib/format";
1415
import Link from "next/link";
16+
import { DeploymentInfoContext } from "@common/lib/deploymentContext";
1517
import { IndexFilterEditor, IndexFilterState } from "./IndexFilterEditor";
1618

1719
export function getDefaultIndex(): {
@@ -86,6 +88,9 @@ export function IndexFilters({
8688
onError,
8789
hasInvalidFilters,
8890
}: IndexFiltersProps) {
91+
const { useLogDeploymentEvent } = useContext(DeploymentInfoContext);
92+
const log = useLogDeploymentEvent();
93+
8994
// Restructure indexOptions to use the Option<IndexOptionValue> type
9095
const indexOptions = indexes
9196
? [
@@ -149,6 +154,10 @@ export function IndexFilters({
149154
return;
150155
}
151156

157+
log("sort by index combobox opened", {
158+
selectedOption: option.name,
159+
});
160+
152161
// Clear all errors for the existing index filters
153162
shownFilters.index?.clauses.forEach((_, idx) => {
154163
onError(idx, []);
@@ -196,6 +205,7 @@ export function IndexFilters({
196205
variant="neutral"
197206
className="w-fit text-xs"
198207
icon={<FingerPrintIcon className="size-4" />}
208+
onClick={() => log("viewed sort by index empty state")}
199209
>
200210
Index: {DEFAULT_INDEX_LABEL}
201211
</Button>

0 commit comments

Comments
 (0)