Skip to content

Commit 1f8aab6

Browse files
committed
fix(rename): make inline rename await mutateAsync so isSaving disables the field; recover edits on failure
- useInlineRename: catch onSave rejection — log, restore the original name, keep the edit session open, and re-arm doneRef (mirrors useItemRename) - switch rename call sites (files, tables, table header, knowledge base, document) from mutate() to mutateAsync() so isSaving spans the request - table-grid column rename stays fire-and-forget by design (optimistic local update + undo entry)
1 parent a28f8c1 commit 1f8aab6

7 files changed

Lines changed: 46 additions & 39 deletions

File tree

apps/sim/app/workspace/[workspaceId]/files/files.tsx

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -274,23 +274,19 @@ export function Files() {
274274
onSave: (rowId, name) => {
275275
const parsed = parseRowId(rowId)
276276
if (parsed.kind === 'folder') {
277-
updateFolder.mutate({ workspaceId, folderId: parsed.id, updates: { name } })
278-
return
277+
return updateFolder.mutateAsync({ workspaceId, folderId: parsed.id, updates: { name } })
279278
}
280-
renameFile.mutate({ workspaceId, fileId: parsed.id, name })
279+
return renameFile.mutateAsync({ workspaceId, fileId: parsed.id, name })
281280
},
282281
})
283282

284283
const headerRename = useInlineRename({
285-
onSave: (fileId, name) => {
286-
renameFile.mutate({ workspaceId, fileId, name })
287-
},
284+
onSave: (fileId, name) => renameFile.mutateAsync({ workspaceId, fileId, name }),
288285
})
289286

290287
const breadcrumbRename = useInlineRename({
291-
onSave: (folderId, name) => {
292-
updateFolder.mutate({ workspaceId, folderId, updates: { name } })
293-
},
288+
onSave: (folderId, name) =>
289+
updateFolder.mutateAsync({ workspaceId, folderId, updates: { name } }),
294290
})
295291

