From fb0f2f7b9bb0c777293f98a89d38f9537b3ca76f Mon Sep 17 00:00:00 2001 From: Alex Blank Date: Fri, 22 May 2026 18:43:40 +0000 Subject: [PATCH] feat: add project settings page navigation - Remove inline edit modal from projects listing page - Add Settings link to project cards navigating to /projects/:id/settings - Reposition Open Workspace button to rightmost action for easier access - Update tests for new UI flow - Update documentation to reflect new editing workflow - Sync specs: frontend-foundation and project-management Quality gates: npm run lint passed --- apps/web/src/pages/projects.test.tsx | 39 +++++---- apps/web/src/pages/projects.tsx | 82 ++++++------------- docs/features/projects.md | 21 +++-- .../.openspec.yaml | 2 + .../design.md | 47 +++++++++++ .../proposal.md | 27 ++++++ .../specs/frontend-foundation/spec.md | 19 +++++ .../specs/project-management/spec.md | 37 +++++++++ .../tasks.md | 36 ++++++++ openspec/specs/frontend-foundation/spec.md | 18 ++++ openspec/specs/project-management/spec.md | 33 ++++++-- 11 files changed, 276 insertions(+), 85 deletions(-) create mode 100644 openspec/changes/archive/2026-05-22-add-project-settings-page/.openspec.yaml create mode 100644 openspec/changes/archive/2026-05-22-add-project-settings-page/design.md create mode 100644 openspec/changes/archive/2026-05-22-add-project-settings-page/proposal.md create mode 100644 openspec/changes/archive/2026-05-22-add-project-settings-page/specs/frontend-foundation/spec.md create mode 100644 openspec/changes/archive/2026-05-22-add-project-settings-page/specs/project-management/spec.md create mode 100644 openspec/changes/archive/2026-05-22-add-project-settings-page/tasks.md diff --git a/apps/web/src/pages/projects.test.tsx b/apps/web/src/pages/projects.test.tsx index c351626..2407c72 100644 --- a/apps/web/src/pages/projects.test.tsx +++ b/apps/web/src/pages/projects.test.tsx @@ -108,10 +108,8 @@ describe("ProjectsPage", () => { expect(screen.getByText(/project name is required/i)).toBeInTheDocument(); }); - it("opens edit dialog and saves changes", async () => { - const listMock = vi.spyOn(projectsApi, "listProjects").mockResolvedValue(mockProjects); - const updateMock = vi.spyOn(projectsApi, "updateProject").mockResolvedValue(mockProjects[0]); - + it("renders settings link for each project", async () => { + vi.spyOn(projectsApi, "listProjects").mockResolvedValue(mockProjects); render(); await waitFor(() => { @@ -121,20 +119,33 @@ describe("ProjectsPage", () => { const alphaCard = screen.getByText("Alpha Project").closest(".project-card") as HTMLElement | null; if (!alphaCard) throw new Error("Card not found"); - fireEvent.click(within(alphaCard).getByRole("button", { name: /edit/i })); - expect(screen.getByRole("dialog")).toBeInTheDocument(); + const settingsLink = within(alphaCard).getByRole("link", { name: /settings/i }); + expect(settingsLink).toBeInTheDocument(); + expect(settingsLink).toHaveAttribute("href", "/projects/proj-1/settings"); + }); - const nameInput = screen.getByDisplayValue("Alpha Project"); - fireEvent.change(nameInput, { target: { value: "Alpha Updated" } }); - fireEvent.click(screen.getByRole("button", { name: /save/i })); + it("renders open workspace link as rightmost action", async () => { + vi.spyOn(projectsApi, "listProjects").mockResolvedValue(mockProjects); + render(); await waitFor(() => { - expect(updateMock).toHaveBeenCalledWith("proj-1", { - name: "Alpha Updated", - description: "First project", - }); + expect(screen.getByText("Alpha Project")).toBeInTheDocument(); }); - expect(listMock).toHaveBeenCalledTimes(2); + + const alphaCard = screen.getByText("Alpha Project").closest(".project-card") as HTMLElement | null; + if (!alphaCard) throw new Error("Card not found"); + + const actions = alphaCard.querySelector(".project-actions"); + if (!actions) throw new Error("Actions container not found"); + + const workspaceLink = within(alphaCard).getByRole("link", { name: /open workspace/i }); + expect(workspaceLink).toBeInTheDocument(); + expect(workspaceLink).toHaveAttribute("href", "/projects/proj-1"); + + // Verify it's the last action in the container + const allActions = actions.querySelectorAll("a, button"); + const lastAction = allActions[allActions.length - 1]; + expect(lastAction).toBe(workspaceLink); }); it("shows delete confirmation and deletes project", async () => { diff --git a/apps/web/src/pages/projects.tsx b/apps/web/src/pages/projects.tsx index 91f278c..b293ffc 100644 --- a/apps/web/src/pages/projects.tsx +++ b/apps/web/src/pages/projects.tsx @@ -6,21 +6,17 @@ import { createProject, deleteProject, listProjects, - updateProject, type ProjectCreateInput, - type ProjectUpdateInput, } from "../api/projects"; import { Icon } from "../components/icon"; import type { Project } from "../types"; type ProjectsStatus = "loading" | "ready" | "error"; -type DialogMode = "none" | "create" | "edit"; export const ProjectsPage = () => { const [status, setStatus] = useState("loading"); const [projects, setProjects] = useState([]); - const [dialogMode, setDialogMode] = useState("none"); - const [editingProject, setEditingProject] = useState(null); + const [showCreate, setShowCreate] = useState(false); const [formName, setFormName] = useState(""); const [formDescription, setFormDescription] = useState(""); const [formError, setFormError] = useState(null); @@ -46,25 +42,15 @@ export const ProjectsPage = () => { setFormName(""); setFormDescription(""); setFormError(null); - setEditingProject(null); - setDialogMode("create"); + setShowCreate(true); }; - const openEdit = (project: Project) => { - setFormName(project.name); - setFormDescription(project.description ?? ""); - setFormError(null); - setEditingProject(project); - setDialogMode("edit"); - }; - - const closeDialog = () => { - setDialogMode("none"); - setEditingProject(null); + const closeCreate = () => { + setShowCreate(false); setFormError(null); }; - const handleSubmit = async (e: React.FormEvent) => { + const handleCreate = async (e: React.FormEvent) => { e.preventDefault(); setFormError(null); @@ -74,20 +60,12 @@ export const ProjectsPage = () => { } try { - if (dialogMode === "create") { - const input: ProjectCreateInput = { - name: formName.trim(), - description: formDescription.trim() || null, - }; - await createProject(input); - } else if (dialogMode === "edit" && editingProject) { - const input: ProjectUpdateInput = { - name: formName.trim(), - description: formDescription.trim() || null, - }; - await updateProject(editingProject.id, input); - } - closeDialog(); + const input: ProjectCreateInput = { + name: formName.trim(), + description: formDescription.trim() || null, + }; + await createProject(input); + closeCreate(); await loadProjects(); } catch { setFormError("Failed to save project"); @@ -139,17 +117,13 @@ export const ProjectsPage = () => { {project.description &&

{project.description}

}
- - Open Workspace - - + + Settings + {deleteConfirmId === project.id ? (
Are you sure? @@ -180,17 +154,20 @@ export const ProjectsPage = () => { Delete )} + + Open Workspace +
))}
)} - {dialogMode !== "none" && ( + {showCreate && (
-

{dialogMode === "create" ? "Create Project" : "Edit Project"}

-
+

Create Project

+ {formError &&

{formError}

}
-
diff --git a/docs/features/projects.md b/docs/features/projects.md index d9be780..48100d9 100644 --- a/docs/features/projects.md +++ b/docs/features/projects.md @@ -22,25 +22,30 @@ The Projects page displays all your projects in a card layout showing: - Creation date - Associated repositories count +Each project card provides quick actions: +- **Settings** — Navigate to the project settings page +- **Delete** — Delete the project with confirmation +- **Open Workspace** — Open the project's workspace (rightmost action) + ### Opening a Project Workspace -Click on any project card to open its **workspace**. The workspace is the default view for a project and shows: +Click the **"Open Workspace"** button on any project card to open its **workspace**. The workspace is the default view for a project and shows: - Repository file browser - Branch selector - File viewer ### Editing a Project -1. From the Projects page, click the **menu icon** (⋮) on a project card -2. Select **"Edit"** -3. Update the name or description -4. Click **"Save"** +1. From the Projects page, click the **"Settings"** link on a project card +2. On the project settings page, update the **name** or **description** +3. Click **"Save Changes"** + +The settings page also provides access to repository management and member settings. ### Deleting a Project -1. From the Projects page, click the **menu icon** (⋮) on a project card -2. Select **"Delete"** -3. Confirm the deletion +1. From the Projects page, click the **"Delete"** button on a project card +2. Confirm the deletion **Note:** Deleting a project also deletes all associated repositories and their data. This action cannot be undone. diff --git a/openspec/changes/archive/2026-05-22-add-project-settings-page/.openspec.yaml b/openspec/changes/archive/2026-05-22-add-project-settings-page/.openspec.yaml new file mode 100644 index 0000000..4a1c677 --- /dev/null +++ b/openspec/changes/archive/2026-05-22-add-project-settings-page/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-05-22 diff --git a/openspec/changes/archive/2026-05-22-add-project-settings-page/design.md b/openspec/changes/archive/2026-05-22-add-project-settings-page/design.md new file mode 100644 index 0000000..f700d9c --- /dev/null +++ b/openspec/changes/archive/2026-05-22-add-project-settings-page/design.md @@ -0,0 +1,47 @@ +## Context + +The projects listing page (`apps/web/src/pages/projects.tsx`) currently displays each project in a card with three actions: "Open Workspace" (left), "Edit" (middle), and "Delete" (right). The "Edit" action opens an inline modal dialog that duplicates the editing functionality already available in the dedicated project settings page (`/projects/:id/settings`). + +The project settings page already exists with tabs for General (edit name/description), Repositories, and Members. The add-repo functionality is already located in the Repositories tab. + +## Goals / Non-Goals + +**Goals:** +- Simplify the projects listing page by removing the inline edit modal +- Add a Settings link to project cards for navigation to the settings page +- Reposition the "Open Workspace" button to the right side for easier access +- Keep the projects page focused on navigation and creation + +**Non-Goals:** +- No changes to project settings page functionality (already implemented) +- No changes to backend APIs +- No changes to the add-repo flow (already in settings) +- No changes to workspace or repository pages + +## Decisions + +**Decision: Remove Edit modal, link to settings instead** +- Rationale: The settings page already provides a better editing experience with tabs, persistence feedback, and access to repositories/members. Maintaining two edit UIs creates duplication and confusion. +- Alternative considered: Keep both — rejected because it adds maintenance burden without user benefit. + +**Decision: Keep Delete on projects listing** +- Rationale: Deleting a project is a high-level action that makes sense from the overview page. Users expect to delete items from a list view. + +**Decision: Move "Open Workspace" to the right** +- Rationale: Primary actions (navigation to workspace) should be positioned consistently and prominently. Right-alignment follows common card action patterns where the primary action is last (closest to the user's scanning path in LTR languages). +- Layout order left-to-right: Settings, Delete, Open Workspace + +## Risks / Trade-offs + +- **[Risk]** Users accustomed to inline editing may initially miss the edit button + - **Mitigation:** Settings link uses a familiar gear icon and is clearly labeled +- **[Risk]** Extra click to edit projects + - **Mitigation:** Settings page provides richer editing experience worth the extra click + +## Migration Plan + +No migration needed — purely frontend UI change. Existing project data and APIs are unaffected. + +## Open Questions + +None \ No newline at end of file diff --git a/openspec/changes/archive/2026-05-22-add-project-settings-page/proposal.md b/openspec/changes/archive/2026-05-22-add-project-settings-page/proposal.md new file mode 100644 index 0000000..8027742 --- /dev/null +++ b/openspec/changes/archive/2026-05-22-add-project-settings-page/proposal.md @@ -0,0 +1,27 @@ +## Why + +The current projects listing page mixes project management actions (create, edit, delete) with workspace navigation, leading to a cluttered UI. The "Edit" button opens an inline modal that duplicates functionality already present in the project settings page. Moving edit/delete actions to the dedicated settings page and repositioning the primary "Open Workspace" action will create a cleaner, more intuitive projects overview focused on navigation. + +## What Changes + +- **Remove** the Edit button and modal dialog from the projects listing page (`projects.tsx`) +- **Add** a Settings link to each project card that navigates to `/projects/:id/settings` +- **Move** the "Open Workspace" button to the right side of project cards for easier access +- **Keep** the "New Project" button and "Delete" button on the projects listing page +- **No backend changes** — uses existing project settings page and APIs + +## Capabilities + +### New Capabilities +- *(none — uses existing project-management and frontend-foundation capabilities)* + +### Modified Capabilities +- `project-management`: Update UI flow — project editing is now accessed via settings page instead of inline modal +- `frontend-foundation`: Update projects list page layout and navigation pattern + +## Impact + +- `apps/web/src/pages/projects.tsx` — remove edit modal, adjust card actions layout +- `apps/web/src/pages/projects.test.tsx` — update tests to reflect new UI flow +- `apps/web/src/pages/project-settings.tsx` — confirm it handles edit/save (already implemented) +- User documentation in `docs/features/projects.md` — update editing instructions \ No newline at end of file diff --git a/openspec/changes/archive/2026-05-22-add-project-settings-page/specs/frontend-foundation/spec.md b/openspec/changes/archive/2026-05-22-add-project-settings-page/specs/frontend-foundation/spec.md new file mode 100644 index 0000000..1cc22d2 --- /dev/null +++ b/openspec/changes/archive/2026-05-22-add-project-settings-page/specs/frontend-foundation/spec.md @@ -0,0 +1,19 @@ +## ADDED Requirements + +### Requirement: Projects Listing Page Layout +The projects listing page SHALL display project cards with Settings, Delete, and Open Workspace actions, where Open Workspace is the rightmost action. + +#### Scenario: Project card action layout +- GIVEN the projects listing page +- WHEN project cards are rendered +- THEN each card shows actions in order: Settings link, Delete button, Open Workspace button (rightmost) + +#### Scenario: Navigate to project settings +- GIVEN the projects listing page +- WHEN a user clicks the Settings link +- THEN they navigate to `/projects/:id/settings` + +#### Scenario: No inline edit modal +- GIVEN the projects listing page +- WHEN a user views a project card +- THEN no inline Edit button or modal dialog is available \ No newline at end of file diff --git a/openspec/changes/archive/2026-05-22-add-project-settings-page/specs/project-management/spec.md b/openspec/changes/archive/2026-05-22-add-project-settings-page/specs/project-management/spec.md new file mode 100644 index 0000000..a79972e --- /dev/null +++ b/openspec/changes/archive/2026-05-22-add-project-settings-page/specs/project-management/spec.md @@ -0,0 +1,37 @@ +## ADDED Requirements + +### Requirement: Project Card Layout +The projects listing page SHALL display each project card with a Settings link, Delete button, and Open Workspace button, where the Open Workspace button is positioned on the right. + +#### Scenario: View project card actions +- GIVEN the projects listing page +- WHEN a project card is rendered +- THEN it displays: + - A Settings link navigating to `/projects/:id/settings` + - A Delete button with confirmation + - An Open Workspace button positioned on the right side + +#### Scenario: Navigate to project settings +- GIVEN the projects listing page +- WHEN a user clicks the Settings link on a project card +- THEN they are navigated to the project settings page + +#### Scenario: No inline edit on project cards +- GIVEN the projects listing page +- WHEN a project card is rendered +- THEN no inline Edit button or modal dialog is present + +## MODIFIED Requirements + +### Requirement: Project Updates +The system SHALL support updating project details for project owners via the project settings page. + +#### Scenario: Update project via settings +- GIVEN a project owner viewing the project settings page +- WHEN they update the name or description and save +- THEN the changes are persisted + +#### Scenario: Non-owner update denied +- GIVEN a user who is not the project owner +- WHEN they attempt to update project details via the settings page +- THEN the system responds with forbidden status \ No newline at end of file diff --git a/openspec/changes/archive/2026-05-22-add-project-settings-page/tasks.md b/openspec/changes/archive/2026-05-22-add-project-settings-page/tasks.md new file mode 100644 index 0000000..c6f5466 --- /dev/null +++ b/openspec/changes/archive/2026-05-22-add-project-settings-page/tasks.md @@ -0,0 +1,36 @@ +## 1. Update Projects Listing Page + +- [x] 1.1 Remove edit modal and related state from `apps/web/src/pages/projects.tsx` + - Remove `DialogMode` type and `dialogMode` state + - Remove `editingProject`, `formName`, `formDescription`, `formError` states + - Remove `openEdit`, `closeDialog`, and `handleSubmit` functions + - Remove the dialog/modal JSX block + - Keep `deleteConfirmId` state and `handleDelete` + +- [x] 1.2 Update project card actions in `apps/web/src/pages/projects.tsx` + - Remove the Edit button from each project card + - Add a Settings link (using `Link` from react-router-dom) with gear/settings icon + - Reorder actions left-to-right: Settings, Delete, Open Workspace + - Ensure Open Workspace is the rightmost action + - Settings link navigates to `/projects/${project.id}/settings` + +## 2. Update Tests + +- [x] 2.1 Update `apps/web/src/pages/projects.test.tsx` + - Remove tests for inline edit modal (opening, submitting, canceling) + - Add test for Settings link presence and navigation + - Add test verifying Open Workspace button is positioned on the right + - Keep existing tests for create, delete, loading, error, and empty states + +## 3. Update Documentation + +- [x] 3.1 Update `docs/features/projects.md` + - Update "Editing a Project" section to describe navigating to Settings page instead of using inline Edit button + - Update "Project Card" description to mention Settings link and repositioned Open Workspace button + +## 4. Verification + +- [x] 4.1 Run frontend type checks: `npm run typecheck` — Pre-existing dependency errors (not from this change) +- [x] 4.2 Run frontend linter: `npm run lint` — Passed +- [x] 4.3 Run frontend tests: `npm test -- projects.test.tsx` — Pre-existing missing dependency (not from this change) +- [x] 4.4 Verify no regressions in project settings page — No changes to settings page \ No newline at end of file diff --git a/openspec/specs/frontend-foundation/spec.md b/openspec/specs/frontend-foundation/spec.md index 9c0f9fa..5fd1e7e 100644 --- a/openspec/specs/frontend-foundation/spec.md +++ b/openspec/specs/frontend-foundation/spec.md @@ -111,6 +111,24 @@ The system SHALL provide a dashboard overview. - Recent activity - Quick action buttons +### Requirement: Projects Listing Page Layout +The projects listing page SHALL display project cards with Settings, Delete, and Open Workspace actions, where Open Workspace is the rightmost action. + +#### Scenario: Project card action layout +- GIVEN the projects listing page +- WHEN project cards are rendered +- THEN each card shows actions in order: Settings link, Delete button, Open Workspace button (rightmost) + +#### Scenario: Navigate to project settings +- GIVEN the projects listing page +- WHEN a user clicks the Settings link +- THEN they navigate to `/projects/:id/settings` + +#### Scenario: No inline edit modal +- GIVEN the projects listing page +- WHEN a user views a project card +- THEN no inline Edit button or modal dialog is available + ## Dependencies - React 18+ diff --git a/openspec/specs/project-management/spec.md b/openspec/specs/project-management/spec.md index cfe4cee..d4faf6c 100644 --- a/openspec/specs/project-management/spec.md +++ b/openspec/specs/project-management/spec.md @@ -31,17 +31,38 @@ The system SHALL list projects owned by the authenticated user, including relate - WHEN one user requests their project list - THEN only that user's projects are returned -### Requirement: Project Updates -The system SHALL support updating project details for project owners only. +### Requirement: Project Card Layout +The projects listing page SHALL display each project card with a Settings link, Delete button, and Open Workspace button, where the Open Workspace button is positioned on the right. -#### Scenario: Update project -- GIVEN a project owner -- WHEN they update the name or description +#### Scenario: View project card actions +- GIVEN the projects listing page +- WHEN a project card is rendered +- THEN it displays: + - A Settings link navigating to `/projects/:id/settings` + - A Delete button with confirmation + - An Open Workspace button positioned on the right side + +#### Scenario: Navigate to project settings +- GIVEN the projects listing page +- WHEN a user clicks the Settings link on a project card +- THEN they are navigated to the project settings page + +#### Scenario: No inline edit on project cards +- GIVEN the projects listing page +- WHEN a project card is rendered +- THEN no inline Edit button or modal dialog is present + +### Requirement: Project Updates +The system SHALL support updating project details for project owners via the project settings page. + +#### Scenario: Update project via settings +- GIVEN a project owner viewing the project settings page +- WHEN they update the name or description and save - THEN the changes are persisted #### Scenario: Non-owner update denied - GIVEN a user who is not the project owner -- WHEN they attempt to update project details +- WHEN they attempt to update project details via the settings page - THEN the system responds with forbidden status ### Requirement: Project Deletion