@@ -10,7 +10,13 @@ import { useParams } from 'next/navigation'
1010import { usePostHog } from 'posthog-js/react'
1111import type { RunLimit , RunMode , TableFindMatch } from '@/lib/api/contracts/tables'
1212import { captureEvent } from '@/lib/posthog/client'
13- import type { ColumnDefinition , Filter , TableRow as TableRowType , WorkflowGroup } from '@/lib/table'
13+ import type {
14+ ColumnDefinition ,
15+ Filter ,
16+ TableMetadata ,
17+ TableRow as TableRowType ,
18+ WorkflowGroup ,
19+ } from '@/lib/table'
1420import { getColumnId } from '@/lib/table/column-keys'
1521import { TABLE_LIMITS } from '@/lib/table/constants'
1622import { useUserPermissionsContext } from '@/app/workspace/[workspaceId]/providers/workspace-permissions-provider'
@@ -214,6 +220,16 @@ interface TableGridProps {
214220 * panel's Columns section edits the same list and the active view persists it.
215221 */
216222 hiddenColumns ?: string [ ]
223+ /** Active view's stored layout. When set it owns column order/width/pinning
224+ * instead of the table's shared `metadata`. */
225+ viewLayout ?: TableMetadata | null
226+ /** Identity of `viewLayout`'s source (the view id, or `null` for "All"). Changing
227+ * it re-seeds the grid — comparing the config object itself would re-seed on
228+ * every refetch. */
229+ viewLayoutKey ?: string | null
230+ /** Routes layout writes to the active view. Falls back to the table-metadata
231+ * mutation when absent. */
232+ onPersistLayout ?: ( patch : TableMetadata ) => void
217233 /**
218234 * Ref the grid populates with its `handleColumnRename` so the wrapper's
219235 * sidebars can fire a column rename back into the grid (rewrites local
@@ -313,6 +329,9 @@ export function TableGrid({
313329 onSelectionChange,
314330 queryOptions,
315331 hiddenColumns,
332+ viewLayout,
333+ viewLayoutKey = null ,
334+ onPersistLayout,
316335 columnRenameSinkRef,
317336 afterDeleteRowsSinkRef,
318337 afterDeleteAllSinkRef,
@@ -370,6 +389,8 @@ export function TableGrid({
370389 const pinnedColumnsRef = useRef ( pinnedColumns )
371390 pinnedColumnsRef . current = pinnedColumns
372391 const metadataSeededRef = useRef ( false )
392+ /** Which layout source the grid last seeded from, so a view switch re-seeds. */
393+ const seededLayoutKeyRef = useRef < string | null > ( null )
373394 const containerRef = useRef < HTMLDivElement > ( null )
374395 const scrollRef = useRef < HTMLDivElement > ( null )
375396 const theadRef = useRef < HTMLTableSectionElement > ( null )
@@ -1735,32 +1756,28 @@ export function TableGrid({
17351756 } , [ tableData ?. id ] )
17361757
17371758 useEffect ( ( ) => {
1738- if ( ! tableData ?. metadata ) return
1739- if (
1740- ! tableData . metadata . columnWidths &&
1741- ! tableData . metadata . columnOrder &&
1742- ! tableData . metadata . pinnedColumns
1743- )
1759+ // With a view active its config owns the layout; otherwise the table's own
1760+ // metadata does. Switching views re-seeds unconditionally so the incoming
1761+ // view's layout replaces the outgoing one.
1762+ const source = viewLayout ?? tableData ?. metadata
1763+ const switchedView = viewLayoutKey !== seededLayoutKeyRef . current
1764+ if ( ! source ) return
1765+ if ( ! source . columnWidths && ! source . columnOrder && ! source . pinnedColumns && ! switchedView )
17441766 return
1745- // First load: seed all from the server and remember we've seeded.
1746- if ( ! metadataSeededRef . current ) {
1767+
1768+ if ( ! metadataSeededRef . current || switchedView ) {
17471769 metadataSeededRef . current = true
1748- if ( tableData . metadata . columnWidths ) {
1749- setColumnWidths ( tableData . metadata . columnWidths )
1750- }
1751- if ( tableData . metadata . columnOrder ) {
1752- setColumnOrder ( tableData . metadata . columnOrder )
1753- }
1754- if ( tableData . metadata . pinnedColumns ) {
1755- setPinnedColumns ( tableData . metadata . pinnedColumns )
1756- }
1770+ seededLayoutKeyRef . current = viewLayoutKey
1771+ setColumnWidths ( source . columnWidths ?? { } )
1772+ setColumnOrder ( source . columnOrder ?? null )
1773+ setPinnedColumns ( source . pinnedColumns ?? [ ] )
17571774 return
17581775 }
17591776 // After first load: only re-seed `columnOrder` when the *set of columns*
17601777 // changes (e.g. a workflow group adds/removes outputs server-side). Pure
17611778 // reorders are left alone so an in-flight optimistic drag isn't clobbered
17621779 // by a refetch returning the pre-drag order.
1763- const serverOrder = tableData . metadata . columnOrder
1780+ const serverOrder = source . columnOrder
17641781 if ( serverOrder ) {
17651782 const localOrder = columnOrderRef . current
17661783 const serverSet = new Set ( serverOrder )
@@ -1771,7 +1788,7 @@ export function TableGrid({
17711788 setColumnOrder ( serverOrder )
17721789 }
17731790 }
1774- } , [ tableData ?. metadata ] )
1791+ } , [ tableData ?. metadata , viewLayout , viewLayoutKey ] )
17751792
17761793 useEffect ( ( ) => {
17771794 if ( ! isColumnSelection || ! selectionAnchor ) return
@@ -2057,7 +2074,7 @@ export function TableGrid({
20572074 batchUpdateAsyncRef . current = batchUpdateRowsMutation . mutateAsync
20582075
20592076 const updateMetadataRef = useRef ( updateMetadataMutation . mutate )
2060- updateMetadataRef . current = updateMetadataMutation . mutate
2077+ updateMetadataRef . current = onPersistLayout ?? updateMetadataMutation . mutate
20612078
20622079 const deleteWorkflowGroupRef = useRef ( deleteWorkflowGroupMutation . mutate )
20632080 deleteWorkflowGroupRef . current = deleteWorkflowGroupMutation . mutate
0 commit comments