ce8b5dc86d
- Extract tool instance lifecycle endpoints (start/stop/restart/delete) from api/tool/tool_instances.py into new api/tool/tool_lifecycle.py. - Register tool_lifecycle_router in main.py and api/tool/__init__.py. - Extract inline WorkspaceDetailPage components into components/features/workspace/: detail header, tab bars, file/git/tools/settings panels. Slim page from ~446 to ~62 lines. - Update OpenSpec reorganize-long-files tasks to reflect completed work and current source state; mark change completed. - Regenerate project maps. Quality gates: python3 -m py_compile (backend clean), npm run typecheck, npm run lint, npm test -- --run (87 passed), pytest workspace integration and unit tests (27 passed, 1 skipped).
63 lines
2.1 KiB
TypeScript
63 lines
2.1 KiB
TypeScript
/** Workspace detail page — primary work surface. */
|
|
|
|
import { useState } from "react";
|
|
import { useParams } from "react-router-dom";
|
|
import { useWorkspaces } from "../hooks/use-workspaces";
|
|
import { useMobileViewport } from "../hooks/use-mobile-viewport";
|
|
import { WorkspaceDetailHeader } from "../components/features/workspace/workspace-detail-header";
|
|
import {
|
|
WorkspaceMobileTabBar,
|
|
WorkspaceTabBar,
|
|
type WorkspaceTab,
|
|
} from "../components/features/workspace/workspace-tab-bar";
|
|
import { WorkspaceFilePanel } from "../components/features/workspace/workspace-file-panel";
|
|
import { WorkspaceGitPanel } from "../components/features/workspace/workspace-git-panel";
|
|
import { WorkspaceToolsPanel } from "../components/features/workspace/workspace-tools-panel";
|
|
import { WorkspaceSettingsPanel } from "../components/features/workspace/workspace-settings-panel";
|
|
|
|
export function WorkspaceDetailPage() {
|
|
const { workspaceId } = useParams<{ workspaceId: string }>();
|
|
const [activeTab, setActiveTab] = useState<WorkspaceTab>("files");
|
|
const isMobile = useMobileViewport();
|
|
|
|
const { workspaces, loading: wsLoading } = useWorkspaces();
|
|
const workspace = workspaces.find((w) => w.id === workspaceId);
|
|
|
|
if (wsLoading) {
|
|
return <div className="loading-state">Loading workspace...</div>;
|
|
}
|
|
|
|
if (!workspace) {
|
|
return (
|
|
<div className="empty-state">
|
|
<h2>Workspace not found</h2>
|
|
<p>The workspace you are looking for does not exist.</p>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className={`workspace-detail ${isMobile ? "mobile" : ""}`}>
|
|
<WorkspaceDetailHeader workspace={workspace} />
|
|
<WorkspaceTabBar active={activeTab} onChange={setActiveTab} />
|
|
<div className="workspace-content">
|
|
{activeTab === "files" && (
|
|
<WorkspaceFilePanel workspaceId={workspace.id} />
|
|
)}
|
|
{activeTab === "git" && (
|
|
<WorkspaceGitPanel workspaceId={workspace.id} />
|
|
)}
|
|
{activeTab === "tools" && (
|
|
<WorkspaceToolsPanel workspace={workspace} />
|
|
)}
|
|
{activeTab === "settings" && (
|
|
<WorkspaceSettingsPanel workspace={workspace} />
|
|
)}
|
|
</div>
|
|
{isMobile && (
|
|
<WorkspaceMobileTabBar active={activeTab} onChange={setActiveTab} />
|
|
)}
|
|
</div>
|
|
);
|
|
}
|