@@ -358,6 +358,14 @@ export function Table({
358358 ( ( previousName : string , newName : string ) => void ) | null
359359 > ( null )
360360
361+ // Declared before `useTable`: the rows query is gated on `viewsLoaded`, since a
362+ // view owns the filter/sort the query runs with.
363+ const { data : views = NO_VIEWS , isSuccess : viewsLoaded } = useTableViews ( {
364+ workspaceId,
365+ tableId,
366+ enabled : viewsEnabled ,
367+ } )
368+
361369 // Single source of truth for `useTable` — drives both the grid render and
362370 // the wrapper's slideouts/modals. The grid receives the bundle as props.
363371 const {
@@ -373,12 +381,10 @@ export function Table({
373381 workspaceId,
374382 tableId,
375383 queryOptions,
376- } )
377-
378- const { data : views = NO_VIEWS , isSuccess : viewsLoaded } = useTableViews ( {
379- workspaceId,
380- tableId,
381- enabled : viewsEnabled ,
384+ // Without this the first fetch runs against an empty filter and the grid
385+ // paints the unfiltered set before refetching. Gates only the first pass —
386+ // `viewsLoaded` stays true after, so later filter edits fetch immediately.
387+ rowsEnabled : ! viewsEnabled || viewsLoaded ,
382388 } )
383389 const createViewMutation = useCreateTableView ( { workspaceId, tableId } )
384390 const updateViewMutation = useUpdateTableView ( { workspaceId, tableId } )
@@ -468,7 +474,14 @@ export function Table({
468474 // back to "All" without touching state, for the same reason. An explicit
469475 // `?sort=` alongside `?view=` also wins over the view's stored sort.
470476 seededViewIdRef . current = activeView ?. id ?? null
471- if ( activeView ) applyViewConfig ( activeView . config , localWork ( ) )
477+ if ( activeView ) {
478+ applyViewConfig ( activeView . config , localWork ( ) )
479+ } else {
480+ // Nothing to apply, but the URL still names a view that no longer exists.
481+ // Rewrite it so a stale bookmark can't be copied on, and so the param
482+ // matches the All the UI is already showing.
483+ setTableParams ( { view : ALL_VIEW_PARAM } )
484+ }
472485 return
473486 }
474487
@@ -590,13 +603,16 @@ export function Table({
590603 const handlePersistLayout = useCallback (
591604 ( patch : TableMetadata ) => {
592605 liveLayoutRef . current = { ...liveLayoutRef . current , ...patch }
593- if ( ! activeView ) return
606+ // The resize grip and drag handles stay live for read-only members, so
607+ // without this a resize fires a write-gated PATCH and an error toast. Local
608+ // layout still updates — only the persist is suppressed.
609+ if ( ! activeView || ! userPermissions . canEdit ) return
594610 updateViewMutation . mutate (
595611 { viewId : activeView . id , configPatch : patch } ,
596612 { onError : ( error ) => toast . error ( getErrorMessage ( error , 'Failed to save layout' ) ) }
597613 )
598614 } ,
599- [ activeView ]
615+ [ activeView , userPermissions . canEdit ]
600616 )
601617
602618 const handleSaveView = ( ) => {
0 commit comments