Skip to content

Commit df96919

Browse files
author
Dylan Huang
committed
filter button works
1 parent 1ab2240 commit df96919

File tree

3 files changed

+110
-66
lines changed

3 files changed

+110
-66
lines changed

vite-app/src/components/EvaluationRow.tsx

Lines changed: 102 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -6,67 +6,105 @@ import StatusIndicator from "./StatusIndicator";
66
import { state } from "../App";
77
import { TableCell, TableRowInteractive } from "./TableContainer";
88
import { useState } from "react";
9+
import type { FilterGroup, FilterConfig } from "../types/filters";
910

10-
// Copy button component
11-
const CopyButton = observer(({ text }: { text: string }) => {
12-
const [copied, setCopied] = useState(false);
13-
14-
const handleClick = async (e: React.MouseEvent) => {
15-
e.stopPropagation(); // Prevent row expansion
16-
try {
17-
await navigator.clipboard.writeText(text);
18-
setCopied(true);
19-
// Reset to "Copy" state after 2 seconds
20-
setTimeout(() => setCopied(false), 2000);
21-
} catch (err) {
22-
console.error("Failed to copy text: ", err);
23-
}
24-
};
11+
// Add filter button component
12+
const AddFilterButton = observer(
13+
({
14+
fieldPath,
15+
value,
16+
label,
17+
}: {
18+
fieldPath: string;
19+
value: string;
20+
label: string;
21+
}) => {
22+
const [added, setAdded] = useState(false);
23+
24+
const handleClick = (e: React.MouseEvent) => {
25+
e.stopPropagation(); // Prevent row expansion
26+
27+
// Create a new filter for this field/value
28+
const newFilter: FilterConfig = {
29+
field: fieldPath,
30+
operator: "==",
31+
value: value,
32+
type: "text",
33+
};
34+
35+
// Add the filter to the existing filter configuration
36+
const currentFilters = state.filterConfig;
37+
let newFilters: FilterGroup[];
38+
39+
if (currentFilters.length === 0) {
40+
// If no filters exist, create a new filter group
41+
newFilters = [
42+
{
43+
logic: "AND",
44+
filters: [newFilter],
45+
},
46+
];
47+
} else {
48+
// Add to the first filter group (assuming AND logic)
49+
newFilters = [...currentFilters];
50+
newFilters[0] = {
51+
...newFilters[0],
52+
filters: [...newFilters[0].filters, newFilter],
53+
};
54+
}
55+
56+
state.updateFilterConfig(newFilters);
57+
setAdded(true);
58+
59+
// Reset to "Add Filter" state after 2 seconds
60+
setTimeout(() => setAdded(false), 2000);
61+
};
2562

26-
return (
27-
<button
28-
className="p-1 text-gray-400 hover:text-gray-600 transition-colors relative group cursor-pointer"
29-
onClick={handleClick}
30-
title="Copy to clipboard"
31-
>
32-
{/* Tooltip */}
33-
<div className="absolute bottom-full left-1/2 transform -translate-x-1/2 mb-2 px-2 py-1 text-xs text-white bg-gray-800 rounded opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none whitespace-nowrap">
34-
{copied ? "Copied!" : "Copy"}
35-
</div>
63+
return (
64+
<button
65+
className="text-gray-400 hover:text-gray-600 transition-colors relative group cursor-pointer"
66+
onClick={handleClick}
67+
title="Add filter for this value"
68+
>
69+
{/* Tooltip */}
70+
<div className="absolute bottom-full left-1/2 transform -translate-x-1/2 mb-2 px-2 py-1 text-xs text-white bg-gray-800 rounded opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none whitespace-nowrap">
71+
{added ? "Filter Added!" : `Add ${label} Filter`}
72+
</div>
3673

37-
{/* Icon */}
38-
{copied ? (
39-
<svg
40-
className="w-3 h-3 text-green-600"
41-
fill="none"
42-
stroke="currentColor"
43-
viewBox="0 0 24 24"
44-
>
45-
<path
46-
strokeLinecap="round"
47-
strokeLinejoin="round"
48-
strokeWidth={2}
49-
d="M5 13l4 4L19 7"
50-
/>
51-
</svg>
52-
) : (
53-
<svg
54-
className="w-3 h-3"
55-
fill="none"
56-
stroke="currentColor"
57-
viewBox="0 0 24 24"
58-
>
59-
<path
60-
strokeLinecap="round"
61-
strokeLinejoin="round"
62-
strokeWidth={2}
63-
d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"
64-
/>
65-
</svg>
66-
)}
67-
</button>
68-
);
69-
});
74+
{/* Icon */}
75+
{added ? (
76+
<svg
77+
className="w-3 h-3 text-green-600"
78+
fill="none"
79+
stroke="currentColor"
80+
viewBox="0 0 24 24"
81+
>
82+
<path
83+
strokeLinecap="round"
84+
strokeLinejoin="round"
85+
strokeWidth={2}
86+
d="M5 13l4 4L19 7"
87+
/>
88+
</svg>
89+
) : (
90+
<svg
91+
className="w-3 h-3"
92+
fill="none"
93+
stroke="currentColor"
94+
viewBox="0 0 24 24"
95+
>
96+
<path
97+
strokeLinecap="round"
98+
strokeLinejoin="round"
99+
strokeWidth={2}
100+
d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.207A1 1 0 013 6.5V4z"
101+
/>
102+
</svg>
103+
)}
104+
</button>
105+
);
106+
}
107+
);
70108

71109
// Small, focused components following "dereference values late" principle
72110
const ExpandIcon = observer(({ rolloutId }: { rolloutId?: string }) => {
@@ -131,9 +169,13 @@ const InvocationId = observer(({ invocationId }: { invocationId?: string }) => {
131169
return null;
132170
}
133171
return (
134-
<span className="font-mono text-gray-900 whitespace-nowrap">
172+
<span className="font-mono text-gray-900 whitespace-nowrap flex items-center gap-1">
135173
{invocationId}
136-
<CopyButton text={invocationId} />
174+
<AddFilterButton
175+
fieldPath="$.execution_metadata.invocation_id"
176+
value={invocationId}
177+
label="Invocation"
178+
/>
137179
</span>
138180
);
139181
});

vite-app/src/types/filters.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
1+
export type Operator = "==" | "!=" | ">" | "<" | ">=" | "<=" | "contains" | "!contains" | "between";
2+
13
// Filter configuration interface
24
export interface FilterConfig {
35
field: string;
4-
operator: string;
6+
operator: Operator;
57
value: string;
68
value2?: string; // For filtering between dates
79
type?: "text" | "date" | "date-range";
810
}
911

10-
export interface FilterOperator {
11-
value: string;
12+
export type FilterOperator = {
13+
value: Operator;
1214
label: string;
13-
}
15+
};
1416

1517
// Filter group interface for AND/OR logic
1618
export interface FilterGroup {

vite-app/src/util/filter-utils.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { FilterConfig, FilterGroup } from "../types/filters";
1+
import type { FilterConfig, FilterGroup, FilterOperator } from "../types/filters";
22

33
// Filter utilities
44
export const isDateField = (field: string): boolean => {
@@ -14,7 +14,7 @@ export const getFieldType = (field: string): "text" | "date" | "date-range" => {
1414
return isDateField(field) ? "date" : "text";
1515
};
1616

17-
export const getOperatorsForField = (field: string, type?: string) => {
17+
export const getOperatorsForField = (field: string, type?: string): FilterOperator[] => {
1818
if (type === "date" || type === "date-range" || isDateField(field)) {
1919
return [
2020
{ value: ">=", label: "on or after" },

0 commit comments

Comments
 (0)