@@ -14,6 +14,7 @@ import {
1414 AlertDialogHeader ,
1515 AlertDialogTitle ,
1616} from '@/components/ui/alert-dialog'
17+ import { Input } from '@/components/ui/input'
1718import { Tooltip , TooltipContent , TooltipTrigger } from '@/components/ui/tooltip'
1819import { createLogger } from '@/lib/logs/console-logger'
1920import { type FolderTreeNode , useFolderStore } from '@/stores/folders/store'
@@ -48,14 +49,33 @@ export function FolderItem({
4849 const [ showDeleteDialog , setShowDeleteDialog ] = useState ( false )
4950 const [ isDeleting , setIsDeleting ] = useState ( false )
5051 const [ isDragging , setIsDragging ] = useState ( false )
52+ const [ isEditing , setIsEditing ] = useState ( false )
53+ const [ editValue , setEditValue ] = useState ( folder . name )
54+ const [ isRenaming , setIsRenaming ] = useState ( false )
5155 const dragStartedRef = useRef ( false )
56+ const inputRef = useRef < HTMLInputElement > ( null )
5257 const params = useParams ( )
5358 const workspaceId = params . workspaceId as string
5459 const isExpanded = expandedFolders . has ( folder . id )
5560 const updateTimeoutRef = useRef < ReturnType < typeof setTimeout > | undefined > ( undefined )
5661 const pendingStateRef = useRef < boolean | null > ( null )
5762
63+ // Update editValue when folder name changes
64+ useEffect ( ( ) => {
65+ setEditValue ( folder . name )
66+ } , [ folder . name ] )
67+
68+ // Focus input when entering edit mode
69+ useEffect ( ( ) => {
70+ if ( isEditing && inputRef . current ) {
71+ inputRef . current . focus ( )
72+ inputRef . current . select ( )
73+ }
74+ } , [ isEditing ] )
75+
5876 const handleToggleExpanded = useCallback ( ( ) => {
77+ if ( isEditing ) return // Don't toggle when editing
78+
5979 const newExpandedState = ! isExpanded
6080 toggleExpanded ( folder . id )
6181 pendingStateRef . current = newExpandedState
@@ -73,9 +93,11 @@ export function FolderItem({
7393 } )
7494 }
7595 } , 300 )
76- } , [ folder . id , isExpanded , toggleExpanded , updateFolderAPI ] )
96+ } , [ folder . id , isExpanded , toggleExpanded , updateFolderAPI , isEditing ] )
7797
7898 const handleDragStart = ( e : React . DragEvent ) => {
99+ if ( isEditing ) return
100+
79101 dragStartedRef . current = true
80102 setIsDragging ( true )
81103
@@ -101,7 +123,7 @@ export function FolderItem({
101123 }
102124
103125 const handleClick = ( e : React . MouseEvent ) => {
104- if ( dragStartedRef . current ) {
126+ if ( dragStartedRef . current || isEditing ) {
105127 e . preventDefault ( )
106128 return
107129 }
@@ -116,15 +138,57 @@ export function FolderItem({
116138 }
117139 } , [ ] )
118140
119- const handleRename = async ( folderId : string , newName : string ) => {
141+ const handleStartEdit = ( ) => {
142+ setIsEditing ( true )
143+ setEditValue ( folder . name )
144+ }
145+
146+ const handleSaveEdit = async ( ) => {
147+ if ( ! editValue . trim ( ) || editValue . trim ( ) === folder . name ) {
148+ setIsEditing ( false )
149+ setEditValue ( folder . name )
150+ return
151+ }
152+
153+ setIsRenaming ( true )
120154 try {
121- await updateFolderAPI ( folderId , { name : newName } )
155+ await updateFolderAPI ( folder . id , { name : editValue . trim ( ) } )
156+ logger . info ( `Successfully renamed folder from "${ folder . name } " to "${ editValue . trim ( ) } "` )
157+ setIsEditing ( false )
122158 } catch ( error ) {
123- logger . error ( 'Failed to rename folder:' , { error } )
159+ logger . error ( 'Failed to rename folder:' , {
160+ error,
161+ folderId : folder . id ,
162+ oldName : folder . name ,
163+ newName : editValue . trim ( ) ,
164+ } )
165+ // Reset to original name on error
166+ setEditValue ( folder . name )
167+ } finally {
168+ setIsRenaming ( false )
169+ }
170+ }
171+
172+ const handleCancelEdit = ( ) => {
173+ setIsEditing ( false )
174+ setEditValue ( folder . name )
175+ }
176+
177+ const handleKeyDown = ( e : React . KeyboardEvent ) => {
178+ if ( e . key === 'Enter' ) {
179+ e . preventDefault ( )
180+ handleSaveEdit ( )
181+ } else if ( e . key === 'Escape' ) {
182+ e . preventDefault ( )
183+ handleCancelEdit ( )
124184 }
125185 }
126186
127- const handleDelete = async ( folderId : string ) => {
187+ const handleInputBlur = ( ) => {
188+ handleSaveEdit ( )
189+ }
190+
191+ const handleDelete = async ( ) => {
128192 setShowDeleteDialog ( true )
129193 }
130194
@@ -154,7 +218,7 @@ export function FolderItem({
154218 onDragLeave = { onDragLeave }
155219 onDrop = { onDrop }
156220 onClick = { handleClick }
157- draggable = { true }
221+ draggable = { ! isEditing }
158222 onDragStart = { handleDragStart }
159223 onDragEnd = { handleDragEnd }
160224 >
@@ -222,7 +286,7 @@ export function FolderItem({
222286 maxWidth : isFirstItem ? `${ 164 - level * 20 } px` : `${ 206 - level * 20 } px` ,
223287 } }
224288 onClick = { handleClick }
225- draggable = { true }
289+ draggable = { ! isEditing }
226290 onDragStart = { handleDragStart }
227291 onDragEnd = { handleDragEnd }
228292 >
@@ -234,18 +298,34 @@ export function FolderItem({
234298 ) }
235299 </ div >
236300
237- < span className = 'flex-1 select-none truncate text-muted-foreground' > { folder . name } </ span >
238-
239- < div className = 'flex items-center justify-center' onClick = { ( e ) => e . stopPropagation ( ) } >
240- < FolderContextMenu
241- folderId = { folder . id }
242- folderName = { folder . name }
243- onCreateWorkflow = { onCreateWorkflow }
244- onRename = { handleRename }
245- onDelete = { handleDelete }
246- level = { level }
301+ { isEditing ? (
302+ < Input
303+ ref = { inputRef }
304+ value = { editValue }
305+ onChange = { ( e ) => setEditValue ( e . target . value ) }
306+ onKeyDown = { handleKeyDown }
307+ onBlur = { handleInputBlur }
308+ className = 'h-6 flex-1 border-0 bg-transparent p-0 text-muted-foreground text-sm outline-none focus:outline-none focus:ring-0 focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-offset-0'
309+ maxLength = { 50 }
310+ disabled = { isRenaming }
311+ onClick = { ( e ) => e . stopPropagation ( ) } // Prevent folder toggle when clicking input
247312 />
248- </ div >
313+ ) : (
314+ < span className = 'flex-1 select-none truncate text-muted-foreground' > { folder . name } </ span >
315+ ) }
316+
317+ { ! isEditing && (
318+ < div className = 'flex items-center justify-center' onClick = { ( e ) => e . stopPropagation ( ) } >
319+ < FolderContextMenu
320+ folderId = { folder . id }
321+ folderName = { folder . name }
322+ onCreateWorkflow = { onCreateWorkflow }
323+ onDelete = { handleDelete }
324+ onStartEdit = { handleStartEdit }
325+ level = { level }
326+ />
327+ </ div >
328+ ) }
249329 </ div >
250330 </ div >
251331
0 commit comments