fix: add top-level GET /workspaces endpoint and derive project/repo from workspace data
- Add all_workspaces_router with GET /workspaces/ (no project/repo required) - Include project_id in workspace responses - Frontend: useWorkspaces() calls listAllWorkspaces when no args - Frontend: WorkspacesPage uses top-level list, derives project/repo from workspace for mutations - Fixes 422 from invalid UUID path params
This commit is contained in:
@@ -22,6 +22,11 @@ export async function listWorkspaces(
|
||||
return response.data;
|
||||
}
|
||||
|
||||
export async function listAllWorkspaces(): Promise<Workspace[]> {
|
||||
const response = await apiClient.get<Workspace[]>("/workspaces/");
|
||||
return response.data;
|
||||
}
|
||||
|
||||
export async function createWorkspace(
|
||||
projectId: string,
|
||||
repoId: string,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/** Hook for fetching workspaces. */
|
||||
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { listWorkspaces } from "../api/workspaces";
|
||||
import { listAllWorkspaces, listWorkspaces } from "../api/workspaces";
|
||||
import type { Workspace } from "../types/workspace";
|
||||
|
||||
export interface UseWorkspacesResult {
|
||||
@@ -12,8 +12,8 @@ export interface UseWorkspacesResult {
|
||||
}
|
||||
|
||||
export function useWorkspaces(
|
||||
projectId: string,
|
||||
repoId: string,
|
||||
projectId?: string,
|
||||
repoId?: string,
|
||||
): UseWorkspacesResult {
|
||||
const [workspaces, setWorkspaces] = useState<Workspace[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
@@ -23,7 +23,10 @@ export function useWorkspaces(
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const data = await listWorkspaces(projectId, repoId);
|
||||
const data =
|
||||
projectId && repoId
|
||||
? await listWorkspaces(projectId, repoId)
|
||||
: await listAllWorkspaces();
|
||||
setWorkspaces(data);
|
||||
} catch (err) {
|
||||
setError(
|
||||
|
||||
@@ -13,29 +13,25 @@ import type { Workspace } from "../types/workspace";
|
||||
export function WorkspacesPage() {
|
||||
const [showCreate, setShowCreate] = useState(false);
|
||||
const [startWorkspace, setStartWorkspace] = useState<Workspace | null>(null);
|
||||
const [createTarget, setCreateTarget] = useState<{ projectId: string; repoId: string } | null>(null);
|
||||
|
||||
// TODO: Get projectId and repoId from URL params or context
|
||||
const projectId = "default-project";
|
||||
const repoId = "default-repo";
|
||||
|
||||
const { workspaces, loading, error, refresh } = useWorkspaces(
|
||||
projectId,
|
||||
repoId,
|
||||
);
|
||||
const { workspaces, loading, error, refresh } = useWorkspaces();
|
||||
const actions = useWorkspaceActions();
|
||||
|
||||
const handleCreate = async (data: { name: string; branch: string }) => {
|
||||
await actions.create(projectId, repoId, data);
|
||||
if (!createTarget) return;
|
||||
await actions.create(createTarget.projectId, createTarget.repoId, data);
|
||||
setShowCreate(false);
|
||||
setCreateTarget(null);
|
||||
await refresh();
|
||||
};
|
||||
|
||||
const handleDelete = async (workspace: Workspace) => {
|
||||
await actions.delete(projectId, repoId, workspace, refresh);
|
||||
await actions.delete(workspace.project_id, workspace.repo_id, workspace, refresh);
|
||||
};
|
||||
|
||||
const handleSync = async (workspace: Workspace) => {
|
||||
await actions.sync(projectId, repoId, workspace, refresh);
|
||||
await actions.sync(workspace.project_id, workspace.repo_id, workspace, refresh);
|
||||
};
|
||||
|
||||
const handleStartTool = async (
|
||||
@@ -45,8 +41,8 @@ export function WorkspacesPage() {
|
||||
if (!startWorkspace) return;
|
||||
try {
|
||||
const instance = await createInstance(
|
||||
projectId,
|
||||
repoId,
|
||||
startWorkspace.project_id,
|
||||
startWorkspace.repo_id,
|
||||
toolTypeId,
|
||||
`${startWorkspace.name} - ${toolTypeId}`,
|
||||
undefined,
|
||||
@@ -56,7 +52,7 @@ export function WorkspacesPage() {
|
||||
[],
|
||||
startWorkspace.id,
|
||||
);
|
||||
await startInstance(projectId, repoId, instance.id, configProfileId);
|
||||
await startInstance(startWorkspace.project_id, startWorkspace.repo_id, instance.id, configProfileId);
|
||||
setStartWorkspace(null);
|
||||
await refresh();
|
||||
} catch (err) {
|
||||
@@ -76,23 +72,31 @@ export function WorkspacesPage() {
|
||||
>
|
||||
<Icon name="refresh" size="sm" />
|
||||
</button>
|
||||
<button
|
||||
className="btn btn-primary"
|
||||
onClick={() => setShowCreate(true)}
|
||||
>
|
||||
<Icon name="add" size="sm" /> New Workspace
|
||||
</button>
|
||||
<button
|
||||
className="btn btn-primary"
|
||||
onClick={() => {
|
||||
if (workspaces.length > 0) {
|
||||
const first = workspaces[0];
|
||||
setCreateTarget({ projectId: first.project_id, repoId: first.repo_id });
|
||||
setShowCreate(true);
|
||||
} else {
|
||||
alert("Navigate to a project to create your first workspace.");
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Icon name="add" size="sm" /> New Workspace
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{error && <div className="alert alert-error">{error}</div>}
|
||||
|
||||
{showCreate && (
|
||||
{showCreate && createTarget && (
|
||||
<WorkspaceCreateForm
|
||||
projectId={projectId}
|
||||
repoId={repoId}
|
||||
projectId={createTarget.projectId}
|
||||
repoId={createTarget.repoId}
|
||||
onSubmit={handleCreate}
|
||||
onCancel={() => setShowCreate(false)}
|
||||
onCancel={() => { setShowCreate(false); setCreateTarget(null); }}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -101,12 +105,7 @@ export function WorkspacesPage() {
|
||||
) : workspaces.length === 0 ? (
|
||||
<div className="empty-state">
|
||||
<p>No workspaces yet.</p>
|
||||
<button
|
||||
className="btn btn-primary"
|
||||
onClick={() => setShowCreate(true)}
|
||||
>
|
||||
<Icon name="add" size="sm" /> Create your first workspace
|
||||
</button>
|
||||
<p>Navigate to a project to create your first workspace.</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="workspaces-grid">
|
||||
|
||||
@@ -5,6 +5,7 @@ export interface Workspace {
|
||||
name: string;
|
||||
repo_id: string;
|
||||
repo_name: string;
|
||||
project_id: string;
|
||||
project_name: string;
|
||||
user_id: string;
|
||||
branch: string;
|
||||
|
||||
Reference in New Issue
Block a user