Skip to content

Commit ee7d63e

Browse files
atrakhConvex, Inc.
authored and
Convex, Inc.
committed
dashboard: tidy up index filters ui (#36107)
- Add clear filters button - Make the number of selected filters badge blue - Show a tooltip when the operator selection is disabled - Clear validation errors from index filters when the selected index is changed. - GitOrigin-RevId: 699ee6bcc83ed8be5e9df47f153e337e61d9c2df
1 parent 82cbc0d commit ee7d63e

File tree

8 files changed

+97
-49
lines changed

8 files changed

+97
-49
lines changed

Diff for: npm-packages/dashboard-common/src/features/data/components/DataContent.tsx

+9-2
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ import {
5151
} from "react-resizable-panels";
5252
import { cn } from "@common/lib/cn";
5353
import { useTableIndexes } from "@common/features/data/lib/api";
54+
import { getDefaultIndex } from "@common/features/data/components/DataFilters/IndexFilters";
5455

5556
export function DataContent({
5657
tableName,
@@ -294,8 +295,14 @@ export function DataContent({
294295
onAddDraftFilter={(filter: Filter) => {
295296
setDraftFilters((prev) =>
296297
prev
297-
? { clauses: [...prev.clauses, filter] }
298-
: { clauses: [filter] },
298+
? {
299+
clauses: [...prev.clauses, filter],
300+
index: prev.index ?? getDefaultIndex(),
301+
}
302+
: {
303+
clauses: [filter],
304+
index: getDefaultIndex(),
305+
},
299306
);
300307
setShowFilters(true);
301308
}}

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

+37-21
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import {
1010
import { GenericDocument } from "convex/server";
1111
import {
1212
Filter,
13+
FilterByIndex,
14+
FilterByIndexRange,
1315
FilterExpression,
1416
FilterValidationError,
1517
} from "system-udfs/convex/_system/frontend/lib/filters";
@@ -38,11 +40,7 @@ import { cn } from "@common/lib/cn";
3840
import { useTableIndexes } from "@common/features/data/lib/api";
3941
import { DeploymentInfoContext } from "@common/lib/deploymentContext";
4042
import { IndexFilterState } from "./IndexFilterEditor";
41-
import {
42-
IndexFilters,
43-
DEFAULT_INDEX_NAME,
44-
getDefaultIndexClause,
45-
} from "./IndexFilters";
43+
import { IndexFilters, getDefaultIndex } from "./IndexFilters";
4644

4745
export function DataFilters({
4846
defaultDocument,
@@ -296,10 +294,37 @@ export function DataFilters({
296294
</Button>
297295
) : (
298296
hasFilters && (
299-
<p className="ml-1 flex gap-0.5 text-xs font-medium text-content-secondary">
300-
<CheckIcon />
301-
Filters applied
302-
</p>
297+
<div className="flex w-full items-center gap-1">
298+
<p className="ml-1 flex gap-0.5 text-xs font-medium text-content-secondary">
299+
<CheckIcon />
300+
Filters applied
301+
</p>
302+
<Button
303+
size="xs"
304+
variant="neutral"
305+
className="ml-auto text-xs"
306+
onClick={() => {
307+
onChangeFilters({
308+
clauses: [],
309+
index: shownFilters.index
310+
? {
311+
name: shownFilters.index.name,
312+
clauses: shownFilters.index.clauses.map(
313+
(clause) => ({
314+
...clause,
315+
enabled: false,
316+
}),
317+
) as
318+
| FilterByIndex[]
319+
| [...FilterByIndex[], FilterByIndexRange],
320+
}
321+
: undefined,
322+
});
323+
}}
324+
>
325+
Clear filters
326+
</Button>
327+
</div>
303328
)
304329
)}
305330
{dataFetchErrors && dataFetchErrors.length > 0 && (
@@ -440,10 +465,7 @@ function useDataFilters({
440465
draftFilters ??
441466
({
442467
clauses: [],
443-
index: {
444-
name: DEFAULT_INDEX_NAME,
445-
clauses: [getDefaultIndexClause()],
446-
},
468+
index: getDefaultIndex(),
447469
} as FilterExpression),
448470
[draftFilters],
449471
);
@@ -499,10 +521,7 @@ function useDataFilters({
499521
...shownFilters.clauses.slice(0, idx),
500522
...shownFilters.clauses.slice(idx + 1),
501523
],
502-
index: shownFilters.index || {
503-
name: DEFAULT_INDEX_NAME,
504-
clauses: [getDefaultIndexClause()],
505-
},
524+
index: shownFilters.index || getDefaultIndex(),
506525
} as FilterExpression;
507526
setDraftFilters(newFilters);
508527
},
@@ -518,10 +537,7 @@ function useDataFilters({
518537
generateNewFilter(),
519538
...shownFilters.clauses.slice(idx),
520539
],
521-
index: shownFilters.index || {
522-
name: DEFAULT_INDEX_NAME,
523-
clauses: [getDefaultIndexClause()],
524-
},
540+
index: shownFilters.index || getDefaultIndex(),
525541
} as FilterExpression;
526542
setDraftFilters(newFilters);
527543
},

Diff for: npm-packages/dashboard-common/src/features/data/components/DataFilters/FilterButton.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export function FilterButton({
3939
<div className="flex items-center gap-2">
4040
<span>{enableIndexFilters ? "Filter & Sort" : "Filter"}</span>
4141
{(indexFilters?.length || validFilterNames.size > 0) && (
42-
<span className="rounded-full border bg-background-primary px-1 py-0 text-xs tabular-nums leading-[14px] text-content-secondary">
42+
<span className="rounded-full border bg-blue-100/50 px-1 py-0 text-xs tabular-nums leading-[14px] text-content-accent dark:bg-blue-700/50 dark:text-white">
4343
{(indexFilters?.length || 0) + (regularFilters?.length || 0)}
4444
</span>
4545
)}

Diff for: npm-packages/dashboard-common/src/features/data/components/DataFilters/IndexFilterEditor.tsx

+26-19
Original file line numberDiff line numberDiff line change
@@ -581,26 +581,33 @@ export function IndexFilterEditor({
581581
disabled={!filter.enabled}
582582
/>
583583
) : (
584-
<div
585-
className={cn(
586-
"ml-[-1px] flex w-fit items-center border px-2 py-1 text-xs cursor-not-allowed",
587-
filter.enabled
588-
? "bg-background-secondary"
589-
: "bg-background-tertiary text-content-secondary",
590-
)}
584+
<Tooltip
585+
tip={
586+
filter.enabled &&
587+
"In an index filter, you can only change the operator of the last enabled filter."
588+
}
591589
>
592-
{currentOperator === "between"
593-
? "is between"
594-
: currentOperator === "lt"
595-
? "<"
596-
: currentOperator === "lte"
597-
? "<="
598-
: currentOperator === "gt"
599-
? ">"
600-
: currentOperator === "gte"
601-
? ">="
602-
: "equals"}
603-
</div>
590+
<div
591+
className={cn(
592+
"ml-[-1px] flex w-fit items-center border px-2 py-1 text-xs cursor-not-allowed",
593+
filter.enabled
594+
? "bg-background-secondary"
595+
: "bg-background-tertiary text-content-secondary",
596+
)}
597+
>
598+
{currentOperator === "between"
599+
? "is between"
600+
: currentOperator === "lt"
601+
? "<"
602+
: currentOperator === "lte"
603+
? "<="
604+
: currentOperator === "gt"
605+
? ">"
606+
: currentOperator === "gte"
607+
? ">="
608+
: "equals"}
609+
</div>
610+
</Tooltip>
604611
)}
605612

606613
{/* Render the appropriate value editor */}

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

+20-4
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ import { ArrowsUpDownIcon, FingerPrintIcon } from "@heroicons/react/24/outline";
22
import { InfoCircledIcon } from "@radix-ui/react-icons";
33
import { GenericDocument } from "convex/server";
44
import { ValidatorJSON } from "convex/values";
5-
import { FilterExpression } from "system-udfs/convex/_system/frontend/lib/filters";
5+
import {
6+
FilterByIndexRange,
7+
FilterExpression,
8+
} from "system-udfs/convex/_system/frontend/lib/filters";
69
import { Button } from "@common/elements/Button";
710
import { Combobox } from "@common/elements/Combobox";
811
import { Tooltip } from "@common/elements/Tooltip";
@@ -11,8 +14,18 @@ import { SchemaJson } from "@common/lib/format";
1114
import Link from "next/link";
1215
import { IndexFilterEditor, IndexFilterState } from "./IndexFilterEditor";
1316

17+
export function getDefaultIndex(): {
18+
name: string;
19+
clauses: [FilterByIndexRange];
20+
} {
21+
return {
22+
name: DEFAULT_INDEX_NAME,
23+
clauses: [getDefaultIndexClause()],
24+
};
25+
}
26+
1427
// Function to generate a default index clause with current timestamp
15-
function getDefaultIndexClause() {
28+
function getDefaultIndexClause(): FilterByIndexRange {
1629
return {
1730
type: "indexRange",
1831
enabled: false,
@@ -136,6 +149,11 @@ export function IndexFilters({
136149
return;
137150
}
138151

152+
// Clear all errors for the existing index filters
153+
shownFilters.index?.clauses.forEach((_, idx) => {
154+
onError(idx, []);
155+
});
156+
139157
const newFilters = {
140158
...shownFilters,
141159
index: {
@@ -308,5 +326,3 @@ export function IndexFilters({
308326
</>
309327
);
310328
}
311-
312-
export { DEFAULT_INDEX_NAME, DEFAULT_INDEX, getDefaultIndexClause };

Diff for: npm-packages/dashboard-common/src/features/data/components/Table/TableContextMenu.test.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ describe("TableContextMenu", () => {
108108
op: "eq",
109109
value: defaultProps.defaultDocument.name,
110110
id: expect.anything(),
111+
enabled: true,
111112
});
112113
});
113114

Diff for: npm-packages/dashboard-common/src/features/data/components/Table/TableContextMenu.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,7 @@ function CellActions({
294294
field: state.selectedCell?.column,
295295
op: "eq",
296296
value: convexToJson(value),
297+
enabled: true,
297298
});
298299
}}
299300
/>

Diff for: npm-packages/dashboard-common/src/features/data/lib/useTableFilters.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -143,8 +143,8 @@ export const useTableFilters = (
143143
function hasValidEnabledFilters(filters?: FilterExpression) {
144144
return (
145145
!!filters &&
146-
(filters.clauses.filter(isValidFilter).filter((f) => f.enabled).length >
147-
0 ||
146+
(filters.clauses.filter(isValidFilter).filter((f) => f.enabled !== false)
147+
.length > 0 ||
148148
(filters.index?.clauses.filter((f) => f.enabled).length ?? 0) > 0)
149149
);
150150
}

0 commit comments

Comments
 (0)