296292
const selectedFile = useMemo(

apps/sim/app/workspace/[workspaceId]/knowledge/[id]/[documentId]/document.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ export function Document({
254254
const { mutate: updateChunkMutation } = useUpdateChunk()
255255
const { mutate: deleteDocumentMutation, isPending: isDeletingDocument } = useDeleteDocument()
256256
const { mutate: bulkChunkMutation, isPending: isBulkOperating } = useBulkChunkOperation()
257-
const { mutate: updateDocumentMutation } = useUpdateDocument()
257+
const { mutateAsync: updateDocumentMutation } = useUpdateDocument()
258258

259259
const docRename = useInlineRename({
260260
onSave: (docId, filename) =>

apps/sim/app/workspace/[workspaceId]/knowledge/[id]/base.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ export function KnowledgeBase({
228228
const { mutate: deleteDocumentMutation } = useDeleteDocument()
229229
const { mutate: deleteKnowledgeBaseMutation, isPending: isDeleting } =
230230
useDeleteKnowledgeBase(workspaceId)
231-
const { mutate: updateKnowledgeBaseMutation } = useUpdateKnowledgeBase(workspaceId)
231+
const { mutateAsync: updateKnowledgeBaseMutation } = useUpdateKnowledgeBase(workspaceId)
232232

233233
const kbRename = useInlineRename({
234234
onSave: (kbId, name) =>

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@ export function Table({
343343
onSave: (_id, name) => {
344344
const data = tableDataRef.current
345345
if (data) pushTableRenameUndoSinkRef.current?.(data.name, name)
346-
renameTableMutation.mutate({ tableId, name })
346+
return renameTableMutation.mutateAsync({ tableId, name })
347347
},
348348
})
349349

apps/sim/app/workspace/[workspaceId]/tables/tables.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ export function Tables() {
8080
const importCsvAsync = useImportCsvAsync()
8181

8282
const tableRename = useInlineRename({
83-
onSave: (tableId, name) => renameTable.mutate({ tableId, name }),
83+
onSave: (tableId, name) => renameTable.mutateAsync({ tableId, name }),
8484
})
8585

8686
const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false)

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/components/general/components/version-description-modal.tsx

Lines changed: 13 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,13 @@
22

33
import { useRef, useState } from 'react'
44
import {
5-
Chip,
65
ChipConfirmModal,
76
ChipModal,
87
ChipModalBody,
98
ChipModalError,
109
ChipModalField,
1110
ChipModalFooter,
1211
ChipModalHeader,
13-
ChipTextarea,
1412
} from '@/components/emcn'
1513
import {
1614
useGenerateVersionDescription,
@@ -98,41 +96,33 @@ export function VersionDescriptionModal({
9896
<ChipModalHeader onClose={() => handleCloseAttempt()}>Version Description</ChipModalHeader>
9997
<ChipModalBody>
10098
<ChipModalField
101-
type='custom'
99+
type='textarea'
102100
title={
103101
<span>
104102
{currentDescription ? 'Edit the' : 'Add a'} description for{' '}
105103
<span className='font-medium text-[var(--text-primary)]'>{versionName}</span>
106104
</span>
107105
}
106+
value={description}
107+
onChange={setDescription}
108+
placeholder='Describe the changes in this deployment version...'
109+
maxLength={2000}
110+
minHeight={120}
111+
disabled={isGenerating}
108112
hint={`${description.length}/2000`}
109-
>
110-
<div className='flex justify-end'>
111-
<Chip
112-
flush
113-
onClick={handleGenerateDescription}
114-
disabled={isGenerating || updateMutation.isPending}
115-
>
116-
{isGenerating ? 'Generating...' : 'Generate'}
117-
</Chip>
118-
</div>
119-
<ChipTextarea
120-
value={description}
121-
onChange={(event) => setDescription(event.target.value)}
122-
placeholder='Describe the changes in this deployment version...'
123-
maxLength={2000}
124-
disabled={isGenerating}
125-
className='min-h-[120px]'
126-
aria-label='Version description'
127-
/>
128-
</ChipModalField>
113+
/>
129114
<ChipModalError>
130115
{updateMutation.error?.message || generateMutation.error?.message}
131116
</ChipModalError>
132117
</ChipModalBody>
133118
<ChipModalFooter
134119
onCancel={handleCloseAttempt}
135120
cancelDisabled={updateMutation.isPending || isGenerating}
121+
secondaryAction={{
122+
label: isGenerating ? 'Generating...' : 'Generate',
123+
onClick: handleGenerateDescription,
124+
disabled: isGenerating || updateMutation.isPending,
125+
}}
136126
primaryAction={{
137127
label: updateMutation.isPending ? 'Saving...' : 'Save',
138128
onClick: handleSave,

apps/sim/hooks/use-inline-rename.ts

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
11
import { useCallback, useRef, useState } from 'react'
2+
import { createLogger } from '@sim/logger'
3+
4+
const logger = createLogger('useInlineRename')
25

36
interface UseInlineRenameProps {
4-
onSave: (id: string, newName: string) => void | Promise<void>
7+
/**
8+
* Persists the new name. Return the mutation promise (e.g. React Query's
9+
* `mutateAsync(...)`) — NOT a fire-and-forget `mutate(...)` — so `isSaving`
10+
* spans the in-flight request and a rejection can revive the edit session.
11+
*/
12+
onSave: (id: string, newName: string) => undefined | Promise<unknown>
513
}
614

715
/**
@@ -56,7 +64,6 @@ export function useInlineRename({ onSave }: UseInlineRenameProps) {
5664
setIsSaving(true)
5765
try {
5866
await onSaveRef.current(id, trimmed)
59-
} finally {
6067
/**
6168
* Only clear editing state if this submit still owns the edit session.
6269
* Without the guard, a slow save for row A would tear down a rename of
@@ -65,9 +72,23 @@ export function useInlineRename({ onSave }: UseInlineRenameProps) {
6572
* new session, and the new session's own submit handles its lifecycle.
6673
*/
6774
if (editingIdRef.current === id) {
68-
setIsSaving(false)
6975
setEditingId(null)
7076
}
77+
} catch (error) {
78+
logger.error('Failed to rename item', { error, id, newName: trimmed })
79+
/**
80+
* Mirror `useItemRename`'s failure path: stay in edit mode with the
81+
* original name restored (no silent data loss, no unhandled rejection)
82+
* and re-arm `doneRef` so the revived session can submit or cancel again.
83+
*/
84+
if (editingIdRef.current === id) {
85+
setEditValue(originalNameRef.current)
86+
doneRef.current = false
87+
}
88+
} finally {
89+
if (editingIdRef.current === id) {
90+
setIsSaving(false)
91+
}
7192
}
7293
}, [])
7394

0 commit comments

Comments
 (0)