@@ -2,6 +2,7 @@ import { makeAutoObservable, runInAction } from "mobx";
22import type { EvaluationRow } from "./types/eval-protocol" ;
33import type { PivotConfig , FilterGroup } from "./types/filters" ;
44import flattenJson from "./util/flatten-json" ;
5+ import type { FlatJson } from "./util/flatten-json" ;
56
67// Default pivot configuration
78const DEFAULT_PIVOT_CONFIG : PivotConfig = {
@@ -31,17 +32,35 @@ export class GlobalState {
3132 pivotConfig : PivotConfig ;
3233 // Table filter configuration
3334 tableFilterConfig : FilterGroup [ ] ;
35+ // Debounced, actually applied table filter configuration (for performance while typing)
36+ appliedTableFilterConfig : FilterGroup [ ] ;
3437 // Pagination configuration
3538 currentPage : number ;
3639 pageSize : number ;
3740 // Loading state
3841 isLoading : boolean = true ;
3942
43+ // Cached, denormalized data for performance
44+ // rollout_id -> flattened row
45+ private flattenedById : Record < string , FlatJson > = { } ;
46+ // rollout_id -> created_at timestamp (ms) for cheap sort
47+ private createdAtMsById : Record < string , number > = { } ;
48+
49+ // Debounce timers for localStorage saves and filter application
50+ private savePivotConfigTimer : ReturnType < typeof setTimeout > | null = null ;
51+ private saveTableFilterConfigTimer : ReturnType < typeof setTimeout > | null =
52+ null ;
53+ private savePaginationConfigTimer : ReturnType < typeof setTimeout > | null =
54+ null ;
55+ private applyTableFilterTimer : ReturnType < typeof setTimeout > | null = null ;
56+
4057 constructor ( ) {
4158 // Load pivot config from localStorage or use defaults
4259 this . pivotConfig = this . loadPivotConfig ( ) ;
4360 // Load table filter config from localStorage or use defaults
4461 this . tableFilterConfig = this . loadTableFilterConfig ( ) ;
62+ // Initialize applied filter config with current value
63+ this . appliedTableFilterConfig = this . tableFilterConfig . slice ( ) ;
4564 // Load pagination config from localStorage or use defaults
4665 const paginationConfig = this . loadPaginationConfig ( ) ;
4766 this . currentPage = paginationConfig . currentPage ;
@@ -101,41 +120,55 @@ export class GlobalState {
101120
102121 // Save pivot configuration to localStorage
103122 private savePivotConfig ( ) {
104- try {
105- localStorage . setItem ( "pivotConfig" , JSON . stringify ( this . pivotConfig ) ) ;
106- } catch ( error ) {
107- console . warn ( "Failed to save pivot config to localStorage:" , error ) ;
108- }
123+ if ( this . savePivotConfigTimer ) clearTimeout ( this . savePivotConfigTimer ) ;
124+ this . savePivotConfigTimer = setTimeout ( ( ) => {
125+ try {
126+ localStorage . setItem ( "pivotConfig" , JSON . stringify ( this . pivotConfig ) ) ;
127+ } catch ( error ) {
128+ console . warn ( "Failed to save pivot config to localStorage:" , error ) ;
129+ }
130+ } , 200 ) ;
109131 }
110132
111133 // Save table filter configuration to localStorage
112134 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- }
135+ if ( this . saveTableFilterConfigTimer )
136+ clearTimeout ( this . saveTableFilterConfigTimer ) ;
137+ this . saveTableFilterConfigTimer = setTimeout ( ( ) => {
138+ try {
139+ localStorage . setItem (
140+ "tableFilterConfig" ,
141+ JSON . stringify ( this . tableFilterConfig )
142+ ) ;
143+ } catch ( error ) {
144+ console . warn (
145+ "Failed to save table filter config to localStorage:" ,
146+ error
147+ ) ;
148+ }
149+ } , 200 ) ;
124150 }
125151
126152 // Save pagination configuration to localStorage
127153 private savePaginationConfig ( ) {
128- try {
129- localStorage . setItem (
130- "paginationConfig" ,
131- JSON . stringify ( {
132- currentPage : this . currentPage ,
133- pageSize : this . pageSize ,
134- } )
135- ) ;
136- } catch ( error ) {
137- console . warn ( "Failed to save pagination config to localStorage:" , error ) ;
138- }
154+ if ( this . savePaginationConfigTimer )
155+ clearTimeout ( this . savePaginationConfigTimer ) ;
156+ this . savePaginationConfigTimer = setTimeout ( ( ) => {
157+ try {
158+ localStorage . setItem (
159+ "paginationConfig" ,
160+ JSON . stringify ( {
161+ currentPage : this . currentPage ,
162+ pageSize : this . pageSize ,
163+ } )
164+ ) ;
165+ } catch ( error ) {
166+ console . warn (
167+ "Failed to save pagination config to localStorage:" ,
168+ error
169+ ) ;
170+ }
171+ } , 200 ) ;
139172 }
140173
141174 // Update pivot configuration and save to localStorage
@@ -148,6 +181,12 @@ export class GlobalState {
148181 updateTableFilterConfig ( filters : FilterGroup [ ] ) {
149182 this . tableFilterConfig = filters ;
150183 this . saveTableFilterConfig ( ) ;
184+
185+ // Debounce application of filters to avoid re-filtering on every keystroke
186+ if ( this . applyTableFilterTimer ) clearTimeout ( this . applyTableFilterTimer ) ;
187+ this . applyTableFilterTimer = setTimeout ( ( ) => {
188+ this . appliedTableFilterConfig = this . tableFilterConfig . slice ( ) ;
189+ } , 150 ) ;
151190 }
152191
153192 // Update pagination configuration and save to localStorage
@@ -175,6 +214,7 @@ export class GlobalState {
175214 // Reset table filter configuration to defaults
176215 resetTableFilterConfig ( ) {
177216 this . tableFilterConfig = [ ...DEFAULT_TABLE_FILTER_CONFIG ] ;
217+ this . appliedTableFilterConfig = [ ...DEFAULT_TABLE_FILTER_CONFIG ] ;
178218 this . saveTableFilterConfig ( ) ;
179219 }
180220
@@ -217,7 +257,13 @@ export class GlobalState {
217257 if ( ! row . execution_metadata ?. rollout_id ) {
218258 return ;
219259 }
220- this . dataset [ row . execution_metadata . rollout_id ] = row ;
260+ const rolloutId = row . execution_metadata . rollout_id ;
261+ this . dataset [ rolloutId ] = row ;
262+ // Cache created_at in ms for cheap sorts
263+ const createdMs = new Date ( row . created_at ) . getTime ( ) ;
264+ this . createdAtMsById [ rolloutId ] = isNaN ( createdMs ) ? 0 : createdMs ;
265+ // Cache flattened row for filtering/pivot keys
266+ this . flattenedById [ rolloutId ] = flattenJson ( row ) ;
221267 } ) ;
222268
223269 runInAction ( ( ) => {
@@ -253,42 +299,52 @@ export class GlobalState {
253299 }
254300
255301 // Computed values following MobX best practices
256- get sortedDataset ( ) {
257- return Object . values ( this . dataset ) . sort (
258- ( a , b ) =>
259- new Date ( b . created_at ) . getTime ( ) - new Date ( a . created_at ) . getTime ( )
302+ get sortedIds ( ) {
303+ return Object . keys ( this . dataset ) . sort (
304+ ( a , b ) => ( this . createdAtMsById [ b ] ?? 0 ) - ( this . createdAtMsById [ a ] ?? 0 )
260305 ) ;
261306 }
262307
308+ get sortedDataset ( ) {
309+ return this . sortedIds . map ( ( id ) => this . dataset [ id ] ) ;
310+ }
311+
263312 get flattenedDataset ( ) {
264- return this . sortedDataset . map ( ( row ) => flattenJson ( row ) ) ;
313+ return this . sortedIds . map ( ( id ) => this . flattenedById [ id ] ) ;
265314 }
266315
267316 get filteredFlattenedDataset ( ) {
268- if ( this . tableFilterConfig . length === 0 ) {
317+ if ( this . appliedTableFilterConfig . length === 0 ) {
269318 return this . flattenedDataset ;
270319 }
271320
272- const filterFunction = this . createFilterFunction ( this . tableFilterConfig ) ;
321+ const filterFunction = this . createFilterFunction (
322+ this . appliedTableFilterConfig
323+ ) ;
273324 return this . flattenedDataset . filter ( filterFunction ) ;
274325 }
275326
276327 get filteredOriginalDataset ( ) {
277- if ( this . tableFilterConfig . length === 0 ) {
328+ if ( this . appliedTableFilterConfig . length === 0 ) {
278329 return this . sortedDataset ;
279330 }
280331
281- const filterFunction = this . createFilterFunction ( this . tableFilterConfig ) ;
282- return this . sortedDataset . filter ( ( row ) => {
283- const flattened = flattenJson ( row ) ;
284- return filterFunction ( flattened ) ;
285- } ) ;
332+ const filterFunction = this . createFilterFunction (
333+ this . appliedTableFilterConfig
334+ ) ;
335+ return this . sortedIds
336+ . filter ( ( id ) => filterFunction ( this . flattenedById [ id ] ) )
337+ . map ( ( id ) => this . dataset [ id ] ) ;
286338 }
287339
288340 get flattenedDatasetKeys ( ) {
289341 const keySet = new Set < string > ( ) ;
290- this . flattenedDataset . forEach ( ( row ) => {
291- Object . keys ( row ) . forEach ( ( key ) => keySet . add ( key ) ) ;
342+ // Iterate over cached flattened rows to build a unique key list
343+ this . sortedIds . forEach ( ( id ) => {
344+ const flat = this . flattenedById [ id ] ;
345+ if ( flat ) {
346+ Object . keys ( flat ) . forEach ( ( key ) => keySet . add ( key ) ) ;
347+ }
292348 } ) ;
293349 return Array . from ( keySet ) ;
294350 }
0 commit comments