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
This commit is contained in:
@@ -108,10 +108,8 @@ describe("ProjectsPage", () => {
|
|||||||
expect(screen.getByText(/project name is required/i)).toBeInTheDocument();
|
expect(screen.getByText(/project name is required/i)).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("opens edit dialog and saves changes", async () => {
|
it("renders settings link for each project", async () => {
|
||||||
const listMock = vi.spyOn(projectsApi, "listProjects").mockResolvedValue(mockProjects);
|
vi.spyOn(projectsApi, "listProjects").mockResolvedValue(mockProjects);
|
||||||
const updateMock = vi.spyOn(projectsApi, "updateProject").mockResolvedValue(mockProjects[0]);
|
|
||||||
|
|
||||||
render(<ProjectsPage />);
|
render(<ProjectsPage />);
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
@@ -121,20 +119,33 @@ describe("ProjectsPage", () => {
|
|||||||
const alphaCard = screen.getByText("Alpha Project").closest(".project-card") as HTMLElement | null;
|
const alphaCard = screen.getByText("Alpha Project").closest(".project-card") as HTMLElement | null;
|
||||||
if (!alphaCard) throw new Error("Card not found");
|
if (!alphaCard) throw new Error("Card not found");
|
||||||
|
|
||||||
fireEvent.click(within(alphaCard).getByRole("button", { name: /edit/i }));
|
const settingsLink = within(alphaCard).getByRole("link", { name: /settings/i });
|
||||||
expect(screen.getByRole("dialog")).toBeInTheDocument();
|
expect(settingsLink).toBeInTheDocument();
|
||||||
|
expect(settingsLink).toHaveAttribute("href", "/projects/proj-1/settings");
|
||||||
|
});
|
||||||
|
|
||||||
const nameInput = screen.getByDisplayValue("Alpha Project");
|
it("renders open workspace link as rightmost action", async () => {
|
||||||
fireEvent.change(nameInput, { target: { value: "Alpha Updated" } });
|
vi.spyOn(projectsApi, "listProjects").mockResolvedValue(mockProjects);
|
||||||
fireEvent.click(screen.getByRole("button", { name: /save/i }));
|
render(<ProjectsPage />);
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(updateMock).toHaveBeenCalledWith("proj-1", {
|
expect(screen.getByText("Alpha Project")).toBeInTheDocument();
|
||||||
name: "Alpha Updated",
|
|
||||||
description: "First project",
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
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 () => {
|
it("shows delete confirmation and deletes project", async () => {
|
||||||
|
|||||||
@@ -6,21 +6,17 @@ import {
|
|||||||
createProject,
|
createProject,
|
||||||
deleteProject,
|
deleteProject,
|
||||||
listProjects,
|
listProjects,
|
||||||
updateProject,
|
|
||||||
type ProjectCreateInput,
|
type ProjectCreateInput,
|
||||||
type ProjectUpdateInput,
|
|
||||||
} from "../api/projects";
|
} from "../api/projects";
|
||||||
import { Icon } from "../components/icon";
|
import { Icon } from "../components/icon";
|
||||||
import type { Project } from "../types";
|
import type { Project } from "../types";
|
||||||
|
|
||||||
type ProjectsStatus = "loading" | "ready" | "error";
|
type ProjectsStatus = "loading" | "ready" | "error";
|
||||||
type DialogMode = "none" | "create" | "edit";
|
|
||||||
|
|
||||||
export const ProjectsPage = () => {
|
export const ProjectsPage = () => {
|
||||||
const [status, setStatus] = useState<ProjectsStatus>("loading");
|
const [status, setStatus] = useState<ProjectsStatus>("loading");
|
||||||
const [projects, setProjects] = useState<Project[]>([]);
|
const [projects, setProjects] = useState<Project[]>([]);
|
||||||
const [dialogMode, setDialogMode] = useState<DialogMode>("none");
|
const [showCreate, setShowCreate] = useState(false);
|
||||||
const [editingProject, setEditingProject] = useState<Project | null>(null);
|
|
||||||
const [formName, setFormName] = useState("");
|
const [formName, setFormName] = useState("");
|
||||||
const [formDescription, setFormDescription] = useState("");
|
const [formDescription, setFormDescription] = useState("");
|
||||||
const [formError, setFormError] = useState<string | null>(null);
|
const [formError, setFormError] = useState<string | null>(null);
|
||||||
@@ -46,25 +42,15 @@ export const ProjectsPage = () => {
|
|||||||
setFormName("");
|
setFormName("");
|
||||||
setFormDescription("");
|
setFormDescription("");
|
||||||
setFormError(null);
|
setFormError(null);
|
||||||
setEditingProject(null);
|
setShowCreate(true);
|
||||||
setDialogMode("create");
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const openEdit = (project: Project) => {
|
const closeCreate = () => {
|
||||||
setFormName(project.name);
|
setShowCreate(false);
|
||||||
setFormDescription(project.description ?? "");
|
|
||||||
setFormError(null);
|
|
||||||
setEditingProject(project);
|
|
||||||
setDialogMode("edit");
|
|
||||||
};
|
|
||||||
|
|
||||||
const closeDialog = () => {
|
|
||||||
setDialogMode("none");
|
|
||||||
setEditingProject(null);
|
|
||||||
setFormError(null);
|
setFormError(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSubmit = async (e: React.FormEvent) => {
|
const handleCreate = async (e: React.FormEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setFormError(null);
|
setFormError(null);
|
||||||
|
|
||||||
@@ -74,20 +60,12 @@ export const ProjectsPage = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (dialogMode === "create") {
|
const input: ProjectCreateInput = {
|
||||||
const input: ProjectCreateInput = {
|
name: formName.trim(),
|
||||||
name: formName.trim(),
|
description: formDescription.trim() || null,
|
||||||
description: formDescription.trim() || null,
|
};
|
||||||
};
|
await createProject(input);
|
||||||
await createProject(input);
|
closeCreate();
|
||||||
} else if (dialogMode === "edit" && editingProject) {
|
|
||||||
const input: ProjectUpdateInput = {
|
|
||||||
name: formName.trim(),
|
|
||||||
description: formDescription.trim() || null,
|
|
||||||
};
|
|
||||||
await updateProject(editingProject.id, input);
|
|
||||||
}
|
|
||||||
closeDialog();
|
|
||||||
await loadProjects();
|
await loadProjects();
|
||||||
} catch {
|
} catch {
|
||||||
setFormError("Failed to save project");
|
setFormError("Failed to save project");
|
||||||
@@ -139,17 +117,13 @@ export const ProjectsPage = () => {
|
|||||||
{project.description && <p className="muted">{project.description}</p>}
|
{project.description && <p className="muted">{project.description}</p>}
|
||||||
</div>
|
</div>
|
||||||
<div className="project-actions">
|
<div className="project-actions">
|
||||||
<Link className="ghost-button" to={`/projects/${project.id}`}>
|
<Link
|
||||||
Open Workspace
|
|
||||||
</Link>
|
|
||||||
<button
|
|
||||||
className="ghost-button"
|
className="ghost-button"
|
||||||
onClick={() => openEdit(project)}
|
to={`/projects/${project.id}/settings`}
|
||||||
type="button"
|
|
||||||
>
|
>
|
||||||
<Icon name="edit" size="sm" />
|
<Icon name="settings" size="sm" />
|
||||||
Edit
|
Settings
|
||||||
</button>
|
</Link>
|
||||||
{deleteConfirmId === project.id ? (
|
{deleteConfirmId === project.id ? (
|
||||||
<div className="delete-confirm">
|
<div className="delete-confirm">
|
||||||
<span>Are you sure?</span>
|
<span>Are you sure?</span>
|
||||||
@@ -180,17 +154,20 @@ export const ProjectsPage = () => {
|
|||||||
Delete
|
Delete
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
<Link className="ghost-button" to={`/projects/${project.id}`}>
|
||||||
|
Open Workspace
|
||||||
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{dialogMode !== "none" && (
|
{showCreate && (
|
||||||
<div className="dialog-overlay" role="dialog" aria-modal="true">
|
<div className="dialog-overlay" role="dialog" aria-modal="true">
|
||||||
<div className="dialog">
|
<div className="dialog">
|
||||||
<h2>{dialogMode === "create" ? "Create Project" : "Edit Project"}</h2>
|
<h2>Create Project</h2>
|
||||||
<form onSubmit={handleSubmit} className="stack">
|
<form onSubmit={handleCreate} className="stack">
|
||||||
<label className="form-field">
|
<label className="form-field">
|
||||||
Name
|
Name
|
||||||
<input
|
<input
|
||||||
@@ -211,22 +188,13 @@ export const ProjectsPage = () => {
|
|||||||
</label>
|
</label>
|
||||||
{formError && <p className="error-text">{formError}</p>}
|
{formError && <p className="error-text">{formError}</p>}
|
||||||
<div className="dialog-actions">
|
<div className="dialog-actions">
|
||||||
<button className="secondary-button" onClick={closeDialog} type="button">
|
<button className="secondary-button" onClick={closeCreate} type="button">
|
||||||
<Icon name="cancel" size="sm" />
|
<Icon name="cancel" size="sm" />
|
||||||
Cancel
|
Cancel
|
||||||
</button>
|
</button>
|
||||||
<button className="primary-button" type="submit">
|
<button className="primary-button" type="submit">
|
||||||
{dialogMode === "create" ? (
|
<Icon name="add" size="sm" />
|
||||||
<>
|
Create
|
||||||
<Icon name="add" size="sm" />
|
|
||||||
Create
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<Icon name="save" size="sm" />
|
|
||||||
Save
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -22,25 +22,30 @@ The Projects page displays all your projects in a card layout showing:
|
|||||||
- Creation date
|
- Creation date
|
||||||
- Associated repositories count
|
- 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
|
### 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
|
- Repository file browser
|
||||||
- Branch selector
|
- Branch selector
|
||||||
- File viewer
|
- File viewer
|
||||||
|
|
||||||
### Editing a Project
|
### Editing a Project
|
||||||
|
|
||||||
1. From the Projects page, click the **menu icon** (⋮) on a project card
|
1. From the Projects page, click the **"Settings"** link on a project card
|
||||||
2. Select **"Edit"**
|
2. On the project settings page, update the **name** or **description**
|
||||||
3. Update the name or description
|
3. Click **"Save Changes"**
|
||||||
4. Click **"Save"**
|
|
||||||
|
The settings page also provides access to repository management and member settings.
|
||||||
|
|
||||||
### Deleting a Project
|
### Deleting a Project
|
||||||
|
|
||||||
1. From the Projects page, click the **menu icon** (⋮) on a project card
|
1. From the Projects page, click the **"Delete"** button on a project card
|
||||||
2. Select **"Delete"**
|
2. Confirm the deletion
|
||||||
3. Confirm the deletion
|
|
||||||
|
|
||||||
**Note:** Deleting a project also deletes all associated repositories and their data. This action cannot be undone.
|
**Note:** Deleting a project also deletes all associated repositories and their data. This action cannot be undone.
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
schema: spec-driven
|
||||||
|
created: 2026-05-22
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
+19
@@ -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
|
||||||
+37
@@ -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
|
||||||
@@ -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
|
||||||
@@ -111,6 +111,24 @@ The system SHALL provide a dashboard overview.
|
|||||||
- Recent activity
|
- Recent activity
|
||||||
- Quick action buttons
|
- 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
|
## Dependencies
|
||||||
|
|
||||||
- React 18+
|
- React 18+
|
||||||
|
|||||||
@@ -31,17 +31,38 @@ The system SHALL list projects owned by the authenticated user, including relate
|
|||||||
- WHEN one user requests their project list
|
- WHEN one user requests their project list
|
||||||
- THEN only that user's projects are returned
|
- THEN only that user's projects are returned
|
||||||
|
|
||||||
### Requirement: Project Updates
|
### Requirement: Project Card Layout
|
||||||
The system SHALL support updating project details for project owners only.
|
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
|
#### Scenario: View project card actions
|
||||||
- GIVEN a project owner
|
- GIVEN the projects listing page
|
||||||
- WHEN they update the name or description
|
- 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
|
- THEN the changes are persisted
|
||||||
|
|
||||||
#### Scenario: Non-owner update denied
|
#### Scenario: Non-owner update denied
|
||||||
- GIVEN a user who is not the project owner
|
- 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
|
- THEN the system responds with forbidden status
|
||||||
|
|
||||||
### Requirement: Project Deletion
|
### Requirement: Project Deletion
|
||||||
|
|||||||
Reference in New Issue
Block a user