Skip to content

Commit da8a17a

Browse files
fix(tables): guard default demotion, use the applied filter for dirty/save
1 parent 86c7d26 commit da8a17a

2 files changed

Lines changed: 21 additions & 4 deletions

File tree

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

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -529,11 +529,11 @@ export function Table({
529529
const currentViewConfig = useMemo<TableViewConfig>(
530530
() => ({
531531
...(activeView?.config ?? tableData?.metadata),
532-
filter,
532+
filter: effectiveFilter,
533533
sort: sortQuery,
534534
hiddenColumns: effectiveHiddenColumns,
535535
}),
536-
[activeView, tableData?.metadata, filter, sortQuery, effectiveHiddenColumns]
536+
[activeView, tableData?.metadata, effectiveFilter, sortQuery, effectiveHiddenColumns]
537537
)
538538

539539
/**
@@ -543,7 +543,7 @@ export function Table({
543543
*/
544544
const isViewDirty = activeView
545545
? !isSameViewConfig(currentViewConfig, activeView.config)
546-
: Boolean(filter) || Boolean(sortQuery) || effectiveHiddenColumns.length > 0
546+
: Boolean(effectiveFilter) || Boolean(sortQuery) || effectiveHiddenColumns.length > 0
547547

548548
/** Rename targets a live view rather than a snapshot, so a concurrent rename or
549549
* delete can't leave the modal editing stale data. */
@@ -570,8 +570,14 @@ export function Table({
570570
* a `configPatch` so the server merges it — two overlapping layout writes must
571571
* not each replace the whole blob from their own snapshot. With no view active
572572
* the grid keeps writing the table's shared metadata. */
573+
/** Last layout the grid reported, patch by patch. View layout writes have no
574+
* optimistic cache update, so `activeView.config` lags an in-flight resize —
575+
* this is what a blank "New view" copies so the grid can't snap back. */
576+
const liveLayoutRef = useRef<TableMetadata>({})
577+
573578
const handlePersistLayout = useCallback(
574579
(patch: TableMetadata) => {
580+
liveLayoutRef.current = { ...liveLayoutRef.current, ...patch }
575581
if (!activeView) return
576582
updateViewMutation.mutate(
577583
{ viewId: activeView.id, configPatch: patch },
@@ -591,7 +597,7 @@ export function Table({
591597
{
592598
viewId: activeView.id,
593599
configPatch: {
594-
filter,
600+
filter: effectiveFilter,
595601
sort: sortQuery,
596602
hiddenColumns: effectiveHiddenColumns,
597603
},

apps/sim/lib/table/views/service.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,17 @@ export async function updateTableView(data: UpdateTableViewData): Promise<TableV
171171
if (data.isDefault !== undefined) patch.isDefault = data.isDefault
172172

173173
const row = await db.transaction(async (tx) => {
174+
// Confirm the target exists BEFORE demoting. The demotion has to run first —
175+
// the partial unique index rejects a second default — but on a PATCH naming a
176+
// missing view the target update matches nothing, so without this the demote
177+
// would still commit and silently clear the table's real default.
178+
const [existing] = await tx
179+
.select({ id: tableViews.id })
180+
.from(tableViews)
181+
.where(and(eq(tableViews.id, data.viewId), eq(tableViews.tableId, data.tableId)))
182+
.limit(1)
183+
if (!existing) return null
184+
174185
if (data.isDefault === true) {
175186
await tx
176187
.update(tableViews)

0 commit comments

Comments
 (0)