Files

131 lines
4.4 KiB
TypeScript

"use client";
import { useState, type FormEvent } from "react";
import { Check, ChevronRight, GitBranch, Loader2, Terminal } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
type CreationResult = {
name: string;
path: string;
gitInitialized: boolean;
};
export function ProjectCreator({ projectsRoot }: { projectsRoot: string }) {
const [name, setName] = useState("");
const [isCreating, setIsCreating] = useState(false);
const [error, setError] = useState("");
const [createdProject, setCreatedProject] = useState<CreationResult | null>(null);
async function handleSubmit(event: FormEvent<HTMLFormElement>) {
event.preventDefault();
setIsCreating(true);
setError("");
setCreatedProject(null);
try {
const response = await fetch("/api/projects", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name }),
});
const data = await response.json();
if (!response.ok) {
setError(data.error || "The project could not be created.");
return;
}
setCreatedProject(data);
setName("");
} catch {
setError("Could not reach the local server. Try again.");
} finally {
setIsCreating(false);
}
}
return (
<section className="rounded-xl border border-stone-300 bg-[hsl(var(--card))] p-6 shadow-[0_24px_70px_-38px_rgba(28,25,23,0.45)] sm:p-8">
<div className="mb-8 flex items-start justify-between gap-5">
<div>
<div className="mb-4 flex h-10 w-10 items-center justify-center rounded-lg bg-orange-100 text-orange-700">
<Terminal size={19} />
</div>
<h2 className="text-xl font-semibold tracking-[-0.025em] text-stone-950">
New project
</h2>
<p className="mt-1.5 text-sm leading-6 text-stone-500">
A directory, plus a Git repository.
</p>
</div>
<span className="font-mono text-xs text-stone-400">01 / 01</span>
</div>
<form onSubmit={handleSubmit} className="space-y-6">
<div className="space-y-2.5">
<Label htmlFor="project-name">Project name</Label>
<Input
id="project-name"
name="name"
value={name}
onChange={(event) => setName(event.target.value)}
placeholder="e.g. morning-star"
autoComplete="off"
autoFocus
disabled={isCreating}
aria-describedby="project-name-help"
/>
<p id="project-name-help" className="text-xs leading-5 text-stone-500">
Letters, numbers, spaces, hyphens, and underscores.
</p>
</div>
<div className="rounded-lg border border-dashed border-stone-300 bg-stone-50/70 px-4 py-3.5">
<div className="mb-1 flex items-center gap-2 font-mono text-[10px] uppercase tracking-[0.14em] text-stone-400">
<GitBranch size={12} />
Destination
</div>
<p className="break-all font-mono text-xs leading-5 text-stone-600">
{projectsRoot}/<span className="text-orange-700">{name.trim() || "project-name"}</span>
</p>
</div>
{error ? (
<p role="alert" className="rounded-md border border-red-200 bg-red-50 px-3.5 py-3 text-sm text-red-700">
{error}
</p>
) : null}
{createdProject ? (
<div role="status" className="rounded-md border border-emerald-200 bg-emerald-50 px-3.5 py-3 text-sm text-emerald-800">
<div className="flex items-center gap-2 font-medium">
<Check size={16} />
Project created and Git initialized.
</div>
<p className="mt-1 break-all font-mono text-xs text-emerald-700">
{createdProject.path}
</p>
</div>
) : null}
<Button type="submit" size="lg" className="w-full" disabled={isCreating || !name.trim()}>
{isCreating ? (
<>
<Loader2 className="mr-2 animate-spin" size={17} />
Creating workspace
</>
) : (
<>
Create project
<ChevronRight className="ml-2" size={17} />
</>
)}
</Button>
</form>
</section>
);
}