fix: allow workspace creation from workspaces page
- Replace awkward first-workspace-guessing logic with inline project/repo selector - New WorkspaceCreateInline component with cascading dropdowns: - Select project → loads repositories for that project - Select repository → enter workspace name + branch - Submit creates workspace via top-level POST /workspaces/ - Add createWorkspaceTopLevel() API client for flat endpoint - Works even with zero existing workspaces (shows create button in empty state) - Add CSS grid layout for inline create form - TypeScript + eslint clean
This commit is contained in:
@@ -15,7 +15,11 @@ import {
|
||||
deleteWorkspace,
|
||||
syncWorkspace,
|
||||
} from "../api/workspaces";
|
||||
import { EmptyState, ErrorState, LoadingState } from "../components/data-states";
|
||||
import {
|
||||
EmptyState,
|
||||
ErrorState,
|
||||
LoadingState,
|
||||
} from "../components/data-states";
|
||||
import { Icon } from "../components/icon";
|
||||
import { WorkspaceCreateForm } from "../components/workspace-create-form";
|
||||
import { useAsyncData } from "../hooks/use-async-data";
|
||||
@@ -24,10 +28,11 @@ import type { ProjectWithRepos, WorkspaceSummary } from "../types";
|
||||
type DialogMode = "none" | "create" | "edit";
|
||||
|
||||
export const ProjectsPage = () => {
|
||||
const { data: projects, status, reload } = useAsyncData<ProjectWithRepos[]>(
|
||||
listProjects,
|
||||
[],
|
||||
);
|
||||
const {
|
||||
data: projects,
|
||||
status,
|
||||
reload,
|
||||
} = useAsyncData<ProjectWithRepos[]>(listProjects, []);
|
||||
const [dialogMode, setDialogMode] = useState<DialogMode>("none");
|
||||
const [editingProject, setEditingProject] = useState<ProjectWithRepos | null>(
|
||||
null,
|
||||
@@ -203,11 +208,7 @@ export const ProjectsPage = () => {
|
||||
if (action === "sync") {
|
||||
void handleSyncWorkspace(project.id, repoId, workspace);
|
||||
} else if (action === "delete") {
|
||||
void handleDeleteWorkspace(
|
||||
project.id,
|
||||
repoId,
|
||||
workspace,
|
||||
);
|
||||
void handleDeleteWorkspace(project.id, repoId, workspace);
|
||||
}
|
||||
}}
|
||||
workspaceLoading={workspaceLoading}
|
||||
@@ -317,7 +318,10 @@ function ProjectCard({
|
||||
workspaceLoading: string | null;
|
||||
onCancelCreate: () => void;
|
||||
showCreateForm: string | null;
|
||||
onSubmitCreate: (repoId: string, data: { name: string; branch: string }) => Promise<void>;
|
||||
onSubmitCreate: (
|
||||
repoId: string,
|
||||
data: { name: string; branch: string },
|
||||
) => Promise<void>;
|
||||
}) {
|
||||
return (
|
||||
<article className="card project-card">
|
||||
@@ -328,10 +332,7 @@ function ProjectCard({
|
||||
type="button"
|
||||
aria-expanded={expanded}
|
||||
>
|
||||
<Icon
|
||||
name={expanded ? "chevron-down" : "chevron-right"}
|
||||
size="sm"
|
||||
/>
|
||||
<Icon name={expanded ? "chevron-down" : "chevron-right"} size="sm" />
|
||||
<h3>{project.name}</h3>
|
||||
{project.repositories.length > 0 && (
|
||||
<span className="repo-count">
|
||||
@@ -400,9 +401,7 @@ function ProjectCard({
|
||||
<WorkspaceCreateForm
|
||||
projectId={project.id}
|
||||
repoId={repo.id}
|
||||
onSubmit={(data) =>
|
||||
onSubmitCreate(repo.id, data)
|
||||
}
|
||||
onSubmit={(data) => onSubmitCreate(repo.id, data)}
|
||||
onCancel={onCancelCreate}
|
||||
/>
|
||||
)}
|
||||
@@ -428,15 +427,9 @@ function ProjectCard({
|
||||
<div className="ws-actions">
|
||||
<button
|
||||
type="button"
|
||||
disabled={
|
||||
workspaceLoading === ws.id
|
||||
}
|
||||
disabled={workspaceLoading === ws.id}
|
||||
onClick={() =>
|
||||
onWorkspaceAction(
|
||||
repo.id,
|
||||
ws,
|
||||
"sync",
|
||||
)
|
||||
onWorkspaceAction(repo.id, ws, "sync")
|
||||
}
|
||||
>
|
||||
<Icon name="refresh" size="sm" />
|
||||
@@ -444,15 +437,9 @@ function ProjectCard({
|
||||
<button
|
||||
type="button"
|
||||
className="danger-text"
|
||||
disabled={
|
||||
workspaceLoading === ws.id
|
||||
}
|
||||
disabled={workspaceLoading === ws.id}
|
||||
onClick={() =>
|
||||
onWorkspaceAction(
|
||||
repo.id,
|
||||
ws,
|
||||
"delete",
|
||||
)
|
||||
onWorkspaceAction(repo.id, ws, "delete")
|
||||
}
|
||||
>
|
||||
<Icon name="delete" size="sm" />
|
||||
|
||||
Reference in New Issue
Block a user