@@ -6,67 +6,105 @@ import StatusIndicator from "./StatusIndicator";
66import { state } from "../App" ;
77import { TableCell , TableRowInteractive } from "./TableContainer" ;
88import { 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
72110const 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} ) ;
0 commit comments