"use client"; import { useEffect, useMemo, useState } from "react"; import { useRouter } from "next/navigation"; import { Loader2, RefreshCw, Search } from "lucide-react"; import { Modal } from "@/components/ui/Modal"; import type { ConnectionSafe, RepoListEntry, RepositoryWithSnapshot } from "@/types"; /** * Ein weiteres Repository mit einem Projekt verknuepfen – oder ein * vorhandenes umbenennen und umhaengen. * * Beim Anlegen des Projekts genuegt der Projektdialog: dort setzen die * Git-Felder das erste Repository. Alles darueber hinaus laeuft hier. */ interface FormState { label: string; connectionId: string; repoUrl: string; repoOwner: string; repoName: string; autoSync: boolean; } const EMPTY: FormState = { label: "", connectionId: "", repoUrl: "", repoOwner: "", repoName: "", autoSync: true, }; export function RepositoryDialog({ open, onClose, projectId, connections, repository, }: { open: boolean; onClose: () => void; projectId: string; connections: ConnectionSafe[]; /** Vorhandenes = Bearbeiten, sonst Verknuepfen. */ repository?: RepositoryWithSnapshot | null; }) { const router = useRouter(); const [form, setForm] = useState(EMPTY); const [saving, setSaving] = useState(false); const [error, setError] = useState(null); const [repos, setRepos] = useState([]); const [pickerOpen, setPickerOpen] = useState(false); const [loading, setLoading] = useState(false); const [filter, setFilter] = useState(""); const isEdit = Boolean(repository); useEffect(() => { if (!open) return; setError(null); setPickerOpen(false); setRepos([]); setFilter(""); setForm( repository ? { label: repository.label ?? "", connectionId: repository.connectionId ?? "", repoUrl: repository.repoUrl ?? "", repoOwner: repository.repoOwner ?? "", repoName: repository.repoName ?? "", autoSync: repository.autoSync, } : EMPTY, ); }, [open, repository]); const gefiltert = useMemo(() => { const needle = filter.toLowerCase().trim(); if (!needle) return repos.slice(0, 60); return repos.filter((repo) => repo.fullPath.toLowerCase().includes(needle)).slice(0, 60); }, [repos, filter]); function update(key: K, value: FormState[K]) { setForm((previous) => ({ ...previous, [key]: value })); } async function loadRepos() { if (!form.connectionId) { setError("Bitte zuerst eine Git-Verbindung auswaehlen."); return; } setLoading(true); setError(null); try { const response = await fetch(`/api/connections/${form.connectionId}/repos`); const payload = await response.json(); if (!response.ok) throw new Error(payload.error ?? "Repos konnten nicht geladen werden."); setRepos(payload.repos ?? []); setPickerOpen(true); } catch (caught) { setError(caught instanceof Error ? caught.message : String(caught)); } finally { setLoading(false); } } function pick(repo: RepoListEntry) { setForm((previous) => ({ ...previous, repoUrl: repo.htmlUrl, repoOwner: repo.owner, repoName: repo.name, })); setPickerOpen(false); } async function submit(event: React.FormEvent) { event.preventDefault(); setSaving(true); setError(null); try { const response = await fetch( isEdit ? `/api/repositories/${repository?.id}` : `/api/projects/${projectId}/repositories`, { method: isEdit ? "PATCH" : "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ label: form.label, connectionId: form.connectionId || null, repoUrl: form.repoUrl, repoOwner: form.repoOwner, repoName: form.repoName, autoSync: form.autoSync, }), }, ); const payload = await response.json().catch(() => ({})); if (!response.ok) throw new Error(payload.error ?? "Speichern fehlgeschlagen."); onClose(); router.refresh(); } catch (caught) { setError(caught instanceof Error ? caught.message : String(caught)); } finally { setSaving(false); } } return (
update("label", event.target.value)} placeholder="Frontend" maxLength={80} />

Ohne Angabe steht schlicht owner/name da.

update("repoUrl", event.target.value)} placeholder="https://github.com/user/repo" />
update("repoOwner", event.target.value)} placeholder="wird aus der URL abgeleitet" />
update("repoName", event.target.value)} placeholder="wird aus der URL abgeleitet" />
{pickerOpen ? (
setFilter(event.target.value)} />
    {gefiltert.map((repo) => (
  • ))} {gefiltert.length === 0 ? (
  • Keine Treffer.
  • ) : null}
) : null} {error ? (

{error}

) : null}
); }