import { useState } from "react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Alert, AlertDescription } from "@/components/ui/alert"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { FolderPicker } from "@/components/ui/folder-picker"; import { Project, CreateProject, UpdateProject } from "shared/types"; import { AlertCircle, Folder } from "lucide-react"; import { makeAuthenticatedRequest } from "@/lib/auth"; interface ProjectFormProps { open: boolean; onClose: () => void; onSuccess: () => void; project?: Project | null; } export function ProjectForm({ open, onClose, onSuccess, project, }: ProjectFormProps) { const [name, setName] = useState(project?.name || ""); const [gitRepoPath, setGitRepoPath] = useState(project?.git_repo_path || ""); const [loading, setLoading] = useState(false); const [error, setError] = useState(""); const [showFolderPicker, setShowFolderPicker] = useState(false); const [repoMode, setRepoMode] = useState<"existing" | "new">("existing"); const [parentPath, setParentPath] = useState(""); const [folderName, setFolderName] = useState(""); const isEditing = !!project; // Auto-populate project name from directory name const handleGitRepoPathChange = (path: string) => { setGitRepoPath(path); // Only auto-populate name for new projects if (!isEditing && path) { // Extract the last part of the path (directory name) const dirName = path.split("/").filter(Boolean).pop() || ""; if (dirName) { // Clean up the directory name for a better project name const cleanName = dirName .replace(/[-_]/g, " ") // Replace hyphens and underscores with spaces .replace(/\b\w/g, (l) => l.toUpperCase()); // Capitalize first letter of each word setName(cleanName); } } }; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); setError(""); setLoading(true); try { let finalGitRepoPath = gitRepoPath; // For new repo mode, construct the full path if (!isEditing && repoMode === "new") { finalGitRepoPath = `${parentPath}/${folderName}`.replace(/\/+/g, "/"); } if (isEditing) { const updateData: UpdateProject = { name, git_repo_path: finalGitRepoPath, }; const response = await makeAuthenticatedRequest( `/api/projects/${project.id}`, { method: "PUT", body: JSON.stringify(updateData), } ); if (!response.ok) { throw new Error("Failed to update project"); } const data = await response.json(); if (!data.success) { throw new Error(data.message || "Failed to update project"); } } else { const createData: CreateProject = { name, git_repo_path: finalGitRepoPath, use_existing_repo: repoMode === "existing", }; const response = await makeAuthenticatedRequest("/api/projects", { method: "POST", body: JSON.stringify(createData), }); if (!response.ok) { throw new Error("Failed to create project"); } const data = await response.json(); if (!data.success) { throw new Error(data.message || "Failed to create project"); } } onSuccess(); setName(""); setGitRepoPath(""); setParentPath(""); setFolderName(""); } catch (error) { setError(error instanceof Error ? error.message : "An error occurred"); } finally { setLoading(false); } }; const handleClose = () => { setName(project?.name || ""); setGitRepoPath(project?.git_repo_path || ""); setError(""); onClose(); }; return ( {isEditing ? "Edit Project" : "Create New Project"} {isEditing ? "Make changes to your project here. Click save when you're done." : "Choose whether to use an existing git repository or create a new one."}
{!isEditing && (
)} {repoMode === "existing" || isEditing ? (
handleGitRepoPathChange(e.target.value)} placeholder="/path/to/your/existing/repo" required className="flex-1" />
{!isEditing && (

Select a folder that already contains a git repository

)}
) : (
setParentPath(e.target.value)} placeholder="/path/to/parent/directory" required className="flex-1" />

Choose where to create the new repository

{ setFolderName(e.target.value); if (e.target.value) { setName( e.target.value .replace(/[-_]/g, " ") .replace(/\b\w/g, (l) => l.toUpperCase()) ); } }} placeholder="my-awesome-project" required className="flex-1" />

The project name will be auto-populated from this folder name

)}
setName(e.target.value)} placeholder="Enter project name" required />
{error && ( {error} )}
setShowFolderPicker(false)} onSelect={(path) => { if (repoMode === "existing" || isEditing) { handleGitRepoPathChange(path); } else { setParentPath(path); } setShowFolderPicker(false); }} value={repoMode === "existing" || isEditing ? gitRepoPath : parentPath} title={ repoMode === "existing" || isEditing ? "Select Git Repository" : "Select Parent Directory" } description={ repoMode === "existing" || isEditing ? "Choose an existing git repository" : "Choose where to create the new repository" } />
); }