diff --git a/apps/web/src/components/repositories-settings-tab.test.tsx b/apps/web/src/components/repositories-settings-tab.test.tsx index c223e4b..69e5304 100644 --- a/apps/web/src/components/repositories-settings-tab.test.tsx +++ b/apps/web/src/components/repositories-settings-tab.test.tsx @@ -32,7 +32,7 @@ afterEach(() => { }); describe("RepositoriesSettingsTab", () => { - it("opens create dialog and submits a repository", async () => { + it("opens create dialog and clones an existing repository", async () => { const listMock = vi.spyOn(gitRepositoriesApi, "listRepositories").mockResolvedValue(mockRepositories); const createMock = vi.spyOn(gitRepositoriesApi, "createRepository").mockResolvedValue(mockRepositories[0]); @@ -46,14 +46,34 @@ describe("RepositoriesSettingsTab", () => { fireEvent.change(screen.getByPlaceholderText(/repository-name/i), { target: { value: "New Repo" }, }); - fireEvent.click(screen.getByRole("button", { name: /create/i })); + fireEvent.change(screen.getByPlaceholderText(/github.com\/user\/repo.git/i), { + target: { value: "https://github.com/user/repo.git" }, + }); + fireEvent.click(screen.getByRole("button", { name: /clone repository/i })); await waitFor(() => { expect(createMock).toHaveBeenCalledWith("proj-1", { name: "New Repo", - remote_url: undefined, + remote_url: "https://github.com/user/repo.git", }); }); expect(listMock).toHaveBeenCalledTimes(2); }); + + it("shows validation when cloning without a remote url", async () => { + vi.spyOn(gitRepositoriesApi, "listRepositories").mockResolvedValue(mockRepositories); + render(); + + await waitFor(() => { + expect(screen.getByText("Main Repo")).toBeInTheDocument(); + }); + + fireEvent.click(screen.getByRole("button", { name: /add repository/i })); + fireEvent.change(screen.getByPlaceholderText(/repository-name/i), { + target: { value: "New Repo" }, + }); + fireEvent.click(screen.getByRole("button", { name: /clone repository/i })); + + expect(screen.getByText(/remote url is required/i)).toBeInTheDocument(); + }); }); diff --git a/apps/web/src/components/repositories-settings-tab.tsx b/apps/web/src/components/repositories-settings-tab.tsx index 7ff9a62..c7545ee 100644 --- a/apps/web/src/components/repositories-settings-tab.tsx +++ b/apps/web/src/components/repositories-settings-tab.tsx @@ -15,6 +15,7 @@ export const RepositoriesSettingsTab: React.FC = () => { const [repositories, setRepositories] = useState([]); const [loading, setLoading] = useState(true); const [showCreate, setShowCreate] = useState(false); + const [createMode, setCreateMode] = useState<"clone" | "blank">("clone"); const [formName, setFormName] = useState(""); const [formRemoteUrl, setFormRemoteUrl] = useState(""); const [error, setError] = useState(""); @@ -49,6 +50,10 @@ export const RepositoriesSettingsTab: React.FC = () => { setError("Repository name is required"); return; } + if (createMode === "clone" && !formRemoteUrl.trim()) { + setError("Remote URL is required to clone an existing repository"); + return; + } try { const input: GitRepositoryCreate = { @@ -57,6 +62,7 @@ export const RepositoriesSettingsTab: React.FC = () => { }; await createRepository(projectId, input); setShowCreate(false); + setCreateMode("clone"); setFormName(""); setFormRemoteUrl(""); await loadRepositories(); @@ -82,7 +88,11 @@ export const RepositoriesSettingsTab: React.FC = () => { Repositories - setShowCreate(true)} type="button"> + setShowCreate(true)} + type="button" + > Add Repository @@ -117,9 +127,32 @@ export const RepositoriesSettingsTab: React.FC = () => { Add Repository + + Clone an existing repository from a git server, or create a blank bare repo here. + + + + setCreateMode("clone")} + /> + Clone existing repository + + + setCreateMode("blank")} + /> + Create blank repository + + - Name + Repository name { /> - Remote URL (optional) + Remote URL {createMode === "clone" ? "(required)" : "(optional)"} { Cancel - Create + {createMode === "clone" ? "Clone Repository" : "Create Blank Repository"}
+ Clone an existing repository from a git server, or create a blank bare repo here. +