refactor(web): reuse repository create dialog
- Extract the repository create dialog into a shared component - Reuse the same clone/validation flow in project settings and repository management pages - Keep the shared UI covered with focused tests Quality gates: tsc --noEmit, vitest run src/components/repositories-settings-tab.test.tsx
This commit is contained in:
@@ -1,13 +1,8 @@
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
import {
|
||||
createRepository,
|
||||
deleteRepository,
|
||||
listRepositories,
|
||||
type GitRepository,
|
||||
type GitRepositoryCreate,
|
||||
} from "../api/git_repositories";
|
||||
import { deleteRepository, listRepositories, type GitRepository } from "../api/git_repositories";
|
||||
import { RepositoryCreateDialog } from "./repository-create-dialog";
|
||||
import { Icon } from "./icon";
|
||||
|
||||
export const RepositoriesSettingsTab: React.FC = () => {
|
||||
@@ -15,9 +10,6 @@ export const RepositoriesSettingsTab: React.FC = () => {
|
||||
const [repositories, setRepositories] = useState<GitRepository[]>([]);
|
||||
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("");
|
||||
|
||||
const loadRepositories = useCallback(async () => {
|
||||
@@ -41,36 +33,6 @@ export const RepositoriesSettingsTab: React.FC = () => {
|
||||
void loadRepositories();
|
||||
}, [loadRepositories]);
|
||||
|
||||
const handleCreate = async (event: React.FormEvent) => {
|
||||
event.preventDefault();
|
||||
setError("");
|
||||
|
||||
if (!projectId) return;
|
||||
if (!formName.trim()) {
|
||||
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 = {
|
||||
name: formName.trim(),
|
||||
remote_url: formRemoteUrl.trim() || undefined,
|
||||
};
|
||||
await createRepository(projectId, input);
|
||||
setShowCreate(false);
|
||||
setCreateMode("clone");
|
||||
setFormName("");
|
||||
setFormRemoteUrl("");
|
||||
await loadRepositories();
|
||||
} catch {
|
||||
setError("Failed to create repository");
|
||||
}
|
||||
};
|
||||
|
||||
const handleDelete = async (repoId: string) => {
|
||||
if (!projectId) return;
|
||||
if (!window.confirm("Are you sure you want to delete this repository?")) return;
|
||||
@@ -124,62 +86,13 @@ export const RepositoriesSettingsTab: React.FC = () => {
|
||||
</div>
|
||||
|
||||
{showCreate && (
|
||||
<div className="dialog-overlay" role="dialog" aria-modal="true">
|
||||
<div className="dialog">
|
||||
<h3>Add Repository</h3>
|
||||
<p className="muted">
|
||||
Clone an existing repository from a git server, or create a blank bare repo here.
|
||||
</p>
|
||||
<form onSubmit={handleCreate} className="stack">
|
||||
<div className="form-field">
|
||||
<label>
|
||||
<input
|
||||
type="radio"
|
||||
name="repository-mode"
|
||||
checked={createMode === "clone"}
|
||||
onChange={() => setCreateMode("clone")}
|
||||
/>
|
||||
Clone existing repository
|
||||
</label>
|
||||
<label>
|
||||
<input
|
||||
type="radio"
|
||||
name="repository-mode"
|
||||
checked={createMode === "blank"}
|
||||
onChange={() => setCreateMode("blank")}
|
||||
/>
|
||||
Create blank repository
|
||||
</label>
|
||||
</div>
|
||||
<label className="form-field">
|
||||
Repository name
|
||||
<input
|
||||
type="text"
|
||||
value={formName}
|
||||
onChange={(event) => setFormName(event.target.value)}
|
||||
placeholder="repository-name"
|
||||
/>
|
||||
</label>
|
||||
<label className="form-field">
|
||||
Remote URL {createMode === "clone" ? "(required)" : "(optional)"}
|
||||
<input
|
||||
type="text"
|
||||
value={formRemoteUrl}
|
||||
onChange={(event) => setFormRemoteUrl(event.target.value)}
|
||||
placeholder="https://github.com/user/repo.git"
|
||||
/>
|
||||
</label>
|
||||
<div className="dialog-actions">
|
||||
<button className="secondary-button" onClick={() => setShowCreate(false)} type="button">
|
||||
Cancel
|
||||
</button>
|
||||
<button className="primary-button" type="submit">
|
||||
{createMode === "clone" ? "Clone Repository" : "Create Blank Repository"}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<RepositoryCreateDialog
|
||||
projectId={projectId!}
|
||||
open={showCreate}
|
||||
title="Add Repository"
|
||||
onClose={() => setShowCreate(false)}
|
||||
onCreated={loadRepositories}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user