From 84604eb22396db6919e6665f83f7f9d0bcfed306 Mon Sep 17 00:00:00 2001 From: Theodore Li Date: Mon, 6 Jul 2026 18:22:30 -0700 Subject: [PATCH 1/2] fix(tables): resolve column ids to display names in enrichment edit sidebar --- .../enrichments-sidebar/enrichment-config.tsx | 35 +++++++++++++------ 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/enrichments-sidebar/enrichment-config.tsx b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/enrichments-sidebar/enrichment-config.tsx index d7ace26b0ef..23820321337 100644 --- a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/enrichments-sidebar/enrichment-config.tsx +++ b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/enrichments-sidebar/enrichment-config.tsx @@ -17,6 +17,7 @@ import { toError } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import type { AddWorkflowGroupBodyInput } from '@/lib/api/contracts/tables' import type { ColumnDefinition, WorkflowGroup, WorkflowGroupOutput } from '@/lib/table' +import { columnMatchesRef, getColumnId } from '@/lib/table/column-keys' import { deriveOutputColumnName } from '@/lib/table/column-naming' import { FieldError } from '@/app/workspace/[workspaceId]/tables/[tableId]/components/sidebar-fields' import type { EnrichmentConfig as EnrichmentDef } from '@/enrichments/types' @@ -49,7 +50,7 @@ function defaultColumnFor( c.name.toLowerCase() === input.id.toLowerCase() || c.name.toLowerCase() === input.name.toLowerCase() ) - return match?.name ?? '' + return match ? getColumnId(match) : '' } /** @@ -71,14 +72,23 @@ export function EnrichmentConfig({ const updateColumn = useUpdateColumn({ workspaceId, tableId }) const isEditing = Boolean(existingGroup) - /** Output column's persisted name (edit mode), used to detect renames. */ - const originalOutputName = (outputId: string): string | undefined => - existingGroup?.outputs.find((o) => o.outputId === outputId)?.columnName + /** Output's column (edit mode). Persisted `columnName` refs hold a stable + * column id (name for legacy groups), so resolve id-or-name to the column. */ + const outputColumn = (outputId: string): ColumnDefinition | undefined => { + const ref = existingGroup?.outputs.find((o) => o.outputId === outputId)?.columnName + return ref === undefined ? undefined : allColumns.find((c) => columnMatchesRef(c, ref)) + } + + /** Output column's persisted display name (edit mode), used to detect renames. */ + const originalOutputName = (outputId: string): string | undefined => outputColumn(outputId)?.name const [inputMappings, setInputMappings] = useState>(() => { if (existingGroup) { const seed: Record = {} - for (const m of existingGroup.inputMappings ?? []) seed[m.inputName] = m.columnName + for (const m of existingGroup.inputMappings ?? []) { + const col = allColumns.find((c) => columnMatchesRef(c, m.columnName)) + seed[m.inputName] = col ? getColumnId(col) : m.columnName + } return seed } const seed: Record = {} @@ -94,7 +104,9 @@ export function EnrichmentConfig({ const seed: Record = {} if (existingGroup) { for (const o of existingGroup.outputs) { - if (o.outputId) seed[o.outputId] = o.columnName + if (!o.outputId) continue + const col = allColumns.find((c) => columnMatchesRef(c, o.columnName)) + seed[o.outputId] = col?.name ?? o.columnName } return seed } @@ -111,7 +123,7 @@ export function EnrichmentConfig({ const [deps, setDeps] = useState(() => existingGroup?.dependencies?.columns ?? []) const [showValidation, setShowValidation] = useState(false) - const columnOptions = allColumns.map((c) => ({ label: c.name, value: c.name })) + const columnOptions = allColumns.map((c) => ({ label: c.name, value: getColumnId(c) })) const missingRequired = enrichment.inputs.some((i) => i.required && !inputMappings[i.id]) const depsValid = !autoRun || deps.length > 0 @@ -162,10 +174,13 @@ export function EnrichmentConfig({ autoRun, }) for (const o of enrichment.outputs) { - const original = originalOutputName(o.id) + const col = outputColumn(o.id) const next = (outputNames[o.id] ?? '').trim() - if (original && next && next !== original) { - await updateColumn.mutateAsync({ columnName: original, updates: { name: next } }) + if (col && next && next !== col.name) { + await updateColumn.mutateAsync({ + columnName: getColumnId(col), + updates: { name: next }, + }) } } toast.success(`Updated "${enrichment.name}"`) From 299c13fc2d39474fbc4abda4e7de8563fce7f1f7 Mon Sep 17 00:00:00 2001 From: Theodore Li Date: Mon, 6 Jul 2026 18:42:17 -0700 Subject: [PATCH 2/2] feat(tables): scope group-header run menu to the active filter --- .../headers/workflow-group-meta-cell.tsx | 37 ++++++++++++++++--- .../components/table-grid/table-grid.tsx | 6 ++- 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table-grid/headers/workflow-group-meta-cell.tsx b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table-grid/headers/workflow-group-meta-cell.tsx index 0f760fff8e1..031a9c8a6f2 100644 --- a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table-grid/headers/workflow-group-meta-cell.tsx +++ b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table-grid/headers/workflow-group-meta-cell.tsx @@ -37,6 +37,18 @@ import type { DisplayColumn } from '../types' * surfaces stay in sync. */ const LIMITED_RUN_PRESETS = [10, 1000] as const +/** Labels for the table-scoped run items. With an active filter the run is + * scoped to matching rows, so the labels say "filtered rows" to make the + * narrowed target visible. Shared by both menu surfaces. */ +function runMenuLabels(hasActiveFilter: boolean) { + const rows = hasActiveFilter ? 'filtered rows' : 'rows' + return { + all: `Run all ${rows}`, + incomplete: `Run empty ${rows}`, + limited: (max: number) => `Run ${max.toLocaleString()} empty ${rows}`, + } +} + interface ColumnOptionsMenuProps { open: boolean onOpenChange: (open: boolean) => void @@ -65,6 +77,9 @@ interface ColumnOptionsMenuProps { /** When set, surfaces a "Run N selected rows" item above Run all. */ onRunColumnSelected?: () => void selectedRowCount?: number + /** Table-scoped run items honor the active filter; when true the labels say + * "filtered rows" so the narrowed scope is visible. */ + hasActiveFilter?: boolean /** When set, the menu surfaces a "View workflow" item that opens a popup * preview of the configured workflow. */ onViewWorkflow?: () => void @@ -97,12 +112,14 @@ export function ColumnOptionsMenu({ onRunColumnLimited, onRunColumnSelected, selectedRowCount = 0, + hasActiveFilter = false, onViewWorkflow, isPinned, onPinToggle, }: ColumnOptionsMenuProps) { const showRunActions = Boolean(onRunColumnAll && onRunColumnIncomplete) const showRunSelected = Boolean(onRunColumnSelected) && selectedRowCount > 0 + const runLabels = runMenuLabels(hasActiveFilter) return ( @@ -140,15 +157,15 @@ export function ColumnOptionsMenu({ )} onRunColumnAll?.()}> - Run all rows + {runLabels.all} onRunColumnIncomplete?.()}> - Run empty rows + {runLabels.incomplete} {onRunColumnLimited && LIMITED_RUN_PRESETS.map((max) => ( onRunColumnLimited(max)}> - {`Run ${max.toLocaleString()} empty rows`} + {runLabels.limited(max)} ))} @@ -221,6 +238,9 @@ interface WorkflowGroupMetaCellProps { /** Row ids in the user's current multi-row selection; when non-empty the * run menu adds a "Run N selected rows" option. */ selectedRowIds?: string[] | null + /** True when the grid has an active filter — table-scoped run items apply + * only to matching rows and are labeled "filtered rows". */ + hasActiveFilter?: boolean /** Opens a popup preview of the underlying workflow. */ onViewWorkflow?: (workflowId: string) => void /** When set, the meta cell becomes draggable and forwards events through @@ -267,6 +287,7 @@ export function WorkflowGroupMetaCell({ onDeleteColumn, onDeleteGroup, selectedRowIds, + hasActiveFilter = false, onViewWorkflow, onDragStart, onDragOver, @@ -292,6 +313,7 @@ export function WorkflowGroupMetaCell({ const didDragRef = useRef(false) const selectedCount = selectedRowIds?.length ?? 0 + const runLabels = runMenuLabels(hasActiveFilter) function handleRunAll() { if (groupId) onRunColumn?.(groupId, 'all') @@ -443,11 +465,13 @@ export function WorkflowGroupMetaCell({ {`Run ${selectedCount} selected ${selectedCount === 1 ? 'row' : 'rows'}`} )} - Run all rows - Run empty rows + {runLabels.all} + + {runLabels.incomplete} + {LIMITED_RUN_PRESETS.map((max) => ( handleRunLimited(max)}> - {`Run ${max.toLocaleString()} empty rows`} + {runLabels.limited(max)} ))} @@ -470,6 +494,7 @@ export function WorkflowGroupMetaCell({ onRunColumnLimited={onRunColumn ? handleRunLimited : undefined} onRunColumnSelected={onRunColumn && selectedCount > 0 ? handleRunSelected : undefined} selectedRowCount={selectedCount} + hasActiveFilter={hasActiveFilter} onViewWorkflow={onViewWorkflow ? () => onViewWorkflow(workflowId) : undefined} isPinned={isPinned} onPinToggle={onPinToggle} diff --git a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table-grid/table-grid.tsx b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table-grid/table-grid.tsx index 7521df83ecb..125418b5f45 100644 --- a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table-grid/table-grid.tsx +++ b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table-grid/table-grid.tsx @@ -503,7 +503,10 @@ export function TableGrid({ rowIds?: string[], limit?: RunLimit ) { - onRunColumn(groupId, runMode, rowIds, limit) + // Table-scoped runs (Run all / Run empty / Run N empty) honor the active + // filter; an explicit rowIds scope (Run selected) already names its rows. + const filter = rowIds ? undefined : (queryOptions.filter ?? undefined) + onRunColumn(groupId, runMode, rowIds, limit, filter) } const handleViewWorkflow = useCallback( @@ -3643,6 +3646,7 @@ export function TableGrid({ onSelectGroup={handleGroupSelect} onOpenConfig={() => handleConfigureWorkflowGroup(g.groupId)} onRunColumn={userPermissions.canEdit ? handleRunColumn : undefined} + hasActiveFilter={Boolean(queryOptions.filter)} selectedRowIds={selectedRowIds} onInsertLeft={ userPermissions.canEdit ? handleInsertColumnLeft : undefined