From 4d3f288784beab96f07e4c3b06efc6767b3850bb Mon Sep 17 00:00:00 2001 From: Gustavo Henrique Santos Souza de Miranda Date: Wed, 21 Jan 2026 04:21:57 -0300 Subject: [PATCH] feat: [US-015] - Rename project Add rename functionality to project cards on the dashboard: - Edit/rename icon button on each project card (visible on hover) - Modal dialog with project name input field - Supabase update for project name - Real-time UI update without page reload - Success toast notification after rename - Enter key support for quick rename - Error handling and display Co-Authored-By: Claude Opus 4.5 --- src/components/ProjectCard.tsx | 189 +++++++++++++++++++++++++++++---- src/components/ProjectList.tsx | 8 ++ 2 files changed, 179 insertions(+), 18 deletions(-) diff --git a/src/components/ProjectCard.tsx b/src/components/ProjectCard.tsx index 7d19ac8..cd47ec0 100644 --- a/src/components/ProjectCard.tsx +++ b/src/components/ProjectCard.tsx @@ -9,6 +9,7 @@ interface ProjectCardProps { name: string updatedAt: string onDelete: (id: string) => void + onRename: (id: string, newName: string) => void } function formatDate(dateString: string): string { @@ -27,9 +28,14 @@ export default function ProjectCard({ name, updatedAt, onDelete, + onRename, }: ProjectCardProps) { const [showDeleteDialog, setShowDeleteDialog] = useState(false) const [isDeleting, setIsDeleting] = useState(false) + const [showRenameDialog, setShowRenameDialog] = useState(false) + const [isRenaming, setIsRenaming] = useState(false) + const [newName, setNewName] = useState(name) + const [renameError, setRenameError] = useState(null) const router = useRouter() const handleCardClick = () => { @@ -65,31 +71,97 @@ export default function ProjectCard({ } } + const handleRenameClick = (e: React.MouseEvent) => { + e.stopPropagation() + setNewName(name) + setRenameError(null) + setShowRenameDialog(true) + } + + const handleConfirmRename = async () => { + if (!newName.trim()) { + setRenameError('Project name cannot be empty') + return + } + + if (newName.trim() === name) { + setShowRenameDialog(false) + return + } + + setIsRenaming(true) + setRenameError(null) + + const supabase = createClient() + const { error } = await supabase + .from('projects') + .update({ name: newName.trim() }) + .eq('id', id) + + if (error) { + setIsRenaming(false) + setRenameError('Failed to rename project: ' + error.message) + return + } + + setIsRenaming(false) + setShowRenameDialog(false) + onRename(id, newName.trim()) + } + + const handleCancelRename = () => { + if (!isRenaming) { + setShowRenameDialog(false) + setRenameError(null) + } + } + return ( <>
- + + + + + +

{name}

@@ -155,6 +227,87 @@ export default function ProjectCard({ )} + + {showRenameDialog && ( +
+ + )} ) } diff --git a/src/components/ProjectList.tsx b/src/components/ProjectList.tsx index 1f0e234..d8cc8fe 100644 --- a/src/components/ProjectList.tsx +++ b/src/components/ProjectList.tsx @@ -26,6 +26,13 @@ export default function ProjectList({ initialProjects }: ProjectListProps) { setToast({ message: 'Project deleted successfully', type: 'success' }) }, []) + const handleRename = useCallback((id: string, newName: string) => { + setProjects((prev) => + prev.map((p) => (p.id === id ? { ...p, name: newName } : p)) + ) + setToast({ message: 'Project renamed successfully', type: 'success' }) + }, []) + const handleCloseToast = useCallback(() => { setToast(null) }, []) @@ -67,6 +74,7 @@ export default function ProjectList({ initialProjects }: ProjectListProps) { name={project.name} updatedAt={project.updated_at} onDelete={handleDelete} + onRename={handleRename} /> ))}