8c7affc933
Fixed import paths for 43 components moved into features/ directories. Key fixes: - api/, types/, hooks/, state/, utils/ imports need ../../../ from features/*/ - components/ imports need ../../ from features/*/ - Cross-feature imports use relative paths (e.g., ../tool/tools-bottom-sheet) - app-shell.tsx updated to import from features/ subdirectories Frontend typecheck now passes except for one pre-existing error: xterm-addon-webgl missing type declarations. Quality gates: ruff passed on backend, py_compile passed on all backend files.
100 lines
2.9 KiB
TypeScript
100 lines
2.9 KiB
TypeScript
import React, { useCallback, useEffect, useState } from "react";
|
|
import { useParams } from "react-router-dom";
|
|
|
|
import { deleteRepository, listRepositories, type GitRepository } from "../../../api/git-repositories";
|
|
import { RepositoryCreateDialog } from "./repository-create-dialog";
|
|
import { Icon } from "../../icon";
|
|
|
|
export const RepositoriesSettingsTab: React.FC = () => {
|
|
const { projectId } = useParams<{ projectId: string }>();
|
|
const [repositories, setRepositories] = useState<GitRepository[]>([]);
|
|
const [loading, setLoading] = useState(true);
|
|
const [showCreate, setShowCreate] = useState(false);
|
|
const [error, setError] = useState("");
|
|
|
|
const loadRepositories = useCallback(async () => {
|
|
if (!projectId) {
|
|
setLoading(false);
|
|
return;
|
|
}
|
|
|
|
setLoading(true);
|
|
try {
|
|
const data = await listRepositories(projectId);
|
|
setRepositories(data);
|
|
} catch {
|
|
setError("Failed to load repositories");
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}, [projectId]);
|
|
|
|
useEffect(() => {
|
|
void loadRepositories();
|
|
}, [loadRepositories]);
|
|
|
|
const handleDelete = async (repoId: string) => {
|
|
if (!projectId) return;
|
|
if (!window.confirm("Are you sure you want to delete this repository?")) return;
|
|
try {
|
|
await deleteRepository(projectId, repoId);
|
|
setRepositories((current) => current.filter((r) => r.id !== repoId));
|
|
} catch {
|
|
setError("Failed to delete repository");
|
|
}
|
|
};
|
|
|
|
if (loading) return <div>Loading...</div>;
|
|
|
|
return (
|
|
<div className="repositories-settings-tab">
|
|
<div className="page-header">
|
|
<h2>Repositories</h2>
|
|
<button
|
|
className="primary-button"
|
|
onClick={() => setShowCreate(true)}
|
|
type="button"
|
|
>
|
|
<Icon name="add" size="sm" />
|
|
Add Repository
|
|
</button>
|
|
</div>
|
|
{error && <div className="error-message">{error}</div>}
|
|
|
|
<div className="repositories-list">
|
|
{repositories.length === 0 ? (
|
|
<p>No repositories yet.</p>
|
|
) : (
|
|
repositories.map((repo) => (
|
|
<div key={repo.id} className="repository-card">
|
|
<div className="repository-info">
|
|
<h3>{repo.name}</h3>
|
|
<p>{repo.remote_url}</p>
|
|
<span className="repo-type">
|
|
{repo.is_mirror ? "Mirror" : "Clone"}
|
|
</span>
|
|
</div>
|
|
<button
|
|
onClick={() => handleDelete(repo.id)}
|
|
className="btn-danger"
|
|
>
|
|
Delete
|
|
</button>
|
|
</div>
|
|
))
|
|
)}
|
|
</div>
|
|
|
|
{showCreate && (
|
|
<RepositoryCreateDialog
|
|
projectId={projectId!}
|
|
open={showCreate}
|
|
title="Add Repository"
|
|
onClose={() => setShowCreate(false)}
|
|
onCreated={loadRepositories}
|
|
/>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|