4913cc7297
- Move the project name below the workspace title row so it reads as a distinct line with a project icon. - Move the status badge into the title row next to the workspace name, preventing it from crowding the project label. - Add .workspace-title-row flex styles and update .workspace-project-name to display inline-flex with a brand-colored project icon. Quality gates: npm run typecheck, npm run lint clean, npm test -- --run 87 passed.
102 lines
2.6 KiB
TypeScript
102 lines
2.6 KiB
TypeScript
/** Card component for displaying a workspace. */
|
|
|
|
import { Link } from "react-router-dom";
|
|
import { Icon } from "../../icon";
|
|
import { WorkspaceInstanceChips } from "./workspace-instance-chips";
|
|
import type { Workspace } from "../../../types/workspace";
|
|
|
|
export interface WorkspaceCardProps {
|
|
workspace: Workspace;
|
|
loading?: boolean;
|
|
onStartTool: (workspace: Workspace) => void;
|
|
onSync: (workspace: Workspace) => void;
|
|
onDelete: (workspace: Workspace) => void;
|
|
}
|
|
|
|
export function WorkspaceCard({
|
|
workspace,
|
|
loading = false,
|
|
onStartTool,
|
|
onSync,
|
|
onDelete,
|
|
}: WorkspaceCardProps) {
|
|
const statusClass =
|
|
workspace.status === "ready"
|
|
? "status-ready"
|
|
: workspace.status === "syncing"
|
|
? "status-syncing"
|
|
: "status-error";
|
|
|
|
return (
|
|
<article className={`card workspace-card ${loading ? "loading" : ""}`}>
|
|
<div className="workspace-card-top">
|
|
<Link
|
|
to={`/workspaces/${workspace.id}`}
|
|
className="workspace-title-link"
|
|
>
|
|
<div className="workspace-title-stack">
|
|
<div className="workspace-title-row">
|
|
<h4 className="workspace-name">{workspace.name}</h4>
|
|
<span className={`status-badge ${statusClass}`}>
|
|
{workspace.status}
|
|
</span>
|
|
</div>
|
|
<span className="workspace-project-name">
|
|
<Icon name="projects" size="sm" />
|
|
{workspace.project_name}
|
|
</span>
|
|
</div>
|
|
</Link>
|
|
</div>
|
|
|
|
<div className="workspace-card-body">
|
|
<div className="workspace-meta-row">
|
|
<span className="workspace-repo">
|
|
<Icon name="repositories" size="sm" />
|
|
{workspace.repo_name}
|
|
</span>
|
|
<span className="workspace-branch">
|
|
<Icon name="branch" size="sm" />
|
|
{workspace.branch}
|
|
</span>
|
|
{workspace.instance_count > 0 && (
|
|
<span className="workspace-instance-count">
|
|
{workspace.instance_count} tool
|
|
{workspace.instance_count > 1 ? "s" : ""}
|
|
</span>
|
|
)}
|
|
</div>
|
|
<WorkspaceInstanceChips workspaceId={workspace.id} />
|
|
</div>
|
|
|
|
<div className="workspace-actions">
|
|
<button
|
|
className="btn btn-primary"
|
|
onClick={() => onStartTool(workspace)}
|
|
disabled={loading}
|
|
title="Start a tool in this workspace"
|
|
>
|
|
<Icon name="play" size="sm" />
|
|
Start Tool
|
|
</button>
|
|
<button
|
|
className="ghost-button"
|
|
onClick={() => onSync(workspace)}
|
|
disabled={loading}
|
|
title="Sync workspace"
|
|
>
|
|
<Icon name="refresh" size="sm" />
|
|
</button>
|
|
<button
|
|
className="ghost-button danger-text"
|
|
onClick={() => onDelete(workspace)}
|
|
disabled={loading}
|
|
title="Delete workspace"
|
|
>
|
|
<Icon name="delete" size="sm" />
|
|
</button>
|
|
</div>
|
|
</article>
|
|
);
|
|
}
|