import { useEffect, useRef, useState } from "react"; import { createRepository, parseGitUrl, type GitRepositoryCreate, type URLParseResult } from "../api/git_repositories"; import { listSSHKeys, type SSHKey } from "../api/ssh_keys"; import { Icon } from "./icon"; type CreateMode = "clone" | "blank"; type UrlValidationStatus = "idle" | "validating" | "valid" | "needs-parsing" | "invalid"; interface RepositoryCreateDialogProps { projectId: string; open: boolean; title: string; onClose: () => void; onCreated: () => Promise | void; } export const RepositoryCreateDialog = ({ projectId, open, title, onClose, onCreated }: RepositoryCreateDialogProps) => { const [createMode, setCreateMode] = useState("clone"); const [formName, setFormName] = useState(""); const [owner, setOwner] = useState(""); const [repoName, setRepoName] = useState(""); const [advancedUrl, setAdvancedUrl] = useState(""); const [useAdvancedUrl, setUseAdvancedUrl] = useState(false); const [formError, setFormError] = useState(null); const [urlValidation, setUrlValidation] = useState<{ status: UrlValidationStatus; result: URLParseResult | null; }>({ status: "idle", result: null }); const [sshKeys, setSshKeys] = useState([]); const [selectedSshKey, setSelectedSshKey] = useState(""); const debounceTimer = useRef | null>(null); useEffect(() => { if (!open && debounceTimer.current) { clearTimeout(debounceTimer.current); debounceTimer.current = null; } }, [open]); useEffect(() => { if (!open) return; const loadKeys = async () => { try { const data = await listSSHKeys(); setSshKeys(data); } catch { // ignore } }; void loadKeys(); }, [open]); useEffect(() => { if (!open) return; if (!useAdvancedUrl) { setUrlValidation({ status: "idle", result: null }); return; } if (debounceTimer.current) { clearTimeout(debounceTimer.current); } if (!advancedUrl.trim()) { setUrlValidation({ status: "idle", result: null }); return; } setUrlValidation({ status: "validating", result: null }); debounceTimer.current = setTimeout(async () => { try { const result = await parseGitUrl(advancedUrl.trim()); if (result.is_valid_clone_url) { setUrlValidation({ status: "valid", result }); } else if (result.needs_parsing) { setUrlValidation({ status: "needs-parsing", result }); } else { setUrlValidation({ status: "invalid", result }); } } catch { setUrlValidation({ status: "invalid", result: null }); } }, 300); return () => { if (debounceTimer.current) { clearTimeout(debounceTimer.current); } }; }, [advancedUrl, open, useAdvancedUrl]); const resetForm = () => { setCreateMode("clone"); setFormName(""); setOwner(""); setRepoName(""); setAdvancedUrl(""); setUseAdvancedUrl(false); setFormError(null); setUrlValidation({ status: "idle", result: null }); setSelectedSshKey(""); }; const handleClose = () => { resetForm(); onClose(); }; const handleSubmit = async (event: React.FormEvent) => { event.preventDefault(); setFormError(null); if (!formName.trim()) { setFormError("Repository name is required"); return; } try { const input: GitRepositoryCreate = { name: formName.trim(), remote_url: undefined, }; if (createMode === "clone") { if (useAdvancedUrl) { if (!advancedUrl.trim()) { setFormError("Remote URL is required for advanced cloning"); return; } input.remote_url = advancedUrl.trim(); } else { if (!owner.trim() || !repoName.trim()) { setFormError("Owner and repository name are required"); return; } input.remote_url = `git@git.commumedia.org:${owner.trim()}/${repoName.trim()}.git`; } if (selectedSshKey) { input.ssh_key_id = selectedSshKey; } } await createRepository(projectId, input); handleClose(); await onCreated(); } catch (error: unknown) { const response = error as { response?: { data?: { detail?: string } } }; const detail = response.response?.data?.detail; setFormError(typeof detail === "string" ? detail : "Failed to create repository"); } }; const handleUseSuggestedUrl = () => { if (urlValidation.result?.base_url) { setAdvancedUrl(urlValidation.result.base_url); setUrlValidation({ status: "idle", result: null }); setFormError(null); } }; const getUrlInputClass = () => { switch (urlValidation.status) { case "valid": return "valid-url"; case "needs-parsing": return "needs-parsing-url"; case "invalid": return "invalid-url"; default: return ""; } }; if (!open) return null; return (

{title}

Clone an existing repository from git.commumedia.org, or create a blank bare repo here.

{createMode === "clone" && !useAdvancedUrl && ( <>

SSH target: git@git.commumedia.org:{owner || "owner"}/{repoName || "repo"}.git

)} {createMode === "clone" && useAdvancedUrl && ( <> )} {formError && (

{formError}

)}
); };