@@ -11,6 +11,7 @@ import { assessTextPaste, formatPasteLimit, PASTE_LIMITS } from '@sim/utils/past
1111import { useVirtualizer } from '@tanstack/react-virtual'
1212import { useParams , useRouter } from 'next/navigation'
1313import { usePostHog } from 'posthog-js/react'
14+ import { extractValidationIssues , isValidationError } from '@/lib/api/client/errors'
1415import type { RunLimit , RunMode , TableFindMatch } from '@/lib/api/contracts/tables'
1516import { attachSelectionContextToClipboard } from '@/lib/copilot/chat/selection-clipboard'
1617import { captureEvent } from '@/lib/posthog/client'
@@ -78,6 +79,7 @@ import {
7879 chipRowCount ,
7980 classifyExecStatusMix ,
8081 collectRowSnapshots ,
82+ columnNameIssue ,
8183 computeNormalizedSelection ,
8284 drainTargetForChip ,
8385 type ExecStatusMix ,
@@ -1504,16 +1506,60 @@ export function TableGrid({
15041506 const handleFindCloseRef = useRef ( handleFindClose )
15051507 handleFindCloseRef . current = handleFindClose
15061508
1509+ const [ renameError , setRenameError ] = useState ( false )
1510+
15071511 const columnRename = useInlineRename ( {
15081512 // `columnName` is the column id; record the prior display name + id so undo
15091513 // restores the label (not the id) and targets the right column.
15101514 onSave : ( columnName , newName ) => {
15111515 const oldName = columnsRef . current . find ( ( c ) => c . key === columnName ) ?. name ?? columnName
15121516 pushUndoRef . current ( { type : 'rename-column' , oldName, newName, columnId : columnName } )
15131517 handleColumnRename ( columnName , newName )
1514- return updateColumnMutation . mutateAsync ( { columnName, updates : { name : newName } } )
1518+ return updateColumnMutation
1519+ . mutateAsync ( { columnName, updates : { name : newName } } )
1520+ . catch ( ( error : unknown ) => {
1521+ if ( isValidationError ( error ) ) {
1522+ toast . error ( extractValidationIssues ( error ) [ 0 ] ?. message ?? getErrorMessage ( error ) )
1523+ }
1524+ setRenameError ( true )
1525+ throw error
1526+ } )
15151527 } ,
15161528 } )
1529+ const columnRenameRef = useRef ( columnRename )
1530+ columnRenameRef . current = columnRename
1531+
1532+ const handleRenameValueChange = useCallback ( ( value : string ) => {
1533+ setRenameError ( false )
1534+ columnRenameRef . current . setEditValue ( value )
1535+ } , [ ] )
1536+
1537+ /** Keeps invalid names in the header so the user can correct them in place. */
1538+ const handleRenameSubmit = useCallback ( ( ) => {
1539+ const { editingId, editValue, submitRename } = columnRenameRef . current
1540+ const trimmedName = editValue . trim ( )
1541+ const currentColumn = columnsRef . current . find ( ( column ) => column . key === editingId )
1542+ if ( trimmedName && currentColumn && trimmedName !== currentColumn . name ) {
1543+ const issue = columnNameIssue (
1544+ trimmedName ,
1545+ schemaColumnsRef . current
1546+ . filter ( ( column ) => getColumnId ( column ) !== editingId )
1547+ . map ( ( column ) => column . name )
1548+ )
1549+ if ( issue ) {
1550+ toast . error ( issue )
1551+ setRenameError ( true )
1552+ return
1553+ }
1554+ }
1555+ setRenameError ( false )
1556+ void submitRename ( )
1557+ } , [ ] )
1558+
1559+ const handleRenameCancel = useCallback ( ( ) => {
1560+ setRenameError ( false )
1561+ columnRenameRef . current . cancelRename ( )
1562+ } , [ ] )
15171563
15181564 const toggleBooleanCell = useCallback (
15191565 ( rowId : string , columnName : string , currentValue : unknown ) => {
@@ -3989,6 +4035,15 @@ export function TableGrid({
39894035 [ onOpenColumnConfig , onOpenWorkflowConfig , workflowGroupById ]
39904036 )
39914037
4038+ const handleRenameColumn = useCallback (
4039+ ( columnName : string ) => {
4040+ setRenameError ( false )
4041+ const column = columnsRef . current . find ( ( candidate ) => candidate . key === columnName )
4042+ columnRename . startRename ( columnName , column ?. name ?? columnName )
4043+ } ,
4044+ [ columnRename . startRename ]
4045+ )
4046+
39924047 const handleConfigureWorkflowGroup = useCallback (
39934048 ( groupId : string ) => {
39944049 const group = workflowGroupById . get ( groupId )
@@ -4879,9 +4934,10 @@ export function TableGrid({
48794934 renameValue = {
48804935 columnRename . editingId === column . key ? columnRename . editValue : ''
48814936 }
4882- onRenameValueChange = { columnRename . setEditValue }
4883- onRenameSubmit = { columnRename . submitRename }
4884- onRenameCancel = { columnRename . cancelRename }
4937+ renameError = { renameError && columnRename . editingId === column . key }
4938+ onRenameValueChange = { handleRenameValueChange }
4939+ onRenameSubmit = { handleRenameSubmit }
4940+ onRenameCancel = { handleRenameCancel }
48854941 onColumnSelect = { handleColumnSelect }
48864942 // Required props here, and the menu is already
48874943 // suppressed for non-editors by `readOnly`.
@@ -4906,6 +4962,9 @@ export function TableGrid({
49064962 workflowGroups = { tableWorkflowGroups }
49074963 sourceInfo = { columnSourceInfo . get ( column . key ) }
49084964 onOpenConfig = { handleConfigureColumn }
4965+ onRenameColumn = {
4966+ userPermissions . canEdit ? handleRenameColumn : undefined
4967+ }
49094968 onGoToReferenceTable = {
49104969 referenceColumnsEnabled ? handleGoToReferenceTable : undefined
49114970 }
0 commit comments