feat: complete workspace-first-ui cleanup and tests

- Delete dead repo-workspace code: RepoWorkspacePage, useRepoWorkspace,
  WorkspaceLayout, FileBrowser, old git components (git-toolbar, file-editor,
  commit-panel), and repo-workspace.css.
- Fix stale backend test imports for moved models/services.
- Add GitOperations unit tests.
- Add integration tests for workspace files, git, and instances endpoints.
- Add frontend tests for WorkspaceDetailPage and ProjectCard.
- Update OpenSpec workspace-first-ui tasks and mark change completed.
- Regenerate project maps.

Quality gates: npm run typecheck, npm run lint, npm test -- --run (87 passed),
python3 -m py_compile on changed backend files, pytest backend workspace tests.
This commit is contained in:
Developer
2026-06-12 17:25:09 +00:00
parent aa49efb236
commit c26e9eacfa
49 changed files with 993 additions and 2057 deletions
@@ -2,13 +2,14 @@
dir: apps/web/src/components/features/project
## role
Provides UI components for managing projects, including cards, dialogs, and repository settings with full CRUD operations and Git integration.
Provides React components for project management UI including project cards, creation/editing dialogs, and repository settings with CRUD operations.
## parent
index: apps/web/src/components/features/.pi-map.index.md
map: apps/web/src/components/features/.pi-map.md
## children
-
## files
- ProjectCard.test.tsx
- ProjectCard.tsx
- ProjectDialog.tsx
- repositories-settings-tab.test.tsx
@@ -21,6 +22,6 @@ map: apps/web/src/components/features/project/.pi-map.md
- change project behavior
read: ProjectCard.tsx, ProjectDialog.tsx, repositories-settings-tab.tsx
- update project tests
read: repositories-settings-tab.test.tsx
read: ProjectCard.test.tsx, repositories-settings-tab.test.tsx
## dirty
-
@@ -4,17 +4,18 @@ dir: apps/web/src/components/features/project
index: apps/web/src/components/features/project/.pi-map.index.md
## role
Provides UI components for managing projects, including cards, dialogs, and repository settings with full CRUD operations and Git integration.
Provides React components for project management UI including project cards, creation/editing dialogs, and repository settings with CRUD operations.
## files
- ProjectCard.test.tsx | Unit tests for the ProjectCard component verifying rendering, workspace creation, and delete confirmation behavior. | dep: @testing-library/jest-dom/vitest, @testing-library/react, react-router-dom, vitest, ./ProjectCard, ../../../types, @testing-library/jest-dom, ProjectCard, types
- ProjectCard.tsx | Renders an expandable project card displaying project info, repositories, and associated workspaces with CRUD actions. | exp: ProjectCard | dep: ../../icon, ../workspace/workspace-create-form, ../../../types, Icon, WorkspaceCreateForm, ProjectWithRepos, WorkspaceSummary
- ProjectDialog.tsx | Renders a modal dialog for creating or editing a project with name/description inputs and cancel/submit actions. | exp: ProjectDialog | dep: ../../icon, React, icon
- repositories-settings-tab.test.tsx | Tests the RepositoriesSettingsTab component's repository creation flows including full URL cloning, owner/repo mode with SSH key selection, and SSH URL validation. | dep: @testing-library/react, vitest, ./repositories-settings-tab, ../../../api/git-repositories, ../../../api/ssh-keys, react-router-dom
- repositories-settings-tab.tsx | A React component that displays and manages Git repositories for a project, allowing users to list, create, and delete repositories. | exp: RepositoriesSettingsTab | dep: react, react-router-dom, ../../../api/git-repositories, ./repository-create-dialog, ../../icon
- repository-create-dialog.tsx | React dialog component for creating or cloning Git repositories with URL validation and SSH key selection. | exp: RepositoryCreateDialog | dep: react, ../../../api/git-repositories, ../../../api/ssh-keys, ../../icon
## arch
Feature-based component architecture with compound UI patterns (card/dialog/tab), form handling for CRUD, and SSH-aware Git repository management with validation testing.
Feature-based component architecture with compound component patterns (card + dialog), test co-location, and separation of presentational components from business logic through dialog-based workflows.
## tags
repositories, project, dialog, react, create, settings, icon, repository
project, repositories, dialog, react, create, settings, icon, repository
## symbols
- ProjectCard
- ProjectDialog
@@ -24,6 +25,6 @@ repositories, project, dialog, react, create, settings, icon, repository
- change project behavior
read: ProjectCard.tsx, ProjectDialog.tsx, repositories-settings-tab.tsx
- update project tests
read: repositories-settings-tab.test.tsx
read: ProjectCard.test.tsx, repositories-settings-tab.test.tsx
## dirty
-
@@ -0,0 +1,117 @@
import "@testing-library/jest-dom/vitest";
import { cleanup, fireEvent, render, screen } from "@testing-library/react";
import { MemoryRouter } from "react-router-dom";
import { afterEach, describe, expect, it, vi } from "vitest";
afterEach(() => {
cleanup();
});
import { ProjectCard } from "./ProjectCard";
import type { ProjectWithRepos } from "../../../types";
const mockProject: ProjectWithRepos = {
id: "proj-1",
name: "Alpha Project",
description: "First project",
owner_id: "user-1",
default_ssh_key_id: null,
created_at: "2026-06-01T00:00:00Z",
repositories: [
{
id: "repo-1",
name: "my-repo",
remote_url: "https://example.com/repo.git",
workspaces: [
{
id: "ws-1",
name: "dev",
branch: "main",
status: "ready",
instance_count: 2,
},
],
},
],
};
describe("ProjectCard", () => {
it("renders project name and repository", () => {
render(
<MemoryRouter>
<ProjectCard
project={mockProject}
expanded={true}
deleteConfirm={false}
workspaceLoading={null}
showCreateForm={null}
onToggle={vi.fn()}
onEdit={vi.fn()}
onDelete={vi.fn()}
onConfirmDelete={vi.fn()}
onCancelDelete={vi.fn()}
onCreateWorkspace={vi.fn()}
onWorkspaceAction={vi.fn()}
onCancelCreate={vi.fn()}
onCreated={vi.fn()}
/>
</MemoryRouter>
);
expect(screen.getByText("Alpha Project")).toBeInTheDocument();
expect(screen.getByText("my-repo")).toBeInTheDocument();
expect(screen.getByText("dev")).toBeInTheDocument();
});
it("calls onCreateWorkspace when new workspace button is clicked", () => {
const onCreateWorkspace = vi.fn();
render(
<MemoryRouter>
<ProjectCard
project={mockProject}
expanded={true}
deleteConfirm={false}
workspaceLoading={null}
showCreateForm={null}
onToggle={vi.fn()}
onEdit={vi.fn()}
onDelete={vi.fn()}
onConfirmDelete={vi.fn()}
onCancelDelete={vi.fn()}
onCreateWorkspace={onCreateWorkspace}
onWorkspaceAction={vi.fn()}
onCancelCreate={vi.fn()}
onCreated={vi.fn()}
/>
</MemoryRouter>
);
fireEvent.click(screen.getByRole("button", { name: /new workspace/i }));
expect(onCreateWorkspace).toHaveBeenCalledWith("repo-1");
});
it("shows delete confirmation", () => {
render(
<MemoryRouter>
<ProjectCard
project={mockProject}
expanded={true}
deleteConfirm={true}
workspaceLoading={null}
showCreateForm={null}
onToggle={vi.fn()}
onEdit={vi.fn()}
onDelete={vi.fn()}
onConfirmDelete={vi.fn()}
onCancelDelete={vi.fn()}
onCreateWorkspace={vi.fn()}
onWorkspaceAction={vi.fn()}
onCancelCreate={vi.fn()}
onCreated={vi.fn()}
/>
</MemoryRouter>
);
expect(screen.getByText(/are you sure/i)).toBeInTheDocument();
});
});