11import { makeAutoObservable , runInAction } from "mobx" ;
22import type { EvaluationRow } from "./types/eval-protocol" ;
3- import type { PivotConfig } from "./types/filters" ;
3+ import type { PivotConfig , FilterGroup } from "./types/filters" ;
44import flattenJson from "./util/flatten-json" ;
55
66// Default pivot configuration
@@ -12,6 +12,9 @@ const DEFAULT_PIVOT_CONFIG: PivotConfig = {
1212 filters : [ ] ,
1313} ;
1414
15+ // Default table filter configuration
16+ const DEFAULT_TABLE_FILTER_CONFIG : FilterGroup [ ] = [ ] ;
17+
1518// Default pagination configuration
1619const DEFAULT_PAGINATION_CONFIG = {
1720 currentPage : 1 ,
@@ -26,6 +29,8 @@ export class GlobalState {
2629 expandedRows : Record < string , boolean > = { } ;
2730 // Pivot configuration
2831 pivotConfig : PivotConfig ;
32+ // Table filter configuration
33+ tableFilterConfig : FilterGroup [ ] ;
2934 // Pagination configuration
3035 currentPage : number ;
3136 pageSize : number ;
@@ -35,6 +40,8 @@ export class GlobalState {
3540 constructor ( ) {
3641 // Load pivot config from localStorage or use defaults
3742 this . pivotConfig = this . loadPivotConfig ( ) ;
43+ // Load table filter config from localStorage or use defaults
44+ this . tableFilterConfig = this . loadTableFilterConfig ( ) ;
3845 // Load pagination config from localStorage or use defaults
3946 const paginationConfig = this . loadPaginationConfig ( ) ;
4047 this . currentPage = paginationConfig . currentPage ;
@@ -57,6 +64,23 @@ export class GlobalState {
5764 return { ...DEFAULT_PIVOT_CONFIG } ;
5865 }
5966
67+ // Load table filter configuration from localStorage
68+ private loadTableFilterConfig ( ) : FilterGroup [ ] {
69+ try {
70+ const stored = localStorage . getItem ( "tableFilterConfig" ) ;
71+ if ( stored ) {
72+ const parsed = JSON . parse ( stored ) ;
73+ return Array . isArray ( parsed ) ? parsed : DEFAULT_TABLE_FILTER_CONFIG ;
74+ }
75+ } catch ( error ) {
76+ console . warn (
77+ "Failed to load table filter config from localStorage:" ,
78+ error
79+ ) ;
80+ }
81+ return DEFAULT_TABLE_FILTER_CONFIG ;
82+ }
83+
6084 // Load pagination configuration from localStorage
6185 private loadPaginationConfig ( ) {
6286 try {
@@ -84,6 +108,21 @@ export class GlobalState {
84108 }
85109 }
86110
111+ // Save table filter configuration to localStorage
112+ private saveTableFilterConfig ( ) {
113+ try {
114+ localStorage . setItem (
115+ "tableFilterConfig" ,
116+ JSON . stringify ( this . tableFilterConfig )
117+ ) ;
118+ } catch ( error ) {
119+ console . warn (
120+ "Failed to save table filter config to localStorage:" ,
121+ error
122+ ) ;
123+ }
124+ }
125+
87126 // Save pagination configuration to localStorage
88127 private savePaginationConfig ( ) {
89128 try {
@@ -105,6 +144,12 @@ export class GlobalState {
105144 this . savePivotConfig ( ) ;
106145 }
107146
147+ // Update table filter configuration and save to localStorage
148+ updateTableFilterConfig ( filters : FilterGroup [ ] ) {
149+ this . tableFilterConfig = filters ;
150+ this . saveTableFilterConfig ( ) ;
151+ }
152+
108153 // Update pagination configuration and save to localStorage
109154 updatePaginationConfig (
110155 updates : Partial < { currentPage : number ; pageSize : number } >
@@ -127,6 +172,12 @@ export class GlobalState {
127172 this . savePivotConfig ( ) ;
128173 }
129174
175+ // Reset table filter configuration to defaults
176+ resetTableFilterConfig ( ) {
177+ this . tableFilterConfig = [ ...DEFAULT_TABLE_FILTER_CONFIG ] ;
178+ this . saveTableFilterConfig ( ) ;
179+ }
180+
130181 // Reset pagination configuration to defaults
131182 resetPaginationConfig ( ) {
132183 this . currentPage = DEFAULT_PAGINATION_CONFIG . currentPage ;
@@ -213,6 +264,27 @@ export class GlobalState {
213264 return this . sortedDataset . map ( ( row ) => flattenJson ( row ) ) ;
214265 }
215266
267+ get filteredFlattenedDataset ( ) {
268+ if ( this . tableFilterConfig . length === 0 ) {
269+ return this . flattenedDataset ;
270+ }
271+
272+ const filterFunction = this . createFilterFunction ( this . tableFilterConfig ) ;
273+ return this . flattenedDataset . filter ( filterFunction ) ;
274+ }
275+
276+ get filteredOriginalDataset ( ) {
277+ if ( this . tableFilterConfig . length === 0 ) {
278+ return this . sortedDataset ;
279+ }
280+
281+ const filterFunction = this . createFilterFunction ( this . tableFilterConfig ) ;
282+ return this . sortedDataset . filter ( ( row ) => {
283+ const flattened = flattenJson ( row ) ;
284+ return filterFunction ( flattened ) ;
285+ } ) ;
286+ }
287+
216288 get flattenedDatasetKeys ( ) {
217289 const keySet = new Set < string > ( ) ;
218290 this . flattenedDataset . forEach ( ( row ) => {
@@ -222,7 +294,7 @@ export class GlobalState {
222294 }
223295
224296 get totalCount ( ) {
225- return Object . keys ( this . dataset ) . length ;
297+ return this . filteredFlattenedDataset . length ;
226298 }
227299
228300 get totalPages ( ) {
@@ -236,4 +308,93 @@ export class GlobalState {
236308 get endRow ( ) {
237309 return Math . min ( this . currentPage * this . pageSize , this . totalCount ) ;
238310 }
311+
312+ // Create filter function from filter group configuration
313+ private createFilterFunction ( filterGroups : FilterGroup [ ] ) {
314+ if ( filterGroups . length === 0 ) return ( ) => true ;
315+
316+ return ( record : any ) => {
317+ return filterGroups . every ( ( group ) => {
318+ if ( group . filters . length === 0 ) return true ;
319+
320+ if ( group . logic === "OR" ) {
321+ // For OR logic, at least one filter must pass
322+ return group . filters . some ( ( filter ) =>
323+ this . evaluateFilter ( filter , record )
324+ ) ;
325+ } else {
326+ // For AND logic, all filters must pass
327+ return group . filters . every ( ( filter ) =>
328+ this . evaluateFilter ( filter , record )
329+ ) ;
330+ }
331+ } ) ;
332+ } ;
333+ }
334+
335+ // Helper function to evaluate a single filter
336+ private evaluateFilter ( filter : any , record : any ) : boolean {
337+ if ( ! filter . field || ! filter . value ) return true ; // Skip incomplete filters
338+
339+ const fieldValue = record [ filter . field ] ;
340+ const filterValue = filter . value ;
341+ const filterValue2 = filter . value2 ;
342+
343+ // Handle date filtering
344+ if ( filter . type === "date" || filter . type === "date-range" ) {
345+ const fieldDate = new Date ( fieldValue ) ;
346+ const valueDate = new Date ( filterValue ) ;
347+
348+ if ( isNaN ( fieldDate . getTime ( ) ) || isNaN ( valueDate . getTime ( ) ) ) {
349+ return true ; // Skip invalid dates
350+ }
351+
352+ switch ( filter . operator ) {
353+ case "==" :
354+ return fieldDate . toDateString ( ) === valueDate . toDateString ( ) ;
355+ case "!=" :
356+ return fieldDate . toDateString ( ) !== valueDate . toDateString ( ) ;
357+ case ">=" :
358+ return fieldDate >= valueDate ;
359+ case "<=" :
360+ return fieldDate <= valueDate ;
361+ case "between" :
362+ if ( filterValue2 ) {
363+ const valueDate2 = new Date ( filterValue2 ) ;
364+ if ( ! isNaN ( valueDate2 . getTime ( ) ) ) {
365+ return fieldDate >= valueDate && fieldDate <= valueDate2 ;
366+ }
367+ }
368+ return true ; // Skip incomplete between filter
369+ default :
370+ return true ;
371+ }
372+ }
373+
374+ // Handle text/numeric filtering
375+ switch ( filter . operator ) {
376+ case "==" :
377+ return String ( fieldValue ) === filterValue ;
378+ case "!=" :
379+ return String ( fieldValue ) !== filterValue ;
380+ case ">" :
381+ return Number ( fieldValue ) > Number ( filterValue ) ;
382+ case "<" :
383+ return Number ( fieldValue ) < Number ( filterValue ) ;
384+ case ">=" :
385+ return Number ( fieldValue ) >= Number ( filterValue ) ;
386+ case "<=" :
387+ return Number ( fieldValue ) <= Number ( filterValue ) ;
388+ case "contains" :
389+ return String ( fieldValue )
390+ . toLowerCase ( )
391+ . includes ( filterValue . toLowerCase ( ) ) ;
392+ case "!contains" :
393+ return ! String ( fieldValue )
394+ . toLowerCase ( )
395+ . includes ( filterValue . toLowerCase ( ) ) ;
396+ default :
397+ return true ;
398+ }
399+ }
239400}
0 commit comments