1021d61be3
Moved 43 component files into 9 feature domains: - features/git/ — commit-dialog, commit-panel, file-editor, git-mount-editor, git-toolbar, merge-dialog - features/project/ — repositories-settings-tab, repository-create-dialog - features/terminal/ — special-keys-panel, special-keys-strip, terminal-session-tabs, terminal - features/workspace/ — workspace-card, workspace-create-form, workspace-header, workspace-instance-chips - features/session/ — create-session-form, session-card, session-list - features/tool/ — instance-list, manifest-editor, start-tool-fab, start-tool-modal, tool-starter, tools-bottom-sheet - features/notification/ — event-toast-bridge, notification-center, notification-item - features/settings/ — settings-tab-layout - features/mobile/ — mobile-action-sheet, mobile-detail-view, mobile-edit-view, mobile-fab, mobile-list-view, mobile-nav, mobile-page-header, mobile-terminal-header, mobile-terminal-wrapper Updated all imports across pages and components. Root components/ now only contains generic UI pieces: app-shell, code-editor, data-states, icon, protected-route, syntax-highlighter. Quality gates: verified no remaining old imports.
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/features/settings/settings-tab-layout";
|
|
import { RepositoriesSettingsTab } from "../components/features/project/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>
|
|
);
|
|
};
|