diff --git a/src/frontend/components/circuit-tree-editor.tsx b/src/frontend/components/circuit-tree-editor.tsx index f339c62..979f90f 100644 --- a/src/frontend/components/circuit-tree-editor.tsx +++ b/src/frontend/components/circuit-tree-editor.tsx @@ -30,8 +30,10 @@ import { formatPhaseTypeLabel, formatValue, getCircuitSectionLabel, + getLegacyProjectColumnLayoutStorageKey, getProjectColumnLayoutStorageKey, isGridEditorControlTarget, + migrateLegacyColumnLayout, normalizeColumnOrder, parseStoredColumnLayout, } from "../utils/circuit-grid-model"; @@ -374,14 +376,23 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str setColumnOrder(allColumns.map((column) => column.key)); setVisibleColumnKeys(defaultVisibleColumnKeys); try { - const parsed = parseStoredColumnLayout( - localStorage.getItem( - getProjectColumnLayoutStorageKey(projectId) - ) ?? - localStorage.getItem( - LEGACY_COLUMN_LAYOUT_STORAGE_KEY - ) + const currentLayout = parseStoredColumnLayout( + localStorage.getItem(getProjectColumnLayoutStorageKey(projectId)) ); + const legacyLayout = + parseStoredColumnLayout( + localStorage.getItem( + getLegacyProjectColumnLayoutStorageKey(projectId) + ) + ) ?? + parseStoredColumnLayout( + localStorage.getItem(LEGACY_COLUMN_LAYOUT_STORAGE_KEY) + ); + const parsed = + currentLayout ?? + (legacyLayout + ? migrateLegacyColumnLayout(legacyLayout) + : null); if (!parsed) { return; } diff --git a/src/frontend/utils/circuit-grid-model.ts b/src/frontend/utils/circuit-grid-model.ts index 49263b4..880dbe9 100644 --- a/src/frontend/utils/circuit-grid-model.ts +++ b/src/frontend/utils/circuit-grid-model.ts @@ -129,6 +129,8 @@ export const defaultVisibleColumnKeys = allColumns .map((column) => column.key); const projectColumnLayoutStoragePrefix = + "circuitTreeEditor.columnLayout.v3"; +const legacyProjectColumnLayoutStoragePrefix = "circuitTreeEditor.columnLayout.v2"; export interface CircuitColumnLayout { @@ -162,6 +164,12 @@ export function getProjectColumnLayoutStorageKey( return `${projectColumnLayoutStoragePrefix}.${projectId}`; } +export function getLegacyProjectColumnLayoutStorageKey( + projectId: string +): string { + return `${legacyProjectColumnLayoutStoragePrefix}.${projectId}`; +} + export function normalizeColumnOrder(keys: CellKey[]): CellKey[] { const unique = [...new Set(keys)]; const allKeys = allColumns.map((column) => column.key); @@ -225,6 +233,28 @@ export function parseStoredColumnLayout( } } +export function migrateLegacyColumnLayout( + layout: CircuitColumnLayout +): CircuitColumnLayout { + const newlyVisibleKey: CellKey = "protectionSummary"; + const order = layout.order.filter( + (key) => key !== newlyVisibleKey + ) as CellKey[]; + const totalPowerIndex = order.indexOf("rowTotalPower"); + order.splice( + totalPowerIndex >= 0 ? totalPowerIndex + 1 : order.length, + 0, + newlyVisibleKey + ); + + return { + order, + visible: layout.visible.includes(newlyVisibleKey) + ? layout.visible + : [...layout.visible, newlyVisibleKey], + }; +} + const deviceOnlyColumns = new Set([ "quantity", "powerPerUnit", diff --git a/tests/circuit-grid-model.test.ts b/tests/circuit-grid-model.test.ts index 5e045d9..b0472b8 100644 --- a/tests/circuit-grid-model.test.ts +++ b/tests/circuit-grid-model.test.ts @@ -6,12 +6,14 @@ import { buildDeviceRowEditPatch, formatValue, getCircuitSectionLabel, + getLegacyProjectColumnLayoutStorageKey, getProjectColumnLayoutStorageKey, isGridEditorControlTarget, getBlockSortValue, getCellKind, getCircuitValue, getDeviceValue, + migrateLegacyColumnLayout, parseStoredColumnLayout, parseNumeric, } from "../src/frontend/utils/circuit-grid-model.js"; @@ -79,6 +81,10 @@ describe("circuit grid model", () => { it("stores valid column layouts under a project-specific key", () => { assert.equal( getProjectColumnLayoutStorageKey("project-1"), + "circuitTreeEditor.columnLayout.v3.project-1" + ); + assert.equal( + getLegacyProjectColumnLayoutStorageKey("project-1"), "circuitTreeEditor.columnLayout.v2.project-1" ); const layout = parseStoredColumnLayout( @@ -100,6 +106,39 @@ describe("circuit grid model", () => { assert.equal(parseStoredColumnLayout("{invalid"), null); }); + it("adds the circuit protection column to legacy project layouts", () => { + const legacyLayout = parseStoredColumnLayout( + JSON.stringify({ + order: [ + "equipmentIdentifier", + "displayName", + "rowTotalPower", + "remark", + ], + visible: [ + "equipmentIdentifier", + "displayName", + "rowTotalPower", + "remark", + ], + }) + ); + assert.ok(legacyLayout); + + const migrated = migrateLegacyColumnLayout(legacyLayout); + assert.equal( + migrated.order.indexOf("protectionSummary"), + migrated.order.indexOf("rowTotalPower") + 1 + ); + assert.deepEqual(migrated.visible, [ + "equipmentIdentifier", + "displayName", + "rowTotalPower", + "remark", + "protectionSummary", + ]); + }); + it("shows stable circuit sections with German labels", () => { assert.equal( getCircuitSectionLabel({