@@ -411,10 +411,11 @@ export function Table({
411411 setTableParams ,
412412 ] )
413413
414- /** Save replaces `config` wholesale, so the current layout is spread back in.
415- * With a view active that's the view's own stored layout; on "All" it's the
416- * table metadata the grid is currently rendering from — without which "Save as
417- * view" would create a layout-less view and then reset the grid to defaults. */
414+ /** The payload for creating a view, and the left-hand side of the dirty check.
415+ * Carries the current layout so "Save as view" from "All" captures the widths /
416+ * order / pins the grid is rendering (they live in the table's shared metadata
417+ * until a view owns them) instead of creating a layout-less view that then
418+ * resets the grid. Updates never send this — they send a merge patch. */
418419 const currentViewConfig = useMemo < TableViewConfig > (
419420 ( ) => ( {
420421 ...( activeView ?. config ?? tableData ?. metadata ) ,
@@ -468,8 +469,12 @@ export function Table({
468469
469470 const handleSaveView = ( ) => {
470471 if ( activeView ) {
472+ // Only the fields Save owns, merged server-side — never a client-built full
473+ // config. A full replace from a cached snapshot would drop a layout write
474+ // still in flight (and vice versa). `null`/`[]` merge as explicit values, so
475+ // clearing a filter or unhiding every column still persists as a removal.
471476 updateViewMutation . mutate (
472- { viewId : activeView . id , config : currentViewConfig } ,
477+ { viewId : activeView . id , configPatch : { filter , sort : sortQuery , hiddenColumns } } ,
473478 { onError : ( error ) => toast . error ( getErrorMessage ( error , 'Failed to save view' ) ) }
474479 )
475480 return
0 commit comments