feat: add main features
Agents for developpement added + frontend add + backend added. Git viewer created + agent and template creator + layout and project creator
This commit is contained in:
103
frontend/src/features/projects/ProjectLauncher.tsx
Normal file
103
frontend/src/features/projects/ProjectLauncher.tsx
Normal file
@ -0,0 +1,103 @@
|
||||
/**
|
||||
* `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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user