Skip to content

Commit 1fe9c5a

Browse files
author
Dylan Huang
committed
Implement global pivot configuration management in GlobalState and update PivotTab to utilize it, including loading, saving, and resetting functionality.
1 parent 3dabef1 commit 1fe9c5a

2 files changed

Lines changed: 140 additions & 43 deletions

File tree

vite-app/src/GlobalState.tsx

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,75 @@ import { makeAutoObservable } from "mobx";
22
import type { EvaluationRow } from "./types/eval-protocol";
33
import flattenJson from "./util/flatten-json";
44

5+
// Pivot configuration interface
6+
export interface PivotConfig {
7+
selectedRowFields: string[];
8+
selectedColumnFields: string[];
9+
selectedValueField: string;
10+
selectedAggregator: string;
11+
filters: Array<{ field: string; operator: string; value: string }>;
12+
}
13+
14+
// Default pivot configuration
15+
const DEFAULT_PIVOT_CONFIG: PivotConfig = {
16+
selectedRowFields: ["$.eval_metadata.name"],
17+
selectedColumnFields: ["$.input_metadata.completion_params.model"],
18+
selectedValueField: "$.evaluation_result.score",
19+
selectedAggregator: "avg",
20+
filters: [],
21+
};
22+
523
export class GlobalState {
624
isConnected: boolean = false;
725
// rollout_id -> EvaluationRow
826
dataset: Record<string, EvaluationRow> = {};
927
// rollout_id -> expanded
1028
expandedRows: Record<string, boolean> = {};
29+
// Pivot configuration
30+
pivotConfig: PivotConfig;
1131

1232
constructor() {
33+
// Load pivot config from localStorage or use defaults
34+
this.pivotConfig = this.loadPivotConfig();
1335
makeAutoObservable(this);
1436
}
1537

38+
// Load pivot configuration from localStorage
39+
private loadPivotConfig(): PivotConfig {
40+
try {
41+
const stored = localStorage.getItem("pivotConfig");
42+
if (stored) {
43+
const parsed = JSON.parse(stored);
44+
// Merge with defaults to handle any missing properties
45+
return { ...DEFAULT_PIVOT_CONFIG, ...parsed };
46+
}
47+
} catch (error) {
48+
console.warn("Failed to load pivot config from localStorage:", error);
49+
}
50+
return { ...DEFAULT_PIVOT_CONFIG };
51+
}
52+
53+
// Save pivot configuration to localStorage
54+
private savePivotConfig() {
55+
try {
56+
localStorage.setItem("pivotConfig", JSON.stringify(this.pivotConfig));
57+
} catch (error) {
58+
console.warn("Failed to save pivot config to localStorage:", error);
59+
}
60+
}
61+
62+
// Update pivot configuration and save to localStorage
63+
updatePivotConfig(updates: Partial<PivotConfig>) {
64+
Object.assign(this.pivotConfig, updates);
65+
this.savePivotConfig();
66+
}
67+
68+
// Reset pivot configuration to defaults
69+
resetPivotConfig() {
70+
this.pivotConfig = { ...DEFAULT_PIVOT_CONFIG };
71+
this.savePivotConfig();
72+
}
73+
1674
upsertRows(dataset: EvaluationRow[]) {
1775
dataset.forEach((row) => {
1876
if (!row.execution_metadata?.rollout_id) {

vite-app/src/components/PivotTab.tsx

Lines changed: 82 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import { observer } from "mobx-react";
22
import PivotTable from "./PivotTable";
33
import Select from "./Select";
4+
import Button from "./Button";
45
import { state } from "../App";
5-
import { useState } from "react";
6+
import { useEffect } from "react";
67

78
interface FieldSelectorProps {
89
title: string;
@@ -243,45 +244,61 @@ const FilterSelector = ({
243244
};
244245

245246
const PivotTab = observer(() => {
246-
const [selectedRowFields, setSelectedRowFields] = useState<string[]>([
247-
"$.eval_metadata.name",
248-
]);
249-
const [selectedColumnFields, setSelectedColumnFields] = useState<string[]>([
250-
"$.input_metadata.completion_params.model",
251-
]);
252-
const [selectedValueField, setSelectedValueField] = useState<string>(
253-
"$.evaluation_result.score"
254-
);
255-
const [selectedAggregator, setSelectedAggregator] = useState<string>("avg");
256-
const [filters, setFilters] = useState<
257-
Array<{ field: string; operator: string; value: string }>
258-
>([]);
247+
// Use global state instead of local state
248+
const { pivotConfig } = state;
249+
250+
// Update global state when configuration changes
251+
const updateRowFields = (index: number, value: string) => {
252+
const newRowFields = [...pivotConfig.selectedRowFields];
253+
newRowFields[index] = value;
254+
state.updatePivotConfig({ selectedRowFields: newRowFields });
255+
};
256+
257+
const updateColumnFields = (index: number, value: string) => {
258+
const newColumnFields = [...pivotConfig.selectedColumnFields];
259+
newColumnFields[index] = value;
260+
state.updatePivotConfig({ selectedColumnFields: newColumnFields });
261+
};
262+
263+
const updateValueField = (value: string) => {
264+
state.updatePivotConfig({ selectedValueField: value });
265+
};
266+
267+
const updateAggregator = (value: string) => {
268+
state.updatePivotConfig({ selectedAggregator: value });
269+
};
270+
271+
const updateFilters = (
272+
filters: Array<{ field: string; operator: string; value: string }>
273+
) => {
274+
state.updatePivotConfig({ filters });
275+
};
259276

260277
const createFieldHandler = (
261-
setter: React.Dispatch<React.SetStateAction<string[]>>
278+
updater: (index: number, value: string) => void
262279
) => {
263280
return (index: number, value: string) => {
264-
setter((prev) => {
265-
const newFields = [...prev];
266-
newFields[index] = value;
267-
return newFields;
268-
});
281+
updater(index, value);
269282
};
270283
};
271284

272285
const createAddHandler = (
273-
setter: React.Dispatch<React.SetStateAction<string[]>>
286+
fields: string[],
287+
updater: (fields: string[]) => void
274288
) => {
275289
return () => {
276-
setter((prev) => (prev.length < 3 ? [...prev, ""] : prev));
290+
if (fields.length < 3) {
291+
updater([...fields, ""]);
292+
}
277293
};
278294
};
279295

280296
const createRemoveHandler = (
281-
setter: React.Dispatch<React.SetStateAction<string[]>>
297+
fields: string[],
298+
updater: (fields: string[]) => void
282299
) => {
283300
return (index: number) => {
284-
setter((prev) => prev.filter((_, i) => i !== index));
301+
updater(fields.filter((_, i) => i !== index));
285302
};
286303
};
287304

@@ -338,63 +355,85 @@ const PivotTab = observer(() => {
338355
specific subsets of your data.
339356
</div>
340357

358+
{/* Controls Section with Reset Button */}
359+
<div className="mb-4 flex justify-between items-center">
360+
<Button
361+
onClick={() => state.resetPivotConfig()}
362+
variant="secondary"
363+
size="sm"
364+
>
365+
Reset to Defaults
366+
</Button>
367+
</div>
368+
341369
<FieldSelector
342370
title="Row Fields"
343-
fields={selectedRowFields}
344-
onFieldChange={createFieldHandler(setSelectedRowFields)}
345-
onAddField={createAddHandler(setSelectedRowFields)}
346-
onRemoveField={createRemoveHandler(setSelectedRowFields)}
371+
fields={pivotConfig.selectedRowFields}
372+
onFieldChange={createFieldHandler(updateRowFields)}
373+
onAddField={createAddHandler(pivotConfig.selectedRowFields, (fields) =>
374+
state.updatePivotConfig({ selectedRowFields: fields })
375+
)}
376+
onRemoveField={createRemoveHandler(
377+
pivotConfig.selectedRowFields,
378+
(fields) => state.updatePivotConfig({ selectedRowFields: fields })
379+
)}
347380
availableKeys={availableKeys}
348381
variant="row"
349382
/>
350383

351384
<FieldSelector
352385
title="Column Fields"
353-
fields={selectedColumnFields}
354-
onFieldChange={createFieldHandler(setSelectedColumnFields)}
355-
onAddField={createAddHandler(setSelectedColumnFields)}
356-
onRemoveField={createRemoveHandler(setSelectedColumnFields)}
386+
fields={pivotConfig.selectedColumnFields}
387+
onFieldChange={createFieldHandler(updateColumnFields)}
388+
onAddField={createAddHandler(
389+
pivotConfig.selectedColumnFields,
390+
(fields) => state.updatePivotConfig({ selectedColumnFields: fields })
391+
)}
392+
onRemoveField={createRemoveHandler(
393+
pivotConfig.selectedColumnFields,
394+
(fields) => state.updatePivotConfig({ selectedColumnFields: fields })
395+
)}
357396
availableKeys={availableKeys}
358397
variant="column"
359398
/>
360399

361400
<SingleFieldSelector
362401
title="Value Field"
363-
field={selectedValueField}
364-
onFieldChange={setSelectedValueField}
402+
field={pivotConfig.selectedValueField}
403+
onFieldChange={updateValueField}
365404
availableKeys={availableKeys}
366405
/>
367406

368407
<AggregatorSelector
369-
aggregator={selectedAggregator}
370-
onAggregatorChange={setSelectedAggregator}
408+
aggregator={pivotConfig.selectedAggregator}
409+
onAggregatorChange={updateAggregator}
371410
/>
372411

373412
<FilterSelector
374-
filters={filters}
375-
onFiltersChange={setFilters}
413+
filters={pivotConfig.filters}
414+
onFiltersChange={updateFilters}
376415
availableKeys={availableKeys}
377416
/>
378417

379418
<PivotTable
380419
data={state.flattenedDataset}
381420
rowFields={
382-
selectedRowFields.filter(
421+
pivotConfig.selectedRowFields.filter(
383422
(field) => field !== ""
384423
) as (keyof (typeof state.flattenedDataset)[number])[]
385424
}
386425
columnFields={
387-
selectedColumnFields.filter(
426+
pivotConfig.selectedColumnFields.filter(
388427
(field) => field !== ""
389428
) as (keyof (typeof state.flattenedDataset)[number])[]
390429
}
391430
valueField={
392-
selectedValueField as keyof (typeof state.flattenedDataset)[number]
431+
pivotConfig.selectedValueField as keyof (typeof state.flattenedDataset)[number]
393432
}
394-
aggregator={selectedAggregator as any}
433+
aggregator={pivotConfig.selectedAggregator as any}
395434
showRowTotals
396435
showColumnTotals
397-
filter={createFilterFunction(filters)}
436+
filter={createFilterFunction(pivotConfig.filters)}
398437
/>
399438
</div>
400439
);

0 commit comments

Comments
 (0)