Files
Codewalkers/apps/web/src/components/RegisterProjectDialog.tsx
Lukas May 28521e1c20 chore: merge main into cw/small-change-flow
Integrates main branch changes (headquarters dashboard, task retry count,
agent prompt persistence, remote sync improvements) with the initiative's
errand agent feature. Both features coexist in the merged result.

Key resolutions:
- Schema: take main's errands table (nullable projectId, no conflictFiles,
  with errandsRelations); migrate to 0035_faulty_human_fly
- Router: keep both errandProcedures and headquartersProcedures
- Errand prompt: take main's simpler version (no question-asking flow)
- Manager: take main's status check (running|idle only, no waiting_for_input)
- Tests: update to match removed conflictFiles field and undefined vs null
2026-03-06 16:48:12 +01:00

135 lines
3.7 KiB
TypeScript

import { useEffect, useState } from "react";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Loader2 } from "lucide-react";
import { toast } from "sonner";
import { trpc } from "@/lib/trpc";
interface RegisterProjectDialogProps {
open: boolean;
onOpenChange: (open: boolean) => void;
}
export function RegisterProjectDialog({
open,
onOpenChange,
}: RegisterProjectDialogProps) {
const [name, setName] = useState("");
const [url, setUrl] = useState("");
const [defaultBranch, setDefaultBranch] = useState("main");
const [error, setError] = useState<string | null>(null);
const utils = trpc.useUtils();
const registerMutation = trpc.registerProject.useMutation({
onSuccess: () => {
onOpenChange(false);
toast.success("Project registered");
void utils.listProjects.invalidate();
},
onError: (err) => {
if (err.data?.code === "INTERNAL_SERVER_ERROR") {
setError("Failed to clone repository. Check the URL and try again.");
} else {
setError(err.message);
}
},
});
useEffect(() => {
if (open) {
setName("");
setUrl("");
setDefaultBranch("main");
setError(null);
}
}, [open]);
function handleSubmit(e: React.FormEvent) {
e.preventDefault();
setError(null);
registerMutation.mutate({
name: name.trim(),
url: url.trim(),
defaultBranch: defaultBranch.trim() || undefined,
});
}
const canSubmit =
name.trim().length > 0 &&
url.trim().length > 0 &&
!registerMutation.isPending;
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent>
<DialogHeader>
<DialogTitle>Register Project</DialogTitle>
<DialogDescription>
Register a git repository as a project.
</DialogDescription>
</DialogHeader>
<form onSubmit={handleSubmit} className="space-y-4">
<div className="space-y-2">
<Label htmlFor="project-name">Name</Label>
<Input
id="project-name"
placeholder="e.g. my-app"
value={name}
onChange={(e) => setName(e.target.value)}
autoFocus
/>
</div>
<div className="space-y-2">
<Label htmlFor="project-url">Repository URL</Label>
<Input
id="project-url"
placeholder="e.g. https://github.com/org/repo.git"
value={url}
onChange={(e) => setUrl(e.target.value)}
/>
</div>
<div className="space-y-2">
<Label htmlFor="default-branch">Default Branch</Label>
<Input
id="default-branch"
placeholder="main"
value={defaultBranch}
onChange={(e) => setDefaultBranch(e.target.value)}
/>
</div>
{error && <p className="text-sm text-destructive">{error}</p>}
<DialogFooter>
<Button
type="button"
variant="outline"
onClick={() => onOpenChange(false)}
>
Cancel
</Button>
<Button type="submit" disabled={!canSubmit}>
{registerMutation.isPending ? (
<>
<Loader2 className="animate-spin mr-2 h-4 w-4" />
Cloning repository
</>
) : (
"Register"
)}
</Button>
</DialogFooter>
</form>
</DialogContent>
</Dialog>
);
}