Teams UI: deploy scope selector + TeamWizard + Team page
The "+" deploy flow now opens a scope selector (Single / Team / Company-soon / Org-soon). Single = the existing CreateClawForm. Team = TeamWizard: pick a baseline topology (from the catalog + role distribution), set size, auto-staff editable claw cards (name / role / model / persona), preview the topology graph, then POST /api/teams → land on the Team page. Team page (/teams/[id]): topology SVG + claw roster (each links to its chat) + a Run panel that drives the team on the durable runner with live SSE progress (reuses the topology-run streaming). /teams list page + a Teams nav entry. lint + typecheck + next build clean. Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
8123a27bcf
commit
bba18a4687
@@ -1,17 +1,15 @@
|
||||
import { CreateClawForm } from "@/components/wizard/CreateClawForm";
|
||||
import { DeployWizard } from "@/components/wizard/DeployWizard";
|
||||
|
||||
export default function NewClawPage() {
|
||||
return (
|
||||
<section className="flex min-h-dvh flex-col items-center justify-center gap-6 px-4 motion-safe:animate-[fade-up_var(--duration-normal)_var(--ease-app)]">
|
||||
<div className="text-center">
|
||||
<h1 className="text-2xl font-semibold tracking-tight">
|
||||
Create your Claw
|
||||
</h1>
|
||||
<h1 className="text-2xl font-semibold tracking-tight">Deploy</h1>
|
||||
<p className="pt-1 text-sm text-muted-foreground">
|
||||
Claws help get your work done.
|
||||
A single claw, or a whole team — pick your scale.
|
||||
</p>
|
||||
</div>
|
||||
<CreateClawForm />
|
||||
<DeployWizard />
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
import { TeamView } from "@/components/team/TeamView";
|
||||
|
||||
export default async function TeamPage({ params }: { params: Promise<{ id: string }> }) {
|
||||
const { id } = await params;
|
||||
return <TeamView teamId={id} />;
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import Link from "next/link";
|
||||
|
||||
interface TeamSummary {
|
||||
id: string;
|
||||
name: string;
|
||||
kind: string;
|
||||
status: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export default function TeamsPage() {
|
||||
const [teams, setTeams] = useState<TeamSummary[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
void (async () => {
|
||||
try {
|
||||
const r = await fetch("/api/teams");
|
||||
if (r.ok) setTeams((await r.json()) as TeamSummary[]);
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
})();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="mx-auto flex w-full max-w-3xl flex-col gap-4 p-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<h1 className="text-xl font-semibold tracking-tight">Teams</h1>
|
||||
<Link
|
||||
href="/claws/new"
|
||||
className="rounded-full bg-coral px-4 py-2 text-sm font-medium text-white"
|
||||
>
|
||||
Deploy a team
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{teams.length === 0 ? (
|
||||
<p className="text-sm text-muted-foreground">
|
||||
No teams yet — deploy a baseline topology staffed with claws.
|
||||
</p>
|
||||
) : (
|
||||
<ul className="flex flex-col gap-2">
|
||||
{teams.map((t) => (
|
||||
<li key={t.id}>
|
||||
<Link
|
||||
href={`/teams/${t.id}`}
|
||||
className="flex items-center gap-3 rounded-lg border border-border p-3 hover:bg-muted/30"
|
||||
>
|
||||
<span className="text-sm font-medium text-foreground">{t.name}</span>
|
||||
<span className="rounded-full bg-muted px-2 py-0.5 text-xs capitalize text-muted-foreground">
|
||||
{t.kind}
|
||||
</span>
|
||||
<span className="text-xs text-muted-foreground">{t.status}</span>
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user