/** Unified workspace creation form with project/repo/branch selectors. */ import { useState, useEffect, useCallback } from "react"; import { Icon } from "./icon"; import { listProjects } from "../api/projects"; import { listRepositories } from "../api/git_repositories"; import { createWorkspaceTopLevel } from "../api/workspaces"; import { useGitRepo } from "../hooks/use-git-repo"; import type { ProjectWithRepos } from "../types"; import type { GitRepository } from "../api/git_repositories"; export interface WorkspaceCreateFormProps { /** Called after successful creation. */ onSubmit: () => void | Promise; /** Cancel callback. */ onCancel: () => void; /** Optional: pre-selected project ID (hides project selector). */ defaultProjectId?: string; /** Optional: pre-selected repo ID (hides repo selector). */ defaultRepoId?: string; } export function WorkspaceCreateForm({ onSubmit, onCancel, defaultProjectId, defaultRepoId, }: WorkspaceCreateFormProps) { const isContextual = Boolean(defaultProjectId && defaultRepoId); const [projects, setProjects] = useState([]); const [repos, setRepos] = useState([]); const [selectedProject, setSelectedProject] = useState( defaultProjectId ?? "", ); const [selectedRepo, setSelectedRepo] = useState(defaultRepoId ?? ""); const [selectedBranch, setSelectedBranch] = useState(""); const [newBranchName, setNewBranchName] = useState(""); const [isNewBranch, setIsNewBranch] = useState(false); const [name, setName] = useState(""); const [submitting, setSubmitting] = useState(false); const [fetchingProjects, setFetchingProjects] = useState(!isContextual); const [error, setError] = useState(null); /* Git repo hook handles branch fetching, loading, errors */ const git = useGitRepo( selectedProject || undefined, selectedRepo || undefined, ); /* Sync local branch state with hook data */ useEffect(() => { if (git.branches.length > 0 && !selectedBranch) { const preferred = git.defaultBranch && git.branches.includes(git.defaultBranch) ? git.defaultBranch : git.branches[0]; setSelectedBranch(preferred); setIsNewBranch(false); } else if (git.error && git.branches.length === 0 && !isNewBranch) { // API failed — default to manual entry so user can type a branch setIsNewBranch(true); setSelectedBranch("__manual__"); } }, [git.branches, git.defaultBranch, git.error, selectedBranch, isNewBranch]); /* ── Load projects (standalone mode only) ── */ const loadProjects = useCallback(async () => { if (isContextual) return; try { const data = await listProjects(); setProjects(data); if (data.length === 1 && !defaultProjectId) { setSelectedProject(data[0].id); } } catch { setError("Failed to load projects"); } finally { setFetchingProjects(false); } }, [isContextual, defaultProjectId]); useEffect(() => { void loadProjects(); }, [loadProjects]); /* ── Load repos when project changes ── */ useEffect(() => { if (!selectedProject) { setRepos([]); if (!defaultRepoId) setSelectedRepo(""); return; } const loadRepos = async () => { try { const data = await listRepositories(selectedProject); setRepos(data); if (data.length === 1 && !defaultRepoId) { setSelectedRepo(data[0].id); } } catch { setError("Failed to load repositories"); } }; void loadRepos(); }, [selectedProject, defaultRepoId]); const handleBranchChange = (value: string) => { if (value === "__new__") { setIsNewBranch(true); setSelectedBranch("__new__"); setNewBranchName(""); } else if (value === "__manual__") { setIsNewBranch(true); setSelectedBranch("__manual__"); setNewBranchName(""); } else { setIsNewBranch(false); setSelectedBranch(value); } }; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!selectedRepo) { setError("Please select a repository"); return; } if (!name.trim()) { setError("Workspace name is required"); return; } const branchName = isNewBranch ? newBranchName.trim() : selectedBranch; if (!branchName) { setError("Please select or enter a branch"); return; } setSubmitting(true); setError(null); try { await createWorkspaceTopLevel({ repo_id: selectedRepo, name: name.trim(), branch: branchName, }); await onSubmit(); } catch (err) { setError( err instanceof Error ? err.message : "Failed to create workspace", ); } finally { setSubmitting(false); } }; /* Show single combined error */ const displayError = error || git.error; if (fetchingProjects) { return (

Loading projects...

); } const branchSelectDisabled = !selectedRepo || submitting || (git.loading && git.branches.length === 0); return (

Create Workspace

{/* Project selector (standalone only) */} {!isContextual && (
)} {/* Repo selector (standalone only) */} {!isContextual && (
)}
setName(e.target.value)} placeholder="e.g., feature-branch" required disabled={submitting} />
{/* Show a hint when branches couldn’t be loaded */} {git.error && git.branches.length === 0 && selectedRepo && (

Couldn’t load branches — type one manually.

)} {/* Text input for new branch or manual entry */} {isNewBranch && ( setNewBranchName(e.target.value)} placeholder="new-branch-name" required style={{ marginTop: "0.5rem" }} disabled={submitting} /> )}
{displayError && (
{displayError}
)}
); }