Agents for developpement added + frontend add + backend added. Git viewer created + agent and template creator + layout and project creator
104 lines
3.1 KiB
TypeScript
104 lines
3.1 KiB
TypeScript
/**
|
|
* `ProjectLauncher` — the empty-state screen shown when no project tab is open.
|
|
*
|
|
* Renders the project creation form and the known-projects list centred in the
|
|
* available space. All behaviour comes from the `vm` prop (a `useProjects`
|
|
* view-model slice); no hooks called here — presentation only.
|
|
*/
|
|
|
|
import type { Project } from "@/domain";
|
|
import { Button, Input, Panel } from "@/shared";
|
|
|
|
export interface ProjectLauncherProps {
|
|
/** Form field values */
|
|
name: string;
|
|
root: string;
|
|
onNameChange: (v: string) => void;
|
|
onRootChange: (v: string) => void;
|
|
onSubmit: (e: React.FormEvent) => void;
|
|
canCreate: boolean;
|
|
busy: boolean;
|
|
onRefresh: () => void;
|
|
projects: Project[];
|
|
onOpen: (id: string) => void;
|
|
error?: string | null;
|
|
}
|
|
|
|
export function ProjectLauncher({
|
|
name,
|
|
root,
|
|
onNameChange,
|
|
onRootChange,
|
|
onSubmit,
|
|
canCreate,
|
|
busy,
|
|
onRefresh,
|
|
projects,
|
|
onOpen,
|
|
error,
|
|
}: ProjectLauncherProps) {
|
|
return (
|
|
<div className="flex flex-1 items-start justify-center p-8">
|
|
<div className="flex w-full max-w-2xl flex-col gap-6">
|
|
{error && (
|
|
<p
|
|
role="alert"
|
|
className="rounded-md border border-danger/40 bg-danger/10 px-3 py-2 text-sm text-danger"
|
|
>
|
|
{error}
|
|
</p>
|
|
)}
|
|
|
|
<div className="flex flex-col gap-3">
|
|
<h2 className="text-base font-semibold text-content">New project</h2>
|
|
<form onSubmit={onSubmit} className="flex flex-wrap items-center gap-2">
|
|
<Input
|
|
aria-label="project name"
|
|
placeholder="Project name"
|
|
value={name}
|
|
onChange={(e) => onNameChange(e.target.value)}
|
|
className="w-48"
|
|
/>
|
|
<Input
|
|
aria-label="project root"
|
|
placeholder="/absolute/project/root"
|
|
value={root}
|
|
onChange={(e) => onRootChange(e.target.value)}
|
|
className="min-w-80 flex-1"
|
|
/>
|
|
<Button type="submit" variant="primary" disabled={!canCreate}>
|
|
Create project
|
|
</Button>
|
|
<Button type="button" onClick={onRefresh} disabled={busy}>
|
|
Refresh
|
|
</Button>
|
|
</form>
|
|
</div>
|
|
|
|
<Panel title="Known projects">
|
|
{projects.length === 0 ? (
|
|
<p className="text-sm text-muted">No projects yet.</p>
|
|
) : (
|
|
<ul className="flex flex-col divide-y divide-border">
|
|
{projects.map((p) => (
|
|
<li
|
|
key={p.id}
|
|
className="flex items-center justify-between gap-3 py-2 first:pt-0 last:pb-0"
|
|
>
|
|
<span className="flex min-w-0 items-baseline gap-2">
|
|
<span className="font-medium text-content">{p.name}</span>
|
|
<code className="truncate text-xs text-muted">{p.root}</code>
|
|
</span>
|
|
<Button size="sm" onClick={() => onOpen(p.id)}>
|
|
Open
|
|
</Button>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
)}
|
|
</Panel>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|