0bc0d99c21
- Create SettingsTabLayout component with sidebar navigation - Create ProjectSettingsPage with General settings tab - Create RepositoriesSettingsTab for repo management - Add Members placeholder tab - Update router with settings routes - Add CSS styles for settings layout - Navigate to /projects/:id/settings from workspace header
149 lines
4.3 KiB
TypeScript
149 lines
4.3 KiB
TypeScript
import React, { useState, useEffect } from "react";
|
|
import { useParams, Link, useNavigate, Routes, Route } from "react-router-dom";
|
|
import { SettingsTabLayout } from "../components/settings-tab-layout";
|
|
import { RepositoriesSettingsTab } from "../components/repositories-settings-tab";
|
|
import { apiClient } from "../api/client";
|
|
import { Project } from "../types";
|
|
|
|
const tabs = [
|
|
{ id: "general", label: "General", path: "general" },
|
|
{ id: "repositories", label: "Repositories", path: "repositories" },
|
|
{ id: "members", label: "Members", path: "members" },
|
|
];
|
|
|
|
const GeneralSettings: React.FC<{
|
|
project: Project;
|
|
projectId: string;
|
|
}> = ({ project, projectId }) => {
|
|
const navigate = useNavigate();
|
|
const [name, setName] = useState(project.name);
|
|
const [description, setDescription] = useState(project.description || "");
|
|
const [saving, setSaving] = useState(false);
|
|
const [error, setError] = useState("");
|
|
|
|
const handleSave = async () => {
|
|
setSaving(true);
|
|
try {
|
|
await apiClient.patch(`/projects/${projectId}`, {
|
|
name,
|
|
description,
|
|
});
|
|
} catch (err) {
|
|
setError("Failed to save changes");
|
|
} finally {
|
|
setSaving(false);
|
|
}
|
|
};
|
|
|
|
const handleDelete = async () => {
|
|
if (!window.confirm("Are you sure you want to delete this project?")) return;
|
|
try {
|
|
await apiClient.delete(`/projects/${projectId}`);
|
|
navigate("/projects");
|
|
} catch (err) {
|
|
setError("Failed to delete project");
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="settings-panel">
|
|
<h2>General Settings</h2>
|
|
{error && <div className="error-message">{error}</div>}
|
|
|
|
<div className="form-group">
|
|
<label>Project Name</label>
|
|
<input
|
|
type="text"
|
|
value={name}
|
|
onChange={(e) => setName(e.target.value)}
|
|
className="form-input"
|
|
/>
|
|
</div>
|
|
|
|
<div className="form-group">
|
|
<label>Description</label>
|
|
<textarea
|
|
value={description}
|
|
onChange={(e) => setDescription(e.target.value)}
|
|
className="form-textarea"
|
|
rows={4}
|
|
/>
|
|
</div>
|
|
|
|
<div className="form-actions">
|
|
<button
|
|
onClick={handleSave}
|
|
disabled={saving}
|
|
className="btn-primary"
|
|
>
|
|
{saving ? "Saving..." : "Save Changes"}
|
|
</button>
|
|
</div>
|
|
|
|
<div className="danger-zone">
|
|
<h3>Danger Zone</h3>
|
|
<button onClick={handleDelete} className="btn-danger">
|
|
Delete Project
|
|
</button>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const MembersSettingsTab: React.FC = () => (
|
|
<div className="settings-panel">
|
|
<h2>Members</h2>
|
|
<p>Team members management coming soon.</p>
|
|
</div>
|
|
);
|
|
|
|
export const ProjectSettingsPage: React.FC = () => {
|
|
const { projectId } = useParams<{ projectId: string }>();
|
|
const [project, setProject] = useState<Project | null>(null);
|
|
const [loading, setLoading] = useState(true);
|
|
const [error, setError] = useState("");
|
|
|
|
useEffect(() => {
|
|
const fetchProject = async () => {
|
|
try {
|
|
const response = await apiClient.get(`/projects/${projectId}`);
|
|
setProject(response.data);
|
|
} catch (err) {
|
|
setError("Failed to load project");
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
fetchProject();
|
|
}, [projectId]);
|
|
|
|
if (loading) return <div>Loading...</div>;
|
|
if (error) return <div className="error-message">{error}</div>;
|
|
if (!project) return <div>Project not found</div>;
|
|
|
|
return (
|
|
<div className="project-settings-page">
|
|
<div className="settings-breadcrumb">
|
|
<Link to="/projects">Projects</Link>
|
|
<span> > </span>
|
|
<Link to={`/projects/${projectId}`}>{project.name}</Link>
|
|
<span> > </span>
|
|
<span>Settings</span>
|
|
</div>
|
|
|
|
<SettingsTabLayout tabs={tabs} basePath={`/projects/${projectId}/settings`}>
|
|
<Routes>
|
|
<Route
|
|
path="general"
|
|
element={<GeneralSettings project={project} projectId={projectId!} />}
|
|
/>
|
|
<Route path="repositories" element={<RepositoriesSettingsTab />} />
|
|
<Route path="members" element={<MembersSettingsTab />} />
|
|
<Route path="*" element={<GeneralSettings project={project} projectId={projectId!} />} />
|
|
</Routes>
|
|
</SettingsTabLayout>
|
|
</div>
|
|
);
|
|
};
|