Skip to content

Commit 20150f7

Browse files
fix(tables): key-order-stable dirty check, reset layout when switching to All
1 parent 8ab2d52 commit 20150f7

2 files changed

Lines changed: 28 additions & 11 deletions

File tree

apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table-grid/table-grid.tsx

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1759,20 +1759,22 @@ export function TableGrid({
17591759
// With a view active its config owns the layout; otherwise the table's own
17601760
// metadata does. Switching views re-seeds unconditionally so the incoming
17611761
// view's layout replaces the outgoing one.
1762-
const source = viewLayout ?? tableData?.metadata
1762+
const source = viewLayout ?? tableData?.metadata ?? null
17631763
const switchedView = viewLayoutKey !== seededLayoutKeyRef.current
1764-
if (!source) return
1765-
if (!source.columnWidths && !source.columnOrder && !source.pinnedColumns && !switchedView)
1766-
return
17671764

17681765
if (!metadataSeededRef.current || switchedView) {
1766+
// A switch resets even when there is nothing to seed from — a table created
1767+
// with `metadata: null` would otherwise keep showing the outgoing view's
1768+
// layout, since layout written under a view never populates table metadata.
1769+
if (!source && !switchedView) return
17691770
metadataSeededRef.current = true
17701771
seededLayoutKeyRef.current = viewLayoutKey
1771-
setColumnWidths(source.columnWidths ?? {})
1772-
setColumnOrder(source.columnOrder ?? null)
1773-
setPinnedColumns(source.pinnedColumns ?? [])
1772+
setColumnWidths(source?.columnWidths ?? {})
1773+
setColumnOrder(source?.columnOrder ?? null)
1774+
setPinnedColumns(source?.pinnedColumns ?? [])
17741775
return
17751776
}
1777+
if (!source) return
17761778
// After first load: only re-seed `columnOrder` when the *set of columns*
17771779
// changes (e.g. a workflow group adds/removes outputs server-side). Pure
17781780
// reorders are left alone so an in-flight optimistic drag isn't clobbered

apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table.tsx

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -141,18 +141,33 @@ const NO_VIEWS: TableViewWire[] = []
141141

142142
type ViewModalState = { mode: 'create' } | { mode: 'rename'; viewId: string } | null
143143

144+
/**
145+
* Order-insensitive JSON, used to compare a locally-built config against one that
146+
* has round-tripped through Postgres. `jsonb` does not preserve object key order
147+
* (`{status,plan}` comes back `{plan,status}`), so a plain `JSON.stringify` would
148+
* report any multi-key filter as permanently dirty. Array order is preserved —
149+
* it is meaningful for `columnOrder`.
150+
*/
151+
function stableStringify(value: unknown): string {
152+
if (value === null || typeof value !== 'object') return JSON.stringify(value) ?? 'null'
153+
if (Array.isArray(value)) return `[${value.map(stableStringify).join(',')}]`
154+
const entries = Object.entries(value as Record<string, unknown>)
155+
.filter(([, entry]) => entry !== undefined)
156+
.sort(([left], [right]) => (left < right ? -1 : left > right ? 1 : 0))
157+
return `{${entries.map(([key, entry]) => `${JSON.stringify(key)}:${stableStringify(entry)}`).join(',')}}`
158+
}
159+
144160
/**
145161
* Structural equality for the parts of a view config the user edits directly.
146162
* Column layout (widths/order/pinning) is excluded — it auto-saves into the
147163
* active view as the user drags, so it can never be the thing that is "unsaved".
148164
*
149-
* Compares JSON rather than field-by-field because `filter` is an arbitrarily
150-
* nested predicate tree; key order is stable since both sides are built by the
151-
* same converters.
165+
* Compares serialized form rather than field-by-field because `filter` is an
166+
* arbitrarily nested predicate tree.
152167
*/
153168
function isSameViewConfig(a: TableViewConfig, b: TableViewConfig): boolean {
154169
const normalize = (config: TableViewConfig) =>
155-
JSON.stringify({
170+
stableStringify({
156171
filter: config.filter ?? null,
157172
sort: config.sort ?? null,
158173
hiddenColumns: [...(config.hiddenColumns ?? [])].sort(),

0 commit comments

Comments
 (0)