@@ -74,6 +74,7 @@ import {
7474 useUpdateColumn ,
7575 useUpdateTableMetadata ,
7676 useUpdateTableRow ,
77+ useUpdateWorkflowGroup ,
7778} from '@/hooks/queries/tables'
7879import { useDeploymentInfo , useDeployWorkflow } from '@/hooks/queries/deployments'
7980import { useLogByExecutionId } from '@/hooks/queries/logs'
@@ -432,6 +433,7 @@ export function Table({
432433 const cancelRunsMutation = useCancelTableRuns ( { workspaceId, tableId } )
433434 const runGroupMutation = useRunGroup ( { workspaceId, tableId } )
434435 const deleteWorkflowGroupMutation = useDeleteWorkflowGroup ( { workspaceId, tableId } )
436+ const updateWorkflowGroupMutation = useUpdateWorkflowGroup ( { workspaceId, tableId } )
435437
436438 const handleRunGroup = useCallback (
437439 ( groupId : string , workflowId : string , mode : 'all' | 'incomplete' = 'all' ) => {
@@ -659,6 +661,7 @@ export function Table({
659661
660662 const columnsRef = useRef ( displayColumns )
661663 const schemaColumnsRef = useRef ( columns )
664+ const workflowGroupsRef = useRef ( tableWorkflowGroups )
662665 const rowsRef = useRef ( rows )
663666 const selectionAnchorRef = useRef ( selectionAnchor )
664667 const selectionFocusRef = useRef ( selectionFocus )
@@ -668,6 +671,7 @@ export function Table({
668671
669672 columnsRef . current = displayColumns
670673 schemaColumnsRef . current = columns
674+ workflowGroupsRef . current = tableWorkflowGroups
671675 rowsRef . current = rows
672676 selectionAnchorRef . current = selectionAnchor
673677 selectionFocusRef . current = selectionFocus
@@ -1302,14 +1306,35 @@ export function Table({
13021306 }
13031307
13041308 useEffect ( ( ) => {
1305- if ( ! tableData ?. metadata || metadataSeededRef . current ) return
1309+ if ( ! tableData ?. metadata ) return
13061310 if ( ! tableData . metadata . columnWidths && ! tableData . metadata . columnOrder ) return
1307- metadataSeededRef . current = true
1308- if ( tableData . metadata . columnWidths ) {
1309- setColumnWidths ( tableData . metadata . columnWidths )
1311+ // First load: seed both from the server and remember we've seeded.
1312+ if ( ! metadataSeededRef . current ) {
1313+ metadataSeededRef . current = true
1314+ if ( tableData . metadata . columnWidths ) {
1315+ setColumnWidths ( tableData . metadata . columnWidths )
1316+ }
1317+ if ( tableData . metadata . columnOrder ) {
1318+ setColumnOrder ( tableData . metadata . columnOrder )
1319+ }
1320+ return
13101321 }
1311- if ( tableData . metadata . columnOrder ) {
1312- setColumnOrder ( tableData . metadata . columnOrder )
1322+ // After first load: only re-seed `columnOrder` when the *set of columns*
1323+ // changes (e.g. a workflow group adds/removes outputs server-side). Pure
1324+ // reorders are left alone so an in-flight optimistic drag isn't clobbered
1325+ // by a refetch returning the pre-drag order.
1326+ const serverOrder = tableData . metadata . columnOrder
1327+ if ( serverOrder ) {
1328+ const localOrder = columnOrderRef . current
1329+ const serverSet = new Set ( serverOrder )
1330+ const localSet = new Set ( localOrder ?? [ ] )
1331+ const setChanged =
1332+ ! localOrder ||
1333+ serverSet . size !== localSet . size ||
1334+ serverOrder . some ( ( n ) => ! localSet . has ( n ) )
1335+ if ( setChanged ) {
1336+ setColumnOrder ( serverOrder )
1337+ }
13131338 }
13141339 } , [ tableData ?. metadata ] )
13151340
@@ -2264,39 +2289,70 @@ export function Table({
22642289 const previousWidth = columnWidthsRef . current [ columnToDelete ] ?? null
22652290 const orderSnapshot = currentOrder ? [ ...currentOrder ] : null
22662291
2267- deleteColumnMutation . mutate ( columnToDelete , {
2268- onSuccess : ( ) => {
2269- deletedOriginalPositions . push ( entry . position )
2270- pushUndoRef . current ( {
2271- type : 'delete-column' ,
2272- columnName : columnToDelete ,
2273- columnType : entry . def ?. type ?? 'string' ,
2274- columnPosition : adjustedPosition >= 0 ? adjustedPosition : cols . length ,
2275- columnUnique : entry . def ?. unique ?? false ,
2276- columnRequired : entry . def ?. required ?? false ,
2277- cellData,
2278- previousOrder : orderSnapshot ,
2279- previousWidth,
2292+ const onDeleted = ( ) => {
2293+ deletedOriginalPositions . push ( entry . position )
2294+ pushUndoRef . current ( {
2295+ type : 'delete-column' ,
2296+ columnName : columnToDelete ,
2297+ columnType : entry . def ?. type ?? 'string' ,
2298+ columnPosition : adjustedPosition >= 0 ? adjustedPosition : cols . length ,
2299+ columnUnique : entry . def ?. unique ?? false ,
2300+ columnRequired : entry . def ?. required ?? false ,
2301+ cellData,
2302+ previousOrder : orderSnapshot ,
2303+ previousWidth,
2304+ } )
2305+
2306+ const { [ columnToDelete ] : _removedWidth , ...cleanedWidths } = columnWidthsRef . current
2307+ setColumnWidths ( cleanedWidths )
2308+ columnWidthsRef . current = cleanedWidths
2309+
2310+ if ( currentOrder ) {
2311+ currentOrder = currentOrder . filter ( ( n ) => n !== columnToDelete )
2312+ setColumnOrder ( currentOrder )
2313+ updateMetadataRef . current ( {
2314+ columnWidths : cleanedWidths ,
2315+ columnOrder : currentOrder ,
22802316 } )
2317+ } else {
2318+ updateMetadataRef . current ( { columnWidths : cleanedWidths } )
2319+ }
22812320
2282- const { [ columnToDelete ] : _removedWidth , ...cleanedWidths } = columnWidthsRef . current
2283- setColumnWidths ( cleanedWidths )
2284- columnWidthsRef . current = cleanedWidths
2321+ deleteNext ( index + 1 )
2322+ }
22852323
2286- if ( currentOrder ) {
2287- currentOrder = currentOrder . filter ( ( n ) => n !== columnToDelete )
2288- setColumnOrder ( currentOrder )
2289- updateMetadataRef . current ( {
2290- columnWidths : cleanedWidths ,
2291- columnOrder : currentOrder ,
2292- } )
2293- } else {
2294- updateMetadataRef . current ( { columnWidths : cleanedWidths } )
2295- }
2324+ // Workflow-output columns are owned by a group: route the delete through
2325+ // `updateWorkflowGroup` so the same code path fires whether the user
2326+ // deselects the output in the sidebar or right-clicks Delete column.
2327+ // Falls back to deleting the whole group when this is its last output,
2328+ // since a group with zero outputs is invalid.
2329+ const groupId = entry . def ?. workflowGroupId
2330+ const group = groupId
2331+ ? workflowGroupsRef . current . find ( ( g ) => g . id === groupId )
2332+ : undefined
2333+ if ( group ) {
2334+ const remainingOutputs = group . outputs . filter ( ( o ) => o . columnName !== columnToDelete )
2335+ if ( remainingOutputs . length === 0 ) {
2336+ deleteWorkflowGroupMutation . mutate (
2337+ { groupId : group . id } ,
2338+ { onSuccess : onDeleted }
2339+ )
2340+ } else {
2341+ updateWorkflowGroupMutation . mutate (
2342+ {
2343+ groupId : group . id ,
2344+ workflowId : group . workflowId ,
2345+ name : group . name ,
2346+ dependencies : group . dependencies ,
2347+ outputs : remainingOutputs ,
2348+ } ,
2349+ { onSuccess : onDeleted }
2350+ )
2351+ }
2352+ return
2353+ }
22962354
2297- deleteNext ( index + 1 )
2298- } ,
2299- } )
2355+ deleteColumnMutation . mutate ( columnToDelete , { onSuccess : onDeleted } )
23002356 }
23012357
23022358 setSelectionAnchor ( null )
0 commit comments