@@ -22,6 +22,10 @@ import {
2222import { useBackend } from "@/providers/BackendProvider" ;
2323import { getBackendStatusString } from "@/utils/backend" ;
2424import { fetchWithErrorHandling } from "@/utils/fetchWithErrorHandling" ;
25+ import {
26+ filtersToApiString ,
27+ parseFilterParam ,
28+ } from "@/utils/pipelineRunFilterUtils" ;
2529
2630import RunRow from "./RunRow" ;
2731
@@ -42,25 +46,12 @@ export const RunSection = ({ onEmptyList }: { onEmptyList?: () => void }) => {
4246 const isCreatedByMeDefault = useFlagValue ( "created-by-me-default" ) ;
4347 const dataVersion = useRef ( 0 ) ;
4448
45- // Parse filter into a dictionary
46- const parseFilter = ( filter ?: string ) : Record < string , string > => {
47- if ( ! filter ) return { } ;
48-
49- const filterDict : Record < string , string > = { } ;
50- const parts = filter . split ( "," ) ;
51-
52- for ( const part of parts ) {
53- const [ key , value ] = part . split ( ":" ) ;
54- if ( key && value ) {
55- filterDict [ key . trim ( ) ] = value . trim ( ) ;
56- }
57- }
49+ // Parse filter from URL - supports both JSON (new) and key:value (legacy) formats
50+ const filters = parseFilterParam ( search . filter ) ;
51+ const createdByValue = filters . created_by ;
5852
59- return filterDict ;
60- } ;
61-
62- const filterDict = parseFilter ( search . filter ) ;
63- const createdByValue = filterDict . created_by ;
53+ // Convert filters to API format (only includes supported filters)
54+ const apiFilterString = filtersToApiString ( filters ) ;
6455
6556 const [ searchUser , setSearchUser ] = useState ( createdByValue ?? "" ) ;
6657
@@ -75,13 +66,15 @@ export const RunSection = ({ onEmptyList }: { onEmptyList?: () => void }) => {
7566
7667 const { data, isLoading, isFetching, error, isFetched } =
7768 useQuery < ListPipelineJobsResponse > ( {
78- queryKey : [ "runs" , backendUrl , pageToken , search . filter ] ,
69+ queryKey : [ "runs" , backendUrl , pageToken , apiFilterString ] ,
7970 refetchOnWindowFocus : false ,
8071 enabled : configured && available ,
8172 queryFn : async ( ) => {
8273 const u = new URL ( PIPELINE_RUNS_QUERY_URL , backendUrl ) ;
8374 if ( pageToken ) u . searchParams . set ( PAGE_TOKEN_QUERY_KEY , pageToken ) ;
84- if ( search . filter ) u . searchParams . set ( FILTER_QUERY_KEY , search . filter ) ;
75+ // Use translated filter string (only includes API-supported filters)
76+ if ( apiFilterString )
77+ u . searchParams . set ( FILTER_QUERY_KEY , apiFilterString ) ;
8578
8679 u . searchParams . set ( INCLUDE_PIPELINE_NAME_QUERY_KEY , "true" ) ;
8780 u . searchParams . set ( INCLUDE_EXECUTION_STATS_QUERY_KEY , "true" ) ;
@@ -108,22 +101,25 @@ export const RunSection = ({ onEmptyList }: { onEmptyList?: () => void }) => {
108101
109102 if ( value ) {
110103 // If there's already a created_by filter, keep it; otherwise use "created_by:me"
111- if ( ! filterDict . created_by ) {
104+ if ( ! filters . created_by ) {
112105 nextSearch . filter = CREATED_BY_ME_FILTER ;
113106 setSearchUser ( "" ) ;
114107 }
115108 } else {
116109 // Remove created_by from filter, but keep other filters
117- const updatedFilterDict = { ...filterDict } ;
118- delete updatedFilterDict . created_by ;
119-
120- // Convert back to string format
121- const remainingFilters = Object . entries ( updatedFilterDict )
122- . map ( ( [ key , value ] ) => `${ key } :${ value } ` )
123- . join ( "," ) ;
110+ const updatedFilters = { ...filters } ;
111+ delete updatedFilters . created_by ;
112+
113+ // Check if any other filters remain
114+ const hasRemainingFilters = Object . keys ( updatedFilters ) . some ( ( key ) => {
115+ const val = updatedFilters [ key as keyof typeof updatedFilters ] ;
116+ if ( val === undefined || val === null || val === "" ) return false ;
117+ if ( Array . isArray ( val ) && val . length === 0 ) return false ;
118+ return true ;
119+ } ) ;
124120
125- if ( remainingFilters ) {
126- nextSearch . filter = remainingFilters ;
121+ if ( hasRemainingFilters ) {
122+ nextSearch . filter = JSON . stringify ( updatedFilters ) ;
127123 } else {
128124 if ( isCreatedByMeDefault ) {
129125 nextSearch . filter = "" ;
@@ -143,16 +139,9 @@ export const RunSection = ({ onEmptyList }: { onEmptyList?: () => void }) => {
143139 const nextSearch : RunSectionSearch = { ...search } ;
144140 delete nextSearch . page_token ;
145141
146- // Create or update the created_by filter
147- const updatedFilterDict = { ...filterDict } ;
148- updatedFilterDict . created_by = searchUser . trim ( ) ;
149-
150- // Convert back to string format
151- const newFilter = Object . entries ( updatedFilterDict )
152- . map ( ( [ key , value ] ) => `${ key } :${ value } ` )
153- . join ( "," ) ;
154-
155- nextSearch . filter = newFilter ;
142+ // Create or update the created_by filter, preserving other filters
143+ const updatedFilters = { ...filters , created_by : searchUser . trim ( ) } ;
144+ nextSearch . filter = JSON . stringify ( updatedFilters ) ;
156145
157146 setPreviousPageTokens ( [ ] ) ;
158147 navigate ( { to : pathname , search : nextSearch } ) ;
0 commit comments