import { Link } from "@/lib/router"; import type { ExecutionWorkspace, Issue } from "@paperclipai/shared"; import { Button } from "@/components/ui/button"; import { CopyText } from "./CopyText"; import { IssuesQuicklook } from "./IssuesQuicklook"; import type { ProjectWorkspaceSummary } from "../lib/project-workspaces-tab"; import { cn, projectWorkspaceUrl } from "../lib/utils"; import { timeAgo } from "../lib/timeAgo"; import { Copy, ExternalLink, FolderOpen, GitBranch, Loader2, Play, Square } from "lucide-react"; function workspaceKindLabel(kind: ProjectWorkspaceSummary["kind"]) { return kind === "execution_workspace" ? "Execution workspace" : "Project workspace"; } function truncatePath(path: string) { const parts = path.split("/").filter(Boolean); if (parts.length <= 3) return path; return `…/${parts.slice(-3).join("/")}`; } interface ProjectWorkspaceSummaryCardProps { projectRef: string; summary: ProjectWorkspaceSummary; runtimeActionKey: string | null; runtimeActionPending: boolean; onRuntimeAction: (input: { key: string; kind: "project_workspace" | "execution_workspace"; workspaceId: string; action: "start" | "stop" | "restart"; }) => void; onCloseWorkspace: (input: { id: string; name: string; status: ExecutionWorkspace["status"]; }) => void; } export function ProjectWorkspaceSummaryCard({ projectRef, summary, runtimeActionKey, runtimeActionPending, onRuntimeAction, onCloseWorkspace, }: ProjectWorkspaceSummaryCardProps) { const visibleIssues = summary.issues.slice(0, 4); const hiddenIssueCount = Math.max(summary.issues.length - visibleIssues.length, 0); const workspaceHref = summary.kind === "project_workspace" ? projectWorkspaceUrl({ id: projectRef, urlKey: projectRef }, summary.workspaceId) : `/execution-workspaces/${summary.workspaceId}`; const hasRunningServices = summary.runningServiceCount > 0; const actionKey = `${summary.key}:${hasRunningServices ? "stop" : "start"}`; return (
{workspaceKindLabel(summary.kind)} Updated {timeAgo(summary.lastUpdatedAt)} {summary.serviceCount > 0 ? ( {summary.runningServiceCount}/{summary.serviceCount} services ) : null} {summary.executionWorkspaceStatus ? ( {summary.executionWorkspaceStatus.replace(/_/g, " ")} ) : null}
{summary.workspaceName}
{summary.hasRuntimeConfig ? ( ) : null} {summary.kind === "execution_workspace" && summary.executionWorkspaceId && summary.executionWorkspaceStatus ? ( ) : null}
{summary.branchName ? (
Branch
{summary.branchName}
) : null} {summary.cwd ? (
Path
{truncatePath(summary.cwd)}
) : null} {summary.primaryServiceUrl ? (
) : null}
{summary.issues.length > 0 ? (
Linked issues
{visibleIssues.map((issue) => ( ))} {hiddenIssueCount > 0 ? ( +{hiddenIssueCount} more ) : null}
) : null}
); } function IssuePill({ issue }: { issue: Issue }) { return ( {issue.identifier ?? issue.id.slice(0, 8)} ); }