Skip to content

Commit db9fcca

Browse files
Change wording for deleting workflow column
1 parent 7c2d896 commit db9fcca

3 files changed

Lines changed: 133 additions & 9 deletions

File tree

apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table/table.tsx

Lines changed: 99 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import {
2929
ArrowRight,
3030
Calendar as CalendarIcon,
3131
ChevronDown,
32+
EyeOff,
3233
Pencil,
3334
PlayOutline,
3435
Plus,
@@ -79,6 +80,7 @@ import {
7980
import { useWorkflowStates, useWorkflows } from '@/hooks/queries/workflows'
8081
import { useInlineRename } from '@/hooks/use-inline-rename'
8182
import { extractCreatedRowId, useTableUndo } from '@/hooks/use-table-undo'
83+
import { useLogDetailsUIStore } from '@/stores/logs/store'
8284
import type { DeletedRowSnapshot } from '@/stores/table/types'
8385
import type { WorkflowMetadata } from '@/stores/workflows/registry/types'
8486
import { useContextMenu, useRowExecution, useTableData } from '../../hooks'
@@ -264,6 +266,8 @@ const COL_WIDTH = 160
264266
const COL_WIDTH_MIN = 80
265267
const CHECKBOX_COL_WIDTH = 56
266268
const ADD_COL_WIDTH = 120
269+
/** Width of the column-config slideout (matches `column-sidebar.tsx`'s `w-[400px]`). */
270+
const COLUMN_SIDEBAR_WIDTH = 400
267271
const SKELETON_COL_COUNT = 4
268272
const SKELETON_ROW_COUNT = 10
269273
const ROW_HEIGHT_ESTIMATE = 35
@@ -2226,6 +2230,18 @@ export function Table({
22262230
const [configState, setConfigState] = useState<ColumnConfigState>(null)
22272231
/** Execution id whose run details are open in the slideout. */
22282232
const [executionDetailsId, setExecutionDetailsId] = useState<string | null>(null)
2233+
/**
2234+
* Right padding added to the table's scroll content while a slideout panel
2235+
* is open, equal to the panel's width. Without it, the rightmost columns are
2236+
* clipped under the panel and there's no way to scroll them into view.
2237+
* The two panels are mutually exclusive (each opener closes the other).
2238+
*/
2239+
const logPanelWidth = useLogDetailsUIStore((state) => state.panelWidth)
2240+
const sidebarReservedPx = configState
2241+
? COLUMN_SIDEBAR_WIDTH
2242+
: executionDetailsId
2243+
? logPanelWidth
2244+
: 0
22292245

22302246
const handleConfigureColumn = useCallback((columnName: string) => {
22312247
setExecutionDetailsId(null)
@@ -2239,7 +2255,13 @@ export function Table({
22392255
[deleteWorkflowGroupMutation]
22402256
)
22412257

2242-
const handleDeleteColumn = useCallback((columnName: string) => {
2258+
/**
2259+
* Computes the names slated for deletion given a click on `columnName` and
2260+
* the current column selection. If the click landed inside a multi-column
2261+
* selection, the entire selection is the target; otherwise it's just the
2262+
* clicked column.
2263+
*/
2264+
const resolveDeletionNames = useCallback((columnName: string): string[] => {
22432265
const cols = columnsRef.current
22442266
if (isColumnSelectionRef.current && selectionAnchorRef.current) {
22452267
const sel = computeNormalizedSelection(selectionAnchorRef.current, selectionFocusRef.current)
@@ -2250,16 +2272,63 @@ export function Table({
22502272
for (let c = sel.startCol; c <= sel.endCol; c++) {
22512273
if (c < cols.length) names.push(cols[c].name)
22522274
}
2253-
if (names.length > 0) {
2254-
setDeletingColumns(names)
2255-
return
2256-
}
2275+
if (names.length > 0) return names
22572276
}
22582277
}
22592278
}
2260-
setDeletingColumns([columnName])
2279+
return [columnName]
22612280
}, [])
22622281

2282+
/**
2283+
* Hide a workflow-output column by removing it from its group's `outputs`
2284+
* via `updateWorkflowGroup`. Server-side this drops the schema column AND
2285+
* wipes the cell data on every row. The user can re-add the output from
2286+
* the sidebar's picker; the existing backfill repopulates from execution
2287+
* logs. Only valid when removing the columns leaves every affected group
2288+
* with at least one surviving output — caller must check first.
2289+
*/
2290+
const hideWorkflowOutputColumns = useCallback(
2291+
(names: string[]) => {
2292+
const schemaCols = schemaColumnsRef.current
2293+
const groups = workflowGroupsRef.current
2294+
const removalsByGroup = new Map<string, Set<string>>()
2295+
for (const name of names) {
2296+
const def = schemaCols.find((c) => c.name === name)
2297+
if (!def?.workflowGroupId) return false
2298+
const set = removalsByGroup.get(def.workflowGroupId) ?? new Set<string>()
2299+
set.add(name)
2300+
removalsByGroup.set(def.workflowGroupId, set)
2301+
}
2302+
for (const [groupId, removed] of removalsByGroup) {
2303+
const group = groups.find((g) => g.id === groupId)
2304+
if (!group) return false
2305+
const remaining = group.outputs.filter((o) => !removed.has(o.columnName))
2306+
if (remaining.length === 0) return false
2307+
updateWorkflowGroupMutation.mutate({
2308+
groupId: group.id,
2309+
workflowId: group.workflowId,
2310+
name: group.name,
2311+
dependencies: group.dependencies,
2312+
outputs: remaining,
2313+
})
2314+
}
2315+
return true
2316+
},
2317+
[updateWorkflowGroupMutation]
2318+
)
2319+
2320+
const handleDeleteColumn = useCallback(
2321+
(columnName: string) => {
2322+
const names = resolveDeletionNames(columnName)
2323+
// If every target is a workflow output AND removing them all leaves each
2324+
// group with ≥1 output, hide them directly — no destructive-confirm
2325+
// modal, since the workflow can re-produce the value any time.
2326+
if (hideWorkflowOutputColumns(names)) return
2327+
setDeletingColumns(names)
2328+
},
2329+
[resolveDeletionNames, hideWorkflowOutputColumns]
2330+
)
2331+
22632332
const handleDeleteColumnConfirm = useCallback(() => {
22642333
if (!deletingColumns || deletingColumns.length === 0) return
22652334
const columnsToDelete = [...deletingColumns]
@@ -2658,7 +2727,13 @@ export function Table({
26582727
onDragOver={handleScrollDragOver}
26592728
onDrop={handleScrollDrop}
26602729
>
2661-
<div className='relative h-fit' style={{ width: `${tableWidth}px` }}>
2730+
<div
2731+
className='relative h-fit'
2732+
style={{
2733+
width: `${tableWidth + sidebarReservedPx}px`,
2734+
paddingRight: sidebarReservedPx,
2735+
}}
2736+
>
26622737
<table
26632738
className='table-fixed border-separate border-spacing-0 text-small'
26642739
style={{ width: `${tableWidth}px` }}
@@ -3880,6 +3955,7 @@ function ColumnOptionsMenu({
38803955
onOpenChange,
38813956
position,
38823957
column,
3958+
deleteLabel,
38833959
onOpenConfig,
38843960
onInsertLeft,
38853961
onInsertRight,
@@ -3892,6 +3968,11 @@ function ColumnOptionsMenu({
38923968
onOpenChange: (open: boolean) => void
38933969
position: { x: number; y: number }
38943970
column: DisplayColumn
3971+
/** Override for the destructive item's label. Defaults to "Delete column"
3972+
* (or "Delete workflow" when `onDeleteGroup` is set). Use "Hide column"
3973+
* when the destructive action is non-lossy (workflow-output column where
3974+
* removing it leaves the group with siblings). */
3975+
deleteLabel?: string
38953976
onOpenConfig: (columnName: string) => void
38963977
onInsertLeft: (columnName: string) => void
38973978
onInsertRight: (columnName: string) => void
@@ -3963,8 +4044,8 @@ function ColumnOptionsMenu({
39634044
<DropdownMenuItem
39644045
onSelect={() => (onDeleteGroup ? onDeleteGroup() : onDeleteColumn(column.name))}
39654046
>
3966-
<Trash />
3967-
{onDeleteGroup ? 'Delete workflow' : 'Delete column'}
4047+
{deleteLabel === 'Hide column' ? <EyeOff /> : <Trash />}
4048+
{deleteLabel ?? (onDeleteGroup ? 'Delete workflow' : 'Delete column')}
39684049
</DropdownMenuItem>
39694050
</DropdownMenuContent>
39704051
</DropdownMenu>
@@ -4190,6 +4271,14 @@ const ColumnHeaderMenu = React.memo(function ColumnHeaderMenu({
41904271
const configuredWorkflow = ownGroup
41914272
? workflows?.find((w) => w.id === ownGroup.workflowId)
41924273
: undefined
4274+
// Workflow-output column with siblings → "Hide column" (non-destructive,
4275+
// re-addable from sidebar). Last output of a group → "Delete workflow"
4276+
// (removes the entire group). Plain column → undefined (default "Delete column").
4277+
const deleteLabel = ownGroup
4278+
? ownGroup.outputs.length > 1
4279+
? 'Hide column'
4280+
: 'Delete workflow'
4281+
: undefined
41934282
const workflowColor = configuredWorkflow?.color
41944283
const blockIconInfo = sourceInfo?.blockIconInfo
41954284
const blockName = sourceInfo?.blockName
@@ -4416,6 +4505,7 @@ const ColumnHeaderMenu = React.memo(function ColumnHeaderMenu({
44164505
onOpenChange={setMenuOpen}
44174506
position={menuPosition}
44184507
column={column}
4508+
deleteLabel={deleteLabel}
44194509
onOpenConfig={onOpenConfig}
44204510
onInsertLeft={onInsertLeft}
44214511
onInsertRight={onInsertRight}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import type { SVGProps } from 'react'
2+
3+
/**
4+
* Eye-off icon. Companion to {@link Eye}; used for "hide" affordances where
5+
* something is being concealed without being destroyed (e.g. hide a workflow
6+
* output column from the table while keeping it re-addable from the sidebar).
7+
*
8+
* Uses currentColor so it inherits the surrounding text color.
9+
*
10+
* @param props - SVG properties including className, fill, etc.
11+
*/
12+
export function EyeOff(props: SVGProps<SVGSVGElement>) {
13+
return (
14+
<svg
15+
width='14'
16+
height='14'
17+
viewBox='0 0 24 24'
18+
fill='none'
19+
stroke='currentColor'
20+
strokeWidth='2'
21+
strokeLinecap='round'
22+
strokeLinejoin='round'
23+
xmlns='http://www.w3.org/2000/svg'
24+
aria-hidden='true'
25+
{...props}
26+
>
27+
<path d='M9.88 9.88a3 3 0 1 0 4.24 4.24' />
28+
<path d='M10.73 5.08A10.43 10.43 0 0 1 12 5c7 0 10 7 10 7a13.16 13.16 0 0 1-1.67 2.68' />
29+
<path d='M6.61 6.61A13.526 13.526 0 0 0 2 12s3 7 10 7a9.74 9.74 0 0 0 5.39-1.61' />
30+
<line x1='2' y1='2' x2='22' y2='22' />
31+
</svg>
32+
)
33+
}

apps/sim/components/emcn/icons/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export { Download } from './download'
2828
export { Duplicate } from './duplicate'
2929
export { Expand } from './expand'
3030
export { Eye } from './eye'
31+
export { EyeOff } from './eye-off'
3132
export { File } from './file'
3233
export { FileX } from './file-x'
3334
export { Fingerprint } from './fingerprint'

0 commit comments

Comments
 (0